diff options
329 files changed, 6713 insertions, 4447 deletions
diff --git a/api/current.xml b/api/current.xml index ba168be..c9c6f6f 100644 --- a/api/current.xml +++ b/api/current.xml @@ -2297,6 +2297,17 @@ visibility="public" > </field> +<field name="animationResolution" + type="int" + transient="false" + volatile="false" + value="16843563" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="antialias" type="int" transient="false" @@ -2473,6 +2484,17 @@ visibility="public" > </field> +<field name="baseline" + type="int" + transient="false" + volatile="false" + value="16843565" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="baselineAlignBottom" type="int" transient="false" @@ -5135,6 +5157,17 @@ visibility="public" > </field> +<field name="indeterminateProgressStyle" + type="int" + transient="false" + volatile="false" + value="16843561" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="indicatorLeft" type="int" transient="false" @@ -7159,6 +7192,17 @@ visibility="public" > </field> +<field name="progressBarPadding" + type="int" + transient="false" + volatile="false" + value="16843562" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="progressBarStyle" type="int" transient="false" @@ -8468,6 +8512,17 @@ visibility="public" > </field> +<field name="state_accelerated" + type="int" + transient="false" + volatile="false" + value="16843564" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="state_activated" type="int" transient="false" @@ -14612,6 +14667,17 @@ visibility="public" > </field> +<field name="Theme_Holo_Light_DialogWhenLarge" + type="int" + transient="false" + volatile="false" + value="16973955" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="Theme_Holo_Light_Dialog_Alert" type="int" transient="false" @@ -19530,6 +19596,17 @@ synchronized="false" static="false" final="false" + deprecated="deprecated" + visibility="public" +> +</method> +<method name="getCustomView" + return="android.view.View" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" deprecated="not deprecated" visibility="public" > @@ -21021,6 +21098,32 @@ <parameter name="nonRoot" type="boolean"> </parameter> </method> +<method name="onActionModeFinished" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="mode" type="android.view.ActionMode"> +</parameter> +</method> +<method name="onActionModeStarted" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="mode" type="android.view.ActionMode"> +</parameter> +</method> <method name="onActivityResult" return="void" abstract="false" @@ -21656,19 +21759,6 @@ visibility="protected" > </method> -<method name="onStartActionMode" - return="android.view.ActionMode" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="callback" type="android.view.ActionMode.Callback"> -</parameter> -</method> <method name="onStop" return="void" abstract="false" @@ -21769,6 +21859,19 @@ <parameter name="hasFocus" type="boolean"> </parameter> </method> +<method name="onWindowStartingActionMode" + return="android.view.ActionMode" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="callback" type="android.view.ActionMode.Callback"> +</parameter> +</method> <method name="openContextMenu" return="void" abstract="false" @@ -21819,47 +21922,6 @@ <parameter name="exitAnim" type="int"> </parameter> </method> -<method name="popBackStack" - return="boolean" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="deprecated" - visibility="public" -> -</method> -<method name="popBackStack" - return="boolean" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="deprecated" - visibility="public" -> -<parameter name="name" type="java.lang.String"> -</parameter> -<parameter name="flags" type="int"> -</parameter> -</method> -<method name="popBackStack" - return="boolean" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="deprecated" - visibility="public" -> -<parameter name="id" type="int"> -</parameter> -<parameter name="flags" type="int"> -</parameter> -</method> <method name="registerForContextMenu" return="void" abstract="false" @@ -22545,17 +22607,6 @@ visibility="protected" > </field> -<field name="POP_BACK_STACK_INCLUSIVE" - type="int" - transient="false" - volatile="false" - value="1" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> <field name="RESULT_CANCELED" type="int" transient="false" @@ -25368,6 +25419,32 @@ visibility="public" > </method> +<method name="onActionModeFinished" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="mode" type="android.view.ActionMode"> +</parameter> +</method> +<method name="onActionModeStarted" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="mode" type="android.view.ActionMode"> +</parameter> +</method> <method name="onAttachedToWindow" return="void" abstract="false" @@ -25718,19 +25795,6 @@ visibility="protected" > </method> -<method name="onStartActionMode" - return="android.view.ActionMode" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="callback" type="android.view.ActionMode.Callback"> -</parameter> -</method> <method name="onStop" return="void" abstract="false" @@ -25794,6 +25858,19 @@ <parameter name="hasFocus" type="boolean"> </parameter> </method> +<method name="onWindowStartingActionMode" + return="android.view.ActionMode" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="callback" type="android.view.ActionMode.Callback"> +</parameter> +</method> <method name="openContextMenu" return="void" abstract="false" @@ -28242,6 +28319,17 @@ <parameter name="args" type="java.lang.String[]"> </parameter> </method> +<method name="executePendingTransactions" + return="boolean" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="findFragmentById" return="android.app.Fragment" abstract="true" @@ -28308,7 +28396,7 @@ > </method> <method name="popBackStack" - return="boolean" + return="void" abstract="true" native="false" synchronized="false" @@ -28319,7 +28407,7 @@ > </method> <method name="popBackStack" - return="boolean" + return="void" abstract="true" native="false" synchronized="false" @@ -28334,6 +28422,47 @@ </parameter> </method> <method name="popBackStack" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="id" type="int"> +</parameter> +<parameter name="flags" type="int"> +</parameter> +</method> +<method name="popBackStackImmediate" + return="boolean" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="popBackStackImmediate" + return="boolean" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="name" type="java.lang.String"> +</parameter> +<parameter name="flags" type="int"> +</parameter> +</method> +<method name="popBackStackImmediate" return="boolean" abstract="true" native="false" @@ -212017,6 +212146,32 @@ <parameter name="event" type="android.view.MotionEvent"> </parameter> </method> +<method name="onActionModeFinished" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="mode" type="android.view.ActionMode"> +</parameter> +</method> +<method name="onActionModeStarted" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="mode" type="android.view.ActionMode"> +</parameter> +</method> <method name="onAttachedToWindow" return="void" abstract="true" @@ -212151,8 +212306,8 @@ visibility="public" > </method> -<method name="onStartActionMode" - return="android.view.ActionMode" +<method name="onWindowAttributesChanged" + return="void" abstract="true" native="false" synchronized="false" @@ -212161,10 +212316,10 @@ deprecated="not deprecated" visibility="public" > -<parameter name="callback" type="android.view.ActionMode.Callback"> +<parameter name="attrs" type="android.view.WindowManager.LayoutParams"> </parameter> </method> -<method name="onWindowAttributesChanged" +<method name="onWindowFocusChanged" return="void" abstract="true" native="false" @@ -212174,11 +212329,11 @@ deprecated="not deprecated" visibility="public" > -<parameter name="attrs" type="android.view.WindowManager.LayoutParams"> +<parameter name="hasFocus" type="boolean"> </parameter> </method> -<method name="onWindowFocusChanged" - return="void" +<method name="onWindowStartingActionMode" + return="android.view.ActionMode" abstract="true" native="false" synchronized="false" @@ -212187,7 +212342,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="hasFocus" type="boolean"> +<parameter name="callback" type="android.view.ActionMode.Callback"> </parameter> </method> </interface> @@ -219249,6 +219404,19 @@ <parameter name="data" type="android.os.Bundle"> </parameter> </method> +<method name="setCurrentInputMethodSubtype" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="subtype" type="android.view.inputmethod.InputMethodSubtype"> +</parameter> +</method> <method name="setInputMethod" return="void" abstract="false" @@ -222201,6 +222369,101 @@ </parameter> </method> </interface> +<class name="WebResourceResponse" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="WebResourceResponse" + type="android.webkit.WebResourceResponse" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="mimeType" type="java.lang.String"> +</parameter> +<parameter name="encoding" type="java.lang.String"> +</parameter> +<parameter name="data" type="java.io.InputStream"> +</parameter> +</constructor> +<method name="getData" + return="java.io.InputStream" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getEncoding" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getMimeType" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="setData" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="data" type="java.io.InputStream"> +</parameter> +</method> +<method name="setEncoding" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="encoding" type="java.lang.String"> +</parameter> +</method> +<method name="setMimeType" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="mimeType" type="java.lang.String"> +</parameter> +</method> +</class> <class name="WebSettings" extends="java.lang.Object" abstract="false" @@ -225290,6 +225553,21 @@ <parameter name="event" type="android.view.KeyEvent"> </parameter> </method> +<method name="shouldInterceptRequest" + return="android.webkit.WebResourceResponse" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="view" type="android.webkit.WebView"> +</parameter> +<parameter name="url" type="java.lang.String"> +</parameter> +</method> <method name="shouldOverrideKeyEvent" return="boolean" abstract="false" @@ -234006,6 +234284,17 @@ visibility="public" > </method> +<method name="getBaselineAlignBottom" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getDrawable" return="android.graphics.drawable.Drawable" abstract="false" @@ -234078,6 +234367,32 @@ <parameter name="alpha" type="int"> </parameter> </method> +<method name="setBaseline" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="baseline" type="int"> +</parameter> +</method> +<method name="setBaselineAlignBottom" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="aligned" type="boolean"> +</parameter> +</method> <method name="setColorFilter" return="void" abstract="false" diff --git a/cmds/stagefright/recordvideo.cpp b/cmds/stagefright/recordvideo.cpp index f8eb514..1264215 100644 --- a/cmds/stagefright/recordvideo.cpp +++ b/cmds/stagefright/recordvideo.cpp @@ -264,7 +264,7 @@ int main(int argc, char **argv) { } enc_meta->setInt32(kKeyWidth, width); enc_meta->setInt32(kKeyHeight, height); - enc_meta->setInt32(kKeySampleRate, frameRateFps); + enc_meta->setInt32(kKeyFrameRate, frameRateFps); enc_meta->setInt32(kKeyBitRate, bitRateBps); enc_meta->setInt32(kKeyStride, width); enc_meta->setInt32(kKeySliceHeight, height); diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index a57b54a..246d661 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -138,14 +138,19 @@ public abstract class ActionBar { * Set the action bar into custom navigation mode, supplying a view * for custom navigation. * - * Custom navigation views appear between the application icon and + * <p>Custom navigation views appear between the application icon and * any action buttons and may use any space available there. Common * use cases for custom navigation views might include an auto-suggesting * address bar for a browser or other navigation mechanisms that do not - * translate well to provided navigation modes. + * translate well to provided navigation modes.</p> + * + * <p>The display option {@link #DISPLAY_SHOW_CUSTOM} must be set for + * the custom view to be displayed.</p> * * @param view Custom navigation view to place in the ActionBar. * @param layoutParams How this custom view should layout in the bar. + * + * @see #setDisplayOptions(int, int) */ public abstract void setCustomView(View view, LayoutParams layoutParams); @@ -248,39 +253,47 @@ public abstract class ActionBar { public abstract void setStandardNavigationMode(); /** - * Set the action bar's title. This will only be displayed in standard navigation mode. + * Set the action bar's title. This will only be displayed if + * {@link #DISPLAY_SHOW_TITLE} is set. * * @param title Title to set * * @see #setTitle(int) + * @see #setDisplayOptions(int, int) */ public abstract void setTitle(CharSequence title); /** - * Set the action bar's title. This will only be displayed in standard navigation mode. + * Set the action bar's title. This will only be displayed if + * {@link #DISPLAY_SHOW_TITLE} is set. * * @param resId Resource ID of title string to set * * @see #setTitle(CharSequence) + * @see #setDisplayOptions(int, int) */ public abstract void setTitle(int resId); /** - * Set the action bar's subtitle. This will only be displayed in standard navigation mode. - * Set to null to disable the subtitle entirely. + * Set the action bar's subtitle. This will only be displayed if + * {@link #DISPLAY_SHOW_TITLE} is set. Set to null to disable the + * subtitle entirely. * * @param subtitle Subtitle to set * * @see #setSubtitle(int) + * @see #setDisplayOptions(int, int) */ public abstract void setSubtitle(CharSequence subtitle); /** - * Set the action bar's subtitle. This will only be displayed in standard navigation mode. + * Set the action bar's subtitle. This will only be displayed if + * {@link #DISPLAY_SHOW_TITLE} is set. * * @param resId Resource ID of subtitle string to set * * @see #setSubtitle(CharSequence) + * @see #setDisplayOptions(int, int) */ public abstract void setSubtitle(int resId); @@ -317,9 +330,16 @@ public abstract class ActionBar { /** * @return The current custom navigation view. + * @deprecated Method has been renamed. Use {@link #getCustomView()}. */ + @Deprecated public abstract View getCustomNavigationView(); - + + /** + * @return The current custom view. + */ + public abstract View getCustomView(); + /** * Returns the current ActionBar title in standard mode. * Returns null if {@link #getNavigationMode()} would not return diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 33f88d8..5174f19 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2086,63 +2086,12 @@ public class Activity extends ContextThemeWrapper } /** - * Flag for {@link #popBackStack(String, int)} - * and {@link #popBackStack(int, int)}: If set, and the name or ID of - * a back stack entry has been supplied, then all matching entries will - * be consumed until one that doesn't match is found or the bottom of - * the stack is reached. Otherwise, all entries up to but not including that entry - * will be removed. - */ - public static final int POP_BACK_STACK_INCLUSIVE = 1<<0; - - /** - * Pop the top state off the back stack. Returns true if there was one - * to pop, else false. - * @deprecated use {@link #getFragmentManager}. - */ - @Deprecated - public boolean popBackStack() { - return mFragments.popBackStack(); - } - - /** - * Pop the last fragment transition from the local activity's fragment - * back stack. If there is nothing to pop, false is returned. - * @param name If non-null, this is the name of a previous back state - * to look for; if found, all states up to that state will be popped. The - * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether - * the named state itself is popped. If null, only the top state is popped. - * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. - * @deprecated use {@link #getFragmentManager}. - */ - @Deprecated - public boolean popBackStack(String name, int flags) { - return mFragments.popBackStack(name, flags); - } - - /** - * Pop all back stack states up to the one with the given identifier. - * @param id Identifier of the stated to be popped. If no identifier exists, - * false is returned. - * The identifier is the number returned by - * {@link FragmentTransaction#commit() FragmentTransaction.commit()}. The - * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether - * the named state itself is popped. - * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. - * @deprecated use {@link #getFragmentManager}. - */ - @Deprecated - public boolean popBackStack(int id, int flags) { - return mFragments.popBackStack(id, flags); - } - - /** * Called when the activity has detected the user's press of the back * key. The default implementation simply finishes the current activity, * but you can override this to do whatever you want. */ public void onBackPressed() { - if (!mFragments.popBackStack()) { + if (!mFragments.popBackStackImmediate()) { finish(); } } @@ -4174,7 +4123,7 @@ public class Activity extends ContextThemeWrapper } /** - * Start a context mode. + * Start an action mode. * * @param callback Callback that will manage lifecycle events for this context mode * @return The ContextMode that was started, or null if it was canceled @@ -4185,7 +4134,18 @@ public class Activity extends ContextThemeWrapper return mWindow.getDecorView().startActionMode(callback); } - public ActionMode onStartActionMode(ActionMode.Callback callback) { + /** + * Give the Activity a chance to control the UI for an action mode requested + * by the system. + * + * <p>Note: If you are looking for a notification callback that an action mode + * has been started for this activity, see {@link #onActionModeStarted(ActionMode)}.</p> + * + * @param callback The callback that should control the new action mode + * @return The new action mode, or <code>null</code> if the activity does not want to + * provide special handling for this action mode. (It will be handled by the system.) + */ + public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) { initActionBar(); if (mActionBar != null) { return mActionBar.startActionMode(callback); @@ -4193,6 +4153,24 @@ public class Activity extends ContextThemeWrapper return null; } + /** + * Notifies the Activity that an action mode has been started. + * Activity subclasses overriding this method should call the superclass implementation. + * + * @param mode The new action mode. + */ + public void onActionModeStarted(ActionMode mode) { + } + + /** + * Notifies the activity that an action mode has finished. + * Activity subclasses overriding this method should call the superclass implementation. + * + * @param mode The action mode that just finished. + */ + public void onActionModeFinished(ActionMode mode) { + } + // ------------------ Internal API ------------------ final void setParent(Activity parent) { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 70e11df..3cead11 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -95,6 +95,7 @@ import java.util.Map; import java.util.TimeZone; import java.util.regex.Pattern; +import dalvik.system.CloseGuard; import dalvik.system.SamplingProfiler; final class SuperNotCalledException extends AndroidRuntimeException { @@ -3726,6 +3727,11 @@ public final class ActivityThread { public static final void main(String[] args) { SamplingProfilerIntegration.start(); + // CloseGuard defaults to true and can be quite spammy. We + // disable it here, but selectively enable it later (via + // StrictMode) on debug builds, but using DropBox, not logs. + CloseGuard.setEnabled(false); + Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 129c29d..dda3107 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -106,9 +106,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.WeakHashMap; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; class ReceiverRestrictedContext extends ContextWrapper { ReceiverRestrictedContext(Context base) { @@ -149,18 +146,9 @@ class ContextImpl extends Context { private final static String TAG = "ApplicationContext"; private final static boolean DEBUG = false; - private static final Object sSync = new Object(); - private static AlarmManager sAlarmManager; - private static PowerManager sPowerManager; - private static ConnectivityManager sConnectivityManager; - private static ThrottleManager sThrottleManager; - private static WifiManager sWifiManager; - private static LocationManager sLocationManager; - private static CountryDetector sCountryDetector; private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs = new HashMap<String, SharedPreferencesImpl>(); - private AudioManager mAudioManager; /*package*/ LoadedApk mPackageInfo; private Resources mResources; /*package*/ ActivityThread mMainThread; @@ -170,24 +158,8 @@ class ContextImpl extends Context { private int mThemeResource = 0; private Resources.Theme mTheme = null; private PackageManager mPackageManager; - private NotificationManager mNotificationManager = null; - private ActivityManager mActivityManager = null; - private WallpaperManager mWallpaperManager = null; private Context mReceiverRestrictedContext = null; - private SearchManager mSearchManager = null; - private SensorManager mSensorManager = null; - private StorageManager mStorageManager = null; - private Vibrator mVibrator = null; - private LayoutInflater mLayoutInflater = null; - private StatusBarManager mStatusBarManager = null; - private TelephonyManager mTelephonyManager = null; - private ClipboardManager mClipboardManager = null; private boolean mRestricted; - private AccountManager mAccountManager; // protected by mSync - private DropBoxManager mDropBoxManager = null; - private DevicePolicyManager mDevicePolicyManager = null; - private UiModeManager mUiModeManager = null; - private DownloadManager mDownloadManager = null; private final Object mSync = new Object(); @@ -200,6 +172,266 @@ class ContextImpl extends Context { private static final String[] EMPTY_FILE_LIST = {}; + /** + * Override this class when the system service constructor needs a + * ContextImpl. Else, use StaticServiceFetcher below. + */ + /*package*/ static class ServiceFetcher { + int mContextCacheIndex = -1; + + /** + * Main entrypoint; only override if you don't need caching. + */ + public Object getService(ContextImpl ctx) { + ArrayList<Object> cache = ctx.mServiceCache; + Object service; + synchronized (cache) { + if (cache.size() == 0) { + // Initialize the cache vector on first access. + // At this point sNextPerContextServiceCacheIndex + // is the number of potential services that are + // cached per-Context. + for (int i = 0; i < sNextPerContextServiceCacheIndex; i++) { + cache.add(null); + } + } else { + service = cache.get(mContextCacheIndex); + if (service != null) { + return service; + } + } + service = createService(ctx); + cache.set(mContextCacheIndex, service); + return service; + } + } + + /** + * Override this to create a new per-Context instance of the + * service. getService() will handle locking and caching. + */ + public Object createService(ContextImpl ctx) { + throw new RuntimeException("Not implemented"); + } + } + + /** + * Override this class for services to be cached process-wide. + */ + abstract static class StaticServiceFetcher extends ServiceFetcher { + private Object mCachedInstance; + + @Override + public final Object getService(ContextImpl unused) { + synchronized (StaticServiceFetcher.this) { + Object service = mCachedInstance; + if (service != null) { + return service; + } + return mCachedInstance = createStaticService(); + } + } + + public abstract Object createStaticService(); + } + + private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP = + new HashMap<String, ServiceFetcher>(); + + private static int sNextPerContextServiceCacheIndex = 0; + private static void registerService(String serviceName, ServiceFetcher fetcher) { + if (!(fetcher instanceof StaticServiceFetcher)) { + fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++; + } + SYSTEM_SERVICE_MAP.put(serviceName, fetcher); + } + + // This one's defined separately and given a variable name so it + // can be re-used by getWallpaperManager(), avoiding a HashMap + // lookup. + private static ServiceFetcher WALLPAPER_FETCHER = new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return new WallpaperManager(ctx.getOuterContext(), + ctx.mMainThread.getHandler()); + }}; + + static { + registerService(ACCESSIBILITY_SERVICE, new ServiceFetcher() { + public Object getService(ContextImpl ctx) { + return AccessibilityManager.getInstance(ctx); + }}); + + registerService(ACCOUNT_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(ACCOUNT_SERVICE); + IAccountManager service = IAccountManager.Stub.asInterface(b); + return new AccountManager(ctx, service); + }}); + + registerService(ACTIVITY_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler()); + }}); + + registerService(ALARM_SERVICE, new StaticServiceFetcher() { + public Object createStaticService() { + IBinder b = ServiceManager.getService(ALARM_SERVICE); + IAlarmManager service = IAlarmManager.Stub.asInterface(b); + return new AlarmManager(service); + }}); + + registerService(AUDIO_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return new AudioManager(ctx); + }}); + + registerService(CLIPBOARD_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return new ClipboardManager(ctx.getOuterContext(), + ctx.mMainThread.getHandler()); + }}); + + registerService(CONNECTIVITY_SERVICE, new StaticServiceFetcher() { + public Object createStaticService() { + IBinder b = ServiceManager.getService(CONNECTIVITY_SERVICE); + return new ConnectivityManager(IConnectivityManager.Stub.asInterface(b)); + }}); + + registerService(COUNTRY_DETECTOR, new StaticServiceFetcher() { + public Object createStaticService() { + IBinder b = ServiceManager.getService(COUNTRY_DETECTOR); + return new CountryDetector(ICountryDetector.Stub.asInterface(b)); + }}); + + registerService(DEVICE_POLICY_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return DevicePolicyManager.create(ctx, ctx.mMainThread.getHandler()); + }}); + + registerService(DOWNLOAD_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return new DownloadManager(ctx.getContentResolver(), ctx.getPackageName()); + }}); + + registerService(DROPBOX_SERVICE, new StaticServiceFetcher() { + public Object createStaticService() { + return createDropBoxManager(); + }}); + + registerService(INPUT_METHOD_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return InputMethodManager.getInstance(ctx); + }}); + + registerService(KEYGUARD_SERVICE, new ServiceFetcher() { + public Object getService(ContextImpl ctx) { + // TODO: why isn't this caching it? It wasn't + // before, so I'm preserving the old behavior and + // using getService(), instead of createService() + // which would do the caching. + return new KeyguardManager(); + }}); + + registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext()); + }}); + + registerService(LOCATION_SERVICE, new StaticServiceFetcher() { + public Object createStaticService() { + IBinder b = ServiceManager.getService(LOCATION_SERVICE); + return new LocationManager(ILocationManager.Stub.asInterface(b)); + }}); + + registerService(NOTIFICATION_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + final Context outerContext = ctx.getOuterContext(); + return new NotificationManager( + new ContextThemeWrapper(outerContext, + outerContext.getApplicationInfo().targetSdkVersion >= + Build.VERSION_CODES.HONEYCOMB + ? com.android.internal.R.style.Theme_Holo_Dialog + : com.android.internal.R.style.Theme_Dialog), + ctx.mMainThread.getHandler()); + }}); + + // Note: this was previously cached in a static variable, but + // constructed using mMainThread.getHandler(), so converting + // it to be a regular Context-cached service... + registerService(POWER_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(POWER_SERVICE); + IPowerManager service = IPowerManager.Stub.asInterface(b); + return new PowerManager(service, ctx.mMainThread.getHandler()); + }}); + + registerService(SEARCH_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return new SearchManager(ctx.getOuterContext(), + ctx.mMainThread.getHandler()); + }}); + + registerService(SENSOR_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return new SensorManager(ctx.mMainThread.getHandler().getLooper()); + }}); + + registerService(STATUS_BAR_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return new StatusBarManager(ctx.getOuterContext()); + }}); + + registerService(STORAGE_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + try { + return new StorageManager(ctx.mMainThread.getHandler().getLooper()); + } catch (RemoteException rex) { + Log.e(TAG, "Failed to create StorageManager", rex); + return null; + } + }}); + + registerService(TELEPHONY_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return new TelephonyManager(ctx.getOuterContext()); + }}); + + registerService(THROTTLE_SERVICE, new StaticServiceFetcher() { + public Object createStaticService() { + IBinder b = ServiceManager.getService(THROTTLE_SERVICE); + return new ThrottleManager(IThrottleManager.Stub.asInterface(b)); + }}); + + registerService(UI_MODE_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return new UiModeManager(); + }}); + + registerService(VIBRATOR_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return new Vibrator(); + }}); + + registerService(WALLPAPER_SERVICE, WALLPAPER_FETCHER); + + registerService(WIFI_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(WIFI_SERVICE); + IWifiManager service = IWifiManager.Stub.asInterface(b); + return new WifiManager(service, ctx.mMainThread.getHandler()); + }}); + + registerService(WINDOW_SERVICE, new ServiceFetcher() { + public Object getService(ContextImpl ctx) { + return WindowManagerImpl.getDefault(); + }}); + } + + // The system service cache for the system services that are + // cached per-ContextImpl. Package-scoped to avoid accessor + // methods. + final ArrayList<Object> mServiceCache = new ArrayList<Object>(); + @Override public AssetManager getAssets() { return mResources.getAssets(); @@ -895,273 +1127,12 @@ class ContextImpl extends Context { @Override public Object getSystemService(String name) { - if (WINDOW_SERVICE.equals(name)) { - return WindowManagerImpl.getDefault(); - } else if (LAYOUT_INFLATER_SERVICE.equals(name)) { - synchronized (mSync) { - LayoutInflater inflater = mLayoutInflater; - if (inflater != null) { - return inflater; - } - mLayoutInflater = inflater = - PolicyManager.makeNewLayoutInflater(getOuterContext()); - return inflater; - } - } else if (ACTIVITY_SERVICE.equals(name)) { - return getActivityManager(); - } else if (INPUT_METHOD_SERVICE.equals(name)) { - return InputMethodManager.getInstance(this); - } else if (ALARM_SERVICE.equals(name)) { - return getAlarmManager(); - } else if (ACCOUNT_SERVICE.equals(name)) { - return getAccountManager(); - } else if (POWER_SERVICE.equals(name)) { - return getPowerManager(); - } else if (CONNECTIVITY_SERVICE.equals(name)) { - return getConnectivityManager(); - } else if (THROTTLE_SERVICE.equals(name)) { - return getThrottleManager(); - } else if (WIFI_SERVICE.equals(name)) { - return getWifiManager(); - } else if (NOTIFICATION_SERVICE.equals(name)) { - return getNotificationManager(); - } else if (KEYGUARD_SERVICE.equals(name)) { - return new KeyguardManager(); - } else if (ACCESSIBILITY_SERVICE.equals(name)) { - return AccessibilityManager.getInstance(this); - } else if (LOCATION_SERVICE.equals(name)) { - return getLocationManager(); - } else if (COUNTRY_DETECTOR.equals(name)) { - return getCountryDetector(); - } else if (SEARCH_SERVICE.equals(name)) { - return getSearchManager(); - } else if (SENSOR_SERVICE.equals(name)) { - return getSensorManager(); - } else if (STORAGE_SERVICE.equals(name)) { - return getStorageManager(); - } else if (VIBRATOR_SERVICE.equals(name)) { - return getVibrator(); - } else if (STATUS_BAR_SERVICE.equals(name)) { - synchronized (mSync) { - if (mStatusBarManager == null) { - mStatusBarManager = new StatusBarManager(getOuterContext()); - } - return mStatusBarManager; - } - } else if (AUDIO_SERVICE.equals(name)) { - return getAudioManager(); - } else if (TELEPHONY_SERVICE.equals(name)) { - return getTelephonyManager(); - } else if (CLIPBOARD_SERVICE.equals(name)) { - return getClipboardManager(); - } else if (WALLPAPER_SERVICE.equals(name)) { - return getWallpaperManager(); - } else if (DROPBOX_SERVICE.equals(name)) { - return getDropBoxManager(); - } else if (DEVICE_POLICY_SERVICE.equals(name)) { - return getDevicePolicyManager(); - } else if (UI_MODE_SERVICE.equals(name)) { - return getUiModeManager(); - } else if (DOWNLOAD_SERVICE.equals(name)) { - return getDownloadManager(); - } - - return null; - } - - private AccountManager getAccountManager() { - synchronized (mSync) { - if (mAccountManager == null) { - IBinder b = ServiceManager.getService(ACCOUNT_SERVICE); - IAccountManager service = IAccountManager.Stub.asInterface(b); - mAccountManager = new AccountManager(this, service); - } - return mAccountManager; - } - } - - private ActivityManager getActivityManager() { - synchronized (mSync) { - if (mActivityManager == null) { - mActivityManager = new ActivityManager(getOuterContext(), - mMainThread.getHandler()); - } - } - return mActivityManager; - } - - private AlarmManager getAlarmManager() { - synchronized (sSync) { - if (sAlarmManager == null) { - IBinder b = ServiceManager.getService(ALARM_SERVICE); - IAlarmManager service = IAlarmManager.Stub.asInterface(b); - sAlarmManager = new AlarmManager(service); - } - } - return sAlarmManager; - } - - private PowerManager getPowerManager() { - synchronized (sSync) { - if (sPowerManager == null) { - IBinder b = ServiceManager.getService(POWER_SERVICE); - IPowerManager service = IPowerManager.Stub.asInterface(b); - sPowerManager = new PowerManager(service, mMainThread.getHandler()); - } - } - return sPowerManager; - } - - private ConnectivityManager getConnectivityManager() - { - synchronized (sSync) { - if (sConnectivityManager == null) { - IBinder b = ServiceManager.getService(CONNECTIVITY_SERVICE); - IConnectivityManager service = IConnectivityManager.Stub.asInterface(b); - sConnectivityManager = new ConnectivityManager(service); - } - } - return sConnectivityManager; - } - - private ThrottleManager getThrottleManager() - { - synchronized (sSync) { - if (sThrottleManager == null) { - IBinder b = ServiceManager.getService(THROTTLE_SERVICE); - IThrottleManager service = IThrottleManager.Stub.asInterface(b); - sThrottleManager = new ThrottleManager(service); - } - } - return sThrottleManager; - } - - private WifiManager getWifiManager() - { - synchronized (sSync) { - if (sWifiManager == null) { - IBinder b = ServiceManager.getService(WIFI_SERVICE); - IWifiManager service = IWifiManager.Stub.asInterface(b); - sWifiManager = new WifiManager(service, mMainThread.getHandler()); - } - } - return sWifiManager; - } - - private NotificationManager getNotificationManager() { - synchronized (mSync) { - if (mNotificationManager == null) { - final Context outerContext = getOuterContext(); - mNotificationManager = new NotificationManager( - new ContextThemeWrapper(outerContext, - outerContext.getApplicationInfo().targetSdkVersion >= - Build.VERSION_CODES.HONEYCOMB - ? com.android.internal.R.style.Theme_Holo_Dialog - : com.android.internal.R.style.Theme_Dialog), - mMainThread.getHandler()); - } - } - return mNotificationManager; + ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name); + return fetcher == null ? null : fetcher.getService(this); } private WallpaperManager getWallpaperManager() { - synchronized (mSync) { - if (mWallpaperManager == null) { - mWallpaperManager = new WallpaperManager(getOuterContext(), - mMainThread.getHandler()); - } - } - return mWallpaperManager; - } - - private TelephonyManager getTelephonyManager() { - synchronized (mSync) { - if (mTelephonyManager == null) { - mTelephonyManager = new TelephonyManager(getOuterContext()); - } - } - return mTelephonyManager; - } - - private ClipboardManager getClipboardManager() { - synchronized (mSync) { - if (mClipboardManager == null) { - mClipboardManager = new ClipboardManager(getOuterContext(), - mMainThread.getHandler()); - } - } - return mClipboardManager; - } - - private LocationManager getLocationManager() { - synchronized (sSync) { - if (sLocationManager == null) { - IBinder b = ServiceManager.getService(LOCATION_SERVICE); - ILocationManager service = ILocationManager.Stub.asInterface(b); - sLocationManager = new LocationManager(service); - } - } - return sLocationManager; - } - - private CountryDetector getCountryDetector() { - synchronized (sSync) { - if (sCountryDetector == null) { - IBinder b = ServiceManager.getService(COUNTRY_DETECTOR); - ICountryDetector service = ICountryDetector.Stub.asInterface(b); - sCountryDetector = new CountryDetector(service); - } - } - return sCountryDetector; - } - - private SearchManager getSearchManager() { - synchronized (mSync) { - if (mSearchManager == null) { - mSearchManager = new SearchManager(getOuterContext(), mMainThread.getHandler()); - } - } - return mSearchManager; - } - - private SensorManager getSensorManager() { - synchronized (mSync) { - if (mSensorManager == null) { - mSensorManager = new SensorManager(mMainThread.getHandler().getLooper()); - } - } - return mSensorManager; - } - - private StorageManager getStorageManager() { - synchronized (mSync) { - if (mStorageManager == null) { - try { - mStorageManager = new StorageManager(mMainThread.getHandler().getLooper()); - } catch (RemoteException rex) { - Log.e(TAG, "Failed to create StorageManager", rex); - mStorageManager = null; - } - } - } - return mStorageManager; - } - - private Vibrator getVibrator() { - synchronized (mSync) { - if (mVibrator == null) { - mVibrator = new Vibrator(); - } - } - return mVibrator; - } - - private AudioManager getAudioManager() - { - if (mAudioManager == null) { - mAudioManager = new AudioManager(this); - } - return mAudioManager; + return (WallpaperManager) WALLPAPER_FETCHER.getService(this); } /* package */ static DropBoxManager createDropBoxManager() { @@ -1177,43 +1148,6 @@ class ContextImpl extends Context { return new DropBoxManager(service); } - private DropBoxManager getDropBoxManager() { - synchronized (mSync) { - if (mDropBoxManager == null) { - mDropBoxManager = createDropBoxManager(); - } - } - return mDropBoxManager; - } - - private DevicePolicyManager getDevicePolicyManager() { - synchronized (mSync) { - if (mDevicePolicyManager == null) { - mDevicePolicyManager = DevicePolicyManager.create(this, - mMainThread.getHandler()); - } - } - return mDevicePolicyManager; - } - - private UiModeManager getUiModeManager() { - synchronized (mSync) { - if (mUiModeManager == null) { - mUiModeManager = new UiModeManager(); - } - } - return mUiModeManager; - } - - private DownloadManager getDownloadManager() { - synchronized (mSync) { - if (mDownloadManager == null) { - mDownloadManager = new DownloadManager(getContentResolver(), getPackageName()); - } - } - return mDownloadManager; - } - @Override public int checkPermission(String permission, int pid, int uid) { if (permission == null) { diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 526129a..64a4d7a 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -867,13 +867,19 @@ public class Dialog implements DialogInterface, Window.Callback, } } - public ActionMode onStartActionMode(ActionMode.Callback callback) { + public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) { if (mActionBar != null) { return mActionBar.startActionMode(callback); } return null; } + public void onActionModeStarted(ActionMode mode) { + } + + public void onActionModeFinished(ActionMode mode) { + } + /** * @return The activity associated with this dialog, or null if there is no associated activity. */ diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index 01414fa..6e18533 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -33,10 +33,7 @@ import android.util.Pair; import java.io.File; import java.io.FileNotFoundException; import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; import java.util.List; -import java.util.Set; /** * The download manager is a system service that handles long-running HTTP downloads. Clients may @@ -281,46 +278,32 @@ public class DownloadManager { */ public static final String EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS = "extra_click_download_ids"; - // this array must contain all public columns - private static final String[] COLUMNS = new String[] { - COLUMN_ID, - COLUMN_MEDIAPROVIDER_URI, - Downloads.Impl.COLUMN_DESTINATION, - COLUMN_TITLE, - COLUMN_DESCRIPTION, - COLUMN_URI, - COLUMN_MEDIA_TYPE, - COLUMN_TOTAL_SIZE_BYTES, - COLUMN_LOCAL_URI, - COLUMN_STATUS, - COLUMN_REASON, - COLUMN_BYTES_DOWNLOADED_SO_FAR, - COLUMN_LAST_MODIFIED_TIMESTAMP, - COLUMN_LOCAL_FILENAME, - }; - - // columns to request from DownloadProvider - private static final String[] UNDERLYING_COLUMNS = new String[] { + /** + * columns to request from DownloadProvider. + * @hide + */ + public static final String[] UNDERLYING_COLUMNS = new String[] { Downloads.Impl._ID, + Downloads.Impl._DATA + " AS " + COLUMN_LOCAL_FILENAME, Downloads.Impl.COLUMN_MEDIAPROVIDER_URI, Downloads.Impl.COLUMN_DESTINATION, Downloads.Impl.COLUMN_TITLE, Downloads.Impl.COLUMN_DESCRIPTION, Downloads.Impl.COLUMN_URI, - Downloads.Impl.COLUMN_MIME_TYPE, - Downloads.Impl.COLUMN_TOTAL_BYTES, Downloads.Impl.COLUMN_STATUS, - Downloads.Impl.COLUMN_CURRENT_BYTES, - Downloads.Impl.COLUMN_LAST_MODIFICATION, Downloads.Impl.COLUMN_FILE_NAME_HINT, - Downloads.Impl._DATA, + Downloads.Impl.COLUMN_MIME_TYPE + " AS " + COLUMN_MEDIA_TYPE, + Downloads.Impl.COLUMN_TOTAL_BYTES + " AS " + COLUMN_TOTAL_SIZE_BYTES, + Downloads.Impl.COLUMN_LAST_MODIFICATION + " AS " + COLUMN_LAST_MODIFIED_TIMESTAMP, + Downloads.Impl.COLUMN_CURRENT_BYTES + " AS " + COLUMN_BYTES_DOWNLOADED_SO_FAR, + /* add the following 'computed' columns to the cursor. + * they are not 'returned' by the database, but their inclusion + * eliminates need to have lot of methods in CursorTranslator + */ + "'placeholder' AS " + COLUMN_LOCAL_URI, + "'placeholder' AS " + COLUMN_REASON }; - private static final Set<String> LONG_COLUMNS = new HashSet<String>( - Arrays.asList(COLUMN_ID, COLUMN_TOTAL_SIZE_BYTES, COLUMN_STATUS, COLUMN_REASON, - COLUMN_BYTES_DOWNLOADED_SO_FAR, COLUMN_LAST_MODIFIED_TIMESTAMP, - Downloads.Impl.COLUMN_DESTINATION)); - /** * This class contains all the information necessary to request a new download. The URI is the * only required parameter. @@ -871,11 +854,7 @@ public class DownloadManager { * @return the number of downloads actually removed */ public int remove(long... ids) { - if (ids == null || ids.length == 0) { - // called with nothing to remove! - throw new IllegalArgumentException("input param 'ids' can't be null"); - } - return mResolver.delete(mBaseUri, getWhereClauseForIds(ids), getWhereArgsForIds(ids)); + return markRowDeleted(ids); } /** @@ -1032,117 +1011,32 @@ public class DownloadManager { } @Override - public int getColumnIndex(String columnName) { - return Arrays.asList(COLUMNS).indexOf(columnName); - } - - @Override - public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException { - int index = getColumnIndex(columnName); - if (index == -1) { - throw new IllegalArgumentException("No such column: " + columnName); - } - return index; - } - - @Override - public String getColumnName(int columnIndex) { - int numColumns = COLUMNS.length; - if (columnIndex < 0 || columnIndex >= numColumns) { - throw new IllegalArgumentException("Invalid column index " + columnIndex + ", " - + numColumns + " columns exist"); - } - return COLUMNS[columnIndex]; - } - - @Override - public String[] getColumnNames() { - String[] returnColumns = new String[COLUMNS.length]; - System.arraycopy(COLUMNS, 0, returnColumns, 0, COLUMNS.length); - return returnColumns; - } - - @Override - public int getColumnCount() { - return COLUMNS.length; - } - - @Override - public byte[] getBlob(int columnIndex) { - throw new UnsupportedOperationException(); - } - - @Override - public double getDouble(int columnIndex) { - return getLong(columnIndex); - } - - private boolean isLongColumn(String column) { - return LONG_COLUMNS.contains(column); - } - - @Override - public float getFloat(int columnIndex) { - return (float) getDouble(columnIndex); - } - - @Override public int getInt(int columnIndex) { return (int) getLong(columnIndex); } @Override public long getLong(int columnIndex) { - return translateLong(getColumnName(columnIndex)); - } - - @Override - public short getShort(int columnIndex) { - return (short) getLong(columnIndex); + if (getColumnName(columnIndex).equals(COLUMN_REASON)) { + return getReason(super.getInt(getColumnIndex(Downloads.Impl.COLUMN_STATUS))); + } else if (getColumnName(columnIndex).equals(COLUMN_STATUS)) { + return translateStatus(super.getInt(getColumnIndex(Downloads.Impl.COLUMN_STATUS))); + } else { + return super.getLong(columnIndex); + } } @Override public String getString(int columnIndex) { - return translateString(getColumnName(columnIndex)); - } - - private String translateString(String column) { - if (isLongColumn(column)) { - return Long.toString(translateLong(column)); - } - if (column.equals(COLUMN_TITLE)) { - return getUnderlyingString(Downloads.Impl.COLUMN_TITLE); - } - if (column.equals(COLUMN_DESCRIPTION)) { - return getUnderlyingString(Downloads.Impl.COLUMN_DESCRIPTION); - } - if (column.equals(COLUMN_URI)) { - return getUnderlyingString(Downloads.Impl.COLUMN_URI); - } - if (column.equals(COLUMN_MEDIA_TYPE)) { - return getUnderlyingString(Downloads.Impl.COLUMN_MIME_TYPE); - } - if (column.equals(COLUMN_LOCAL_FILENAME)) { - return getUnderlyingString(Downloads.Impl._DATA); - } - if (column.equals(COLUMN_MEDIAPROVIDER_URI)) { - return getUnderlyingString(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI); - } - - assert column.equals(COLUMN_LOCAL_URI); - return getLocalUri(); + return (getColumnName(columnIndex).equals(COLUMN_LOCAL_URI)) ? getLocalUri() : + super.getString(columnIndex); } private String getLocalUri() { - long destinationType = getUnderlyingLong(Downloads.Impl.COLUMN_DESTINATION); - if (destinationType == Downloads.Impl.DESTINATION_FILE_URI) { - // return client-provided file URI for external download - return getUnderlyingString(Downloads.Impl.COLUMN_FILE_NAME_HINT); - } - - if (destinationType == Downloads.Impl.DESTINATION_EXTERNAL) { - // return stored destination for legacy external download - String localPath = getUnderlyingString(Downloads.Impl._DATA); + long destinationType = getLong(getColumnIndex(Downloads.Impl.COLUMN_DESTINATION)); + if (destinationType == Downloads.Impl.DESTINATION_FILE_URI || + destinationType == Downloads.Impl.DESTINATION_EXTERNAL) { + String localPath = getString(getColumnIndex(COLUMN_LOCAL_FILENAME)); if (localPath == null) { return null; } @@ -1150,38 +1044,10 @@ public class DownloadManager { } // return content URI for cache download - long downloadId = getUnderlyingLong(Downloads.Impl._ID); + long downloadId = getLong(getColumnIndex(Downloads.Impl._ID)); return ContentUris.withAppendedId(mBaseUri, downloadId).toString(); } - private long translateLong(String column) { - if (!isLongColumn(column)) { - // mimic behavior of underlying cursor -- most likely, throw NumberFormatException - return Long.valueOf(translateString(column)); - } - - if (column.equals(COLUMN_ID)) { - return getUnderlyingLong(Downloads.Impl._ID); - } - if (column.equals(COLUMN_TOTAL_SIZE_BYTES)) { - return getUnderlyingLong(Downloads.Impl.COLUMN_TOTAL_BYTES); - } - if (column.equals(COLUMN_STATUS)) { - return translateStatus((int) getUnderlyingLong(Downloads.Impl.COLUMN_STATUS)); - } - if (column.equals(COLUMN_REASON)) { - return getReason((int) getUnderlyingLong(Downloads.Impl.COLUMN_STATUS)); - } - if (column.equals(COLUMN_BYTES_DOWNLOADED_SO_FAR)) { - return getUnderlyingLong(Downloads.Impl.COLUMN_CURRENT_BYTES); - } - if (column.equals(Downloads.Impl.COLUMN_DESTINATION)) { - return getUnderlyingLong(Downloads.Impl.COLUMN_DESTINATION); - } - assert column.equals(COLUMN_LAST_MODIFIED_TIMESTAMP); - return getUnderlyingLong(Downloads.Impl.COLUMN_LAST_MODIFICATION); - } - private long getReason(int status) { switch (translateStatus(status)) { case STATUS_FAILED: @@ -1249,14 +1115,6 @@ public class DownloadManager { } } - private long getUnderlyingLong(String column) { - return super.getLong(super.getColumnIndex(column)); - } - - private String getUnderlyingString(String column) { - return super.getString(super.getColumnIndex(column)); - } - private int translateStatus(int status) { switch (status) { case Downloads.Impl.STATUS_PENDING: diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 9970418..d3a4f33 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -22,6 +22,7 @@ import android.animation.AnimatorListenerAdapter; import android.content.res.TypedArray; import android.os.Bundle; import android.os.Handler; +import android.os.Looper; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -99,6 +100,20 @@ public abstract class FragmentManager { public abstract FragmentTransaction openTransaction(); /** + * After a {@link FragmentTransaction} is committed with + * {@link FragmentTransaction#commit FragmentTransaction.commit()}, it + * is scheduled to be executed asynchronously on the process's main thread. + * If you want to immediately executing any such pending operations, you + * can call this function (only from the main thread) to do so. Note that + * all callbacks and other related behavior will be done from within this + * call, so be careful about where this is called from. + * + * @return Returns true if there were any pending transactions to be + * executed. + */ + public abstract boolean executePendingTransactions(); + + /** * Finds a fragment that was identified by the given id either when inflated * from XML or as the container ID when added in a transaction. This first * searches through fragments that are currently added to the manager's @@ -132,7 +147,15 @@ public abstract class FragmentManager { * Pop the top state off the back stack. Returns true if there was one * to pop, else false. */ - public abstract boolean popBackStack(); + public abstract void popBackStack(); + + /** + * Like {@link #popBackStack()}, but performs the operation immediately + * inside of the call. This is like calling {@link #executePendingTransactions()} + * afterwards. + * @return Returns true if there was something popped, else false. + */ + public abstract boolean popBackStackImmediate(); /** * Pop the last fragment transition from the manager's fragment @@ -143,7 +166,15 @@ public abstract class FragmentManager { * the named state itself is popped. If null, only the top state is popped. * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. */ - public abstract boolean popBackStack(String name, int flags); + public abstract void popBackStack(String name, int flags); + + /** + * Like {@link #popBackStack(String, int)}, but performs the operation immediately + * inside of the call. This is like calling {@link #executePendingTransactions()} + * afterwards. + * @return Returns true if there was something popped, else false. + */ + public abstract boolean popBackStackImmediate(String name, int flags); /** * Pop all back stack states up to the one with the given identifier. @@ -155,7 +186,15 @@ public abstract class FragmentManager { * the named state itself is popped. * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. */ - public abstract boolean popBackStack(int id, int flags); + public abstract void popBackStack(int id, int flags); + + /** + * Like {@link #popBackStack(int, int)}, but performs the operation immediately + * inside of the call. This is like calling {@link #executePendingTransactions()} + * afterwards. + * @return Returns true if there was something popped, else false. + */ + public abstract boolean popBackStackImmediate(int id, int flags); /** * Return the number of entries currently in the back stack. @@ -300,17 +339,58 @@ final class FragmentManagerImpl extends FragmentManager { } @Override - public boolean popBackStack() { + public boolean executePendingTransactions() { + return execPendingActions(); + } + + @Override + public void popBackStack() { + enqueueAction(new Runnable() { + @Override public void run() { + popBackStackState(mActivity.mHandler, null, -1, 0); + } + }, false); + } + + @Override + public boolean popBackStackImmediate() { + checkStateLoss(); + executePendingTransactions(); return popBackStackState(mActivity.mHandler, null, -1, 0); } @Override - public boolean popBackStack(String name, int flags) { + public void popBackStack(final String name, final int flags) { + enqueueAction(new Runnable() { + @Override public void run() { + popBackStackState(mActivity.mHandler, name, -1, flags); + } + }, false); + } + + @Override + public boolean popBackStackImmediate(String name, int flags) { + checkStateLoss(); + executePendingTransactions(); return popBackStackState(mActivity.mHandler, name, -1, flags); } @Override - public boolean popBackStack(int id, int flags) { + public void popBackStack(final int id, final int flags) { + if (id < 0) { + throw new IllegalArgumentException("Bad id: " + id); + } + enqueueAction(new Runnable() { + @Override public void run() { + popBackStackState(mActivity.mHandler, null, id, flags); + } + }, false); + } + + @Override + public boolean popBackStackImmediate(int id, int flags) { + checkStateLoss(); + executePendingTransactions(); if (id < 0) { throw new IllegalArgumentException("Bad id: " + id); } @@ -849,16 +929,20 @@ final class FragmentManagerImpl extends FragmentManager { return null; } + private void checkStateLoss() { + if (mStateSaved) { + throw new IllegalStateException( + "Can not perform this action after onSaveInstanceState"); + } + if (mNoTransactionsBecause != null) { + throw new IllegalStateException( + "Can not perform this action inside of " + mNoTransactionsBecause); + } + } + public void enqueueAction(Runnable action, boolean allowStateLoss) { if (!allowStateLoss) { - if (mStateSaved) { - throw new IllegalStateException( - "Can not perform this action after onSaveInstanceState"); - } - if (mNoTransactionsBecause != null) { - throw new IllegalStateException( - "Can not perform this action inside of " + mNoTransactionsBecause); - } + checkStateLoss(); } synchronized (this) { if (mActivity == null) { @@ -934,17 +1018,23 @@ final class FragmentManagerImpl extends FragmentManager { /** * Only call from main thread! */ - public void execPendingActions() { + public boolean execPendingActions() { if (mExecutingActions) { - throw new IllegalStateException("Recursive entry to execPendingActions"); + throw new IllegalStateException("Recursive entry to executePendingTransactions"); } + if (Looper.myLooper() != Looper.getMainLooper()) { + throw new IllegalStateException("Must be called from main thread of process"); + } + + boolean didSomething = false; + while (true) { int numActions; synchronized (this) { if (mPendingActions == null || mPendingActions.size() == 0) { - return; + return didSomething; } numActions = mPendingActions.size(); @@ -961,6 +1051,7 @@ final class FragmentManagerImpl extends FragmentManager { mTmpActions[i].run(); } mExecutingActions = false; + didSomething = true; } } @@ -984,19 +1075,14 @@ final class FragmentManagerImpl extends FragmentManager { if (mBackStack == null) { return false; } - if (name == null && id < 0 && (flags&Activity.POP_BACK_STACK_INCLUSIVE) == 0) { + if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) { int last = mBackStack.size()-1; if (last < 0) { return false; } final BackStackRecord bss = mBackStack.remove(last); - enqueueAction(new Runnable() { - public void run() { - if (DEBUG) Log.v(TAG, "Popping back stack state: " + bss); - bss.popFromBackStack(true); - reportBackStackChanged(); - } - }, false); + bss.popFromBackStack(true); + reportBackStackChanged(); } else { int index = -1; if (name != null || id >= 0) { @@ -1016,7 +1102,7 @@ final class FragmentManagerImpl extends FragmentManager { if (index < 0) { return false; } - if ((flags&Activity.POP_BACK_STACK_INCLUSIVE) != 0) { + if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) { index--; // Consume all following entries that match. while (index >= 0) { @@ -1038,16 +1124,12 @@ final class FragmentManagerImpl extends FragmentManager { for (int i=mBackStack.size()-1; i>index; i--) { states.add(mBackStack.remove(i)); } - enqueueAction(new Runnable() { - public void run() { - final int LAST = states.size()-1; - for (int i=0; i<=LAST; i++) { - if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i)); - states.get(i).popFromBackStack(i == LAST); - } - reportBackStackChanged(); - } - }, false); + final int LAST = states.size()-1; + for (int i=0; i<=LAST; i++) { + if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i)); + states.get(i).popFromBackStack(i == LAST); + } + reportBackStackChanged(); } return true; } @@ -1084,6 +1166,10 @@ final class FragmentManagerImpl extends FragmentManager { } Parcelable saveAllState() { + // Make sure all pending operations have now been executed to get + // our state update-to-date. + execPendingActions(); + mStateSaved = true; if (mActive == null || mActive.size() <= 0) { diff --git a/core/java/android/app/ProgressDialog.java b/core/java/android/app/ProgressDialog.java index 4c5d28e..af1b294 100644 --- a/core/java/android/app/ProgressDialog.java +++ b/core/java/android/app/ProgressDialog.java @@ -42,7 +42,7 @@ import java.text.NumberFormat; */ public class ProgressDialog extends AlertDialog { - /** Creates a ProgressDialog with a ciruclar, spinning progress + /** Creates a ProgressDialog with a circular, spinning progress * bar. This is the default. */ public static final int STYLE_SPINNER = 0; diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index 41bb364..7efb7fd 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -1081,7 +1081,7 @@ public class SQLiteDatabase extends SQLiteClosable { mConnectionPool.close(); } } finally { - unlock(); + unlock(); } } @@ -1772,8 +1772,7 @@ public class SQLiteDatabase extends SQLiteClosable { */ public int updateWithOnConflict(String table, ContentValues values, String whereClause, String[] whereArgs, int conflictAlgorithm) { - int setValuesSize = values.size(); - if (values == null || setValuesSize == 0) { + if (values == null || values.size() == 0) { throw new IllegalArgumentException("Empty values"); } @@ -1784,6 +1783,7 @@ public class SQLiteDatabase extends SQLiteClosable { sql.append(" SET "); // move all bind args to one array + int setValuesSize = values.size(); int bindArgsSize = (whereArgs == null) ? setValuesSize : (setValuesSize + whereArgs.length); Object[] bindArgs = new Object[bindArgsSize]; int i = 0; @@ -2118,7 +2118,7 @@ public class SQLiteDatabase extends SQLiteClosable { int maxCacheSz = (mConnectionNum == 0) ? mMaxSqlCacheSize : mParentConnObj.mMaxSqlCacheSize; - + if (SQLiteDebug.DEBUG_SQL_CACHE) { boolean printWarning = (mConnectionNum == 0) ? (!mCacheFullWarning && mCompiledQueries.size() == maxCacheSz) diff --git a/core/java/android/net/http/Connection.java b/core/java/android/net/http/Connection.java index 43fb5f1..95cecd2 100644 --- a/core/java/android/net/http/Connection.java +++ b/core/java/android/net/http/Connection.java @@ -437,8 +437,7 @@ abstract class Connection { ret = false; String error; if (errorId < 0) { - error = mContext.getText( - EventHandler.errorStringResources[-errorId]).toString(); + error = ErrorStrings.getString(errorId, mContext); } else { Throwable cause = e.getCause(); error = cause != null ? cause.toString() : e.getMessage(); diff --git a/core/java/android/net/http/ErrorStrings.java b/core/java/android/net/http/ErrorStrings.java new file mode 100644 index 0000000..8383681 --- /dev/null +++ b/core/java/android/net/http/ErrorStrings.java @@ -0,0 +1,99 @@ +/* + * 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.net.http; + +import android.content.Context; +import android.util.Log; + +/** + * Localized strings for the error codes defined in EventHandler. + * + * {@hide} + */ +public class ErrorStrings { + private ErrorStrings() { /* Utility class, don't instantiate. */ } + + private static final String LOGTAG = "Http"; + + /** + * Get the localized error message resource for the given error code. + * If the code is unknown, we'll return a generic error message. + */ + public static String getString(int errorCode, Context context) { + return context.getText(getResource(errorCode)).toString(); + } + + /** + * Get the localized error message resource for the given error code. + * If the code is unknown, we'll return a generic error message. + */ + public static int getResource(int errorCode) { + switch(errorCode) { + case EventHandler.OK: + return com.android.internal.R.string.httpErrorOk; + + case EventHandler.ERROR: + return com.android.internal.R.string.httpError; + + case EventHandler.ERROR_LOOKUP: + return com.android.internal.R.string.httpErrorLookup; + + case EventHandler.ERROR_UNSUPPORTED_AUTH_SCHEME: + return com.android.internal.R.string.httpErrorUnsupportedAuthScheme; + + case EventHandler.ERROR_AUTH: + return com.android.internal.R.string.httpErrorAuth; + + case EventHandler.ERROR_PROXYAUTH: + return com.android.internal.R.string.httpErrorProxyAuth; + + case EventHandler.ERROR_CONNECT: + return com.android.internal.R.string.httpErrorConnect; + + case EventHandler.ERROR_IO: + return com.android.internal.R.string.httpErrorIO; + + case EventHandler.ERROR_TIMEOUT: + return com.android.internal.R.string.httpErrorTimeout; + + case EventHandler.ERROR_REDIRECT_LOOP: + return com.android.internal.R.string.httpErrorRedirectLoop; + + case EventHandler.ERROR_UNSUPPORTED_SCHEME: + return com.android.internal.R.string.httpErrorUnsupportedScheme; + + case EventHandler.ERROR_FAILED_SSL_HANDSHAKE: + return com.android.internal.R.string.httpErrorFailedSslHandshake; + + case EventHandler.ERROR_BAD_URL: + return com.android.internal.R.string.httpErrorBadUrl; + + case EventHandler.FILE_ERROR: + return com.android.internal.R.string.httpErrorFile; + + case EventHandler.FILE_NOT_FOUND_ERROR: + return com.android.internal.R.string.httpErrorFileNotFound; + + case EventHandler.TOO_MANY_REQUESTS_ERROR: + return com.android.internal.R.string.httpErrorTooManyRequests; + + default: + Log.w(LOGTAG, "Using generic message for unknown error code: " + errorCode); + return com.android.internal.R.string.httpError; + } + } +} diff --git a/core/java/android/net/http/EventHandler.java b/core/java/android/net/http/EventHandler.java index 2aa05eb..3fd471d 100644 --- a/core/java/android/net/http/EventHandler.java +++ b/core/java/android/net/http/EventHandler.java @@ -68,25 +68,6 @@ public interface EventHandler { /** Too many requests queued */ public static final int TOO_MANY_REQUESTS_ERROR = -15; - final static int[] errorStringResources = { - com.android.internal.R.string.httpErrorOk, - com.android.internal.R.string.httpError, - com.android.internal.R.string.httpErrorLookup, - com.android.internal.R.string.httpErrorUnsupportedAuthScheme, - com.android.internal.R.string.httpErrorAuth, - com.android.internal.R.string.httpErrorProxyAuth, - com.android.internal.R.string.httpErrorConnect, - com.android.internal.R.string.httpErrorIO, - com.android.internal.R.string.httpErrorTimeout, - com.android.internal.R.string.httpErrorRedirectLoop, - com.android.internal.R.string.httpErrorUnsupportedScheme, - com.android.internal.R.string.httpErrorFailedSslHandshake, - com.android.internal.R.string.httpErrorBadUrl, - com.android.internal.R.string.httpErrorFile, - com.android.internal.R.string.httpErrorFileNotFound, - com.android.internal.R.string.httpErrorTooManyRequests - }; - /** * Called after status line has been sucessfully processed. * @param major_version HTTP version advertised by server. major diff --git a/core/java/android/os/DropBoxManager.java b/core/java/android/os/DropBoxManager.java index a47c66a..47a7696 100644 --- a/core/java/android/os/DropBoxManager.java +++ b/core/java/android/os/DropBoxManager.java @@ -169,7 +169,12 @@ public class DropBoxManager { is = getInputStream(); if (is == null) return null; byte[] buf = new byte[maxBytes]; - return new String(buf, 0, Math.max(0, is.read(buf))); + int readBytes = 0; + int n = 0; + while (n >= 0 && (readBytes += n) < maxBytes) { + n = is.read(buf, readBytes, maxBytes - readBytes); + } + return new String(buf, 0, readBytes); } catch (IOException e) { return null; } finally { diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 22876c0..97f015b 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -106,6 +106,8 @@ public final class StrictMode { private static final String TAG = "StrictMode"; private static final boolean LOG_V = false; + private static final boolean IS_USER_BUILD = "user".equals(Build.TYPE); + // Only log a duplicate stack trace to the logs every second. private static final long MIN_LOG_INTERVAL_MS = 1000; @@ -605,7 +607,7 @@ public final class StrictMode { // Sets up CloseGuard in Dalvik/libcore private static void setCloseGuardEnabled(boolean enabled) { - if (!(CloseGuard.getReporter() instanceof AndroidBlockGuardPolicy)) { + if (!(CloseGuard.getReporter() instanceof AndroidCloseGuardReporter)) { CloseGuard.setReporter(new AndroidCloseGuardReporter()); } CloseGuard.setEnabled(enabled); @@ -693,7 +695,8 @@ public final class StrictMode { public static boolean conditionallyEnableDebugLogging() { // For debug builds, log event loop stalls to dropbox for analysis. // Similar logic also appears in ActivityThread.java for system apps. - if ("user".equals(Build.TYPE)) { + if (IS_USER_BUILD) { + setCloseGuardEnabled(false); return false; } StrictMode.setThreadPolicyMask( @@ -705,6 +708,7 @@ public final class StrictMode { StrictMode.DETECT_VM_CLOSABLE_LEAKS | StrictMode.PENALTY_DROPBOX | StrictMode.PENALTY_LOG; + setCloseGuardEnabled(vmClosableObjectLeaksEnabled()); return true; } @@ -1238,6 +1242,11 @@ public final class StrictMode { mContainerState = threadState; } + // Empty constructor for the NO_OP_SPAN + protected Span() { + mContainerState = null; + } + /** * To be called when the critical span is complete (i.e. the * animation is done animating). This can be called on any @@ -1284,6 +1293,13 @@ public final class StrictMode { } } + // The no-op span that's used in user builds. + private static final Span NO_OP_SPAN = new Span() { + public void finish() { + // Do nothing. + } + }; + /** * Linked lists of active spans and a freelist. * @@ -1325,6 +1341,9 @@ public final class StrictMode { * @hide */ public static Span enterCriticalSpan(String name) { + if (IS_USER_BUILD) { + return NO_OP_SPAN; + } if (name == null || name.isEmpty()) { throw new IllegalArgumentException("name must be non-null and non-empty"); } diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java index 0ce69ad..39f3cee 100644 --- a/core/java/android/preference/PreferenceActivity.java +++ b/core/java/android/preference/PreferenceActivity.java @@ -23,6 +23,7 @@ import org.xmlpull.v1.XmlPullParserException; import android.app.Fragment; import android.app.FragmentBreadCrumbs; +import android.app.FragmentManager; import android.app.FragmentTransaction; import android.app.ListActivity; import android.content.Context; @@ -902,7 +903,8 @@ public abstract class PreferenceActivity extends ListActivity implements } private void switchToHeaderInner(String fragmentName, Bundle args, int direction) { - getFragmentManager().popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE); + getFragmentManager().popBackStack(BACK_STACK_PREFS, + FragmentManager.POP_BACK_STACK_INCLUSIVE); Fragment f = Fragment.instantiate(this, fragmentName, args); FragmentTransaction transaction = getFragmentManager().openTransaction(); transaction.setTransition(direction == 0 ? FragmentTransaction.TRANSIT_NONE @@ -934,7 +936,8 @@ public abstract class PreferenceActivity extends ListActivity implements if (mCurHeader == header) { // This is the header we are currently displaying. Just make sure // to pop the stack up to its root state. - getFragmentManager().popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE); + getFragmentManager().popBackStack(BACK_STACK_PREFS, + FragmentManager.POP_BACK_STACK_INCLUSIVE); } else { int direction = mHeaders.indexOf(header) - mHeaders.indexOf(mCurHeader); switchToHeaderInner(header.fragment, header.fragmentArguments, direction); @@ -1061,14 +1064,14 @@ public abstract class PreferenceActivity extends ListActivity implements setResult(resultCode, resultData); finish(); } else { + // XXX be smarter about popping the stack. + onBackPressed(); if (caller != null) { if (caller.getTargetFragment() != null) { caller.getTargetFragment().onActivityResult(caller.getTargetRequestCode(), resultCode, resultData); } } - // XXX be smarter about popping the stack. - onBackPressed(); } } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index a54f342..dd0f477 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -79,7 +79,7 @@ interface IWindowManager void executeAppTransition(); void setAppStartingWindow(IBinder token, String pkg, int theme, CharSequence nonLocalizedLabel, int labelRes, - int icon, IBinder transferFrom, boolean createIfNeeded); + int icon, int windowFlags, IBinder transferFrom, boolean createIfNeeded); void setAppWillBeHidden(IBinder token); void setAppVisibility(IBinder token, boolean visible); void startAppFreezingScreen(IBinder token, int configChanges); diff --git a/core/java/android/view/MenuItem.java b/core/java/android/view/MenuItem.java index 99da43b..602c765 100644 --- a/core/java/android/view/MenuItem.java +++ b/core/java/android/view/MenuItem.java @@ -405,6 +405,11 @@ public interface MenuItem { /** * Sets how this item should display in the presence of an Action Bar. + * The parameter actionEnum is a flag set. One of {@link #SHOW_AS_ACTION_ALWAYS}, + * {@link #SHOW_AS_ACTION_IF_ROOM}, or {@link #SHOW_AS_ACTION_NEVER} should + * be used, and you may optionally OR the value with {@link #SHOW_AS_ACTION_WITH_TEXT}. + * SHOW_AS_ACTION_WITH_TEXT requests that when the item is shown as an action, + * it should be shown with a text label. * * @param actionEnum How the item should display. One of * {@link #SHOW_AS_ACTION_ALWAYS}, {@link #SHOW_AS_ACTION_IF_ROOM}, or diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 026f1a0..e6eb46e 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1258,6 +1258,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility static final int VIEW_STATE_ENABLED = 1 << 3; static final int VIEW_STATE_PRESSED = 1 << 4; static final int VIEW_STATE_ACTIVATED = 1 << 5; + static final int VIEW_STATE_ACCELERATED = 1 << 6; static final int[] VIEW_STATE_IDS = new int[] { R.attr.state_window_focused, VIEW_STATE_WINDOW_FOCUSED, @@ -1266,9 +1267,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility R.attr.state_enabled, VIEW_STATE_ENABLED, R.attr.state_pressed, VIEW_STATE_PRESSED, R.attr.state_activated, VIEW_STATE_ACTIVATED, + R.attr.state_accelerated, VIEW_STATE_ACCELERATED, }; static { + if ((VIEW_STATE_IDS.length/2) != R.styleable.ViewDrawableStates.length) { + throw new IllegalStateException( + "VIEW_STATE_IDs array length does not match ViewDrawableStates style array"); + } int[] orderedIds = new int[VIEW_STATE_IDS.length]; for (int i = 0; i < R.styleable.ViewDrawableStates.length; i++) { int viewState = R.styleable.ViewDrawableStates[i]; @@ -6298,12 +6304,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } final AttachInfo ai = mAttachInfo; final ViewParent p = mParent; - if (p != null && ai != null && ai.mHardwareAccelerated) { - // fast-track for GL-enabled applications; just invalidate the whole hierarchy - // with a null dirty rect, which tells the ViewRoot to redraw everything - p.invalidateChild(this, null); - return; - } + if (p != null && ai != null) { final Rect r = ai.mTmpInvalRect; r.set(0, 0, mRight - mLeft, mBottom - mTop); @@ -7176,6 +7177,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility //System.out.println("Attached! " + this); mAttachInfo = info; mWindowAttachCount++; + // We will need to evaluate the drawable state at least once. + mPrivateFlags |= DRAWABLE_STATE_DIRTY; if (mFloatingTreeObserver != null) { info.mTreeObserver.merge(mFloatingTreeObserver); mFloatingTreeObserver = null; @@ -7190,6 +7193,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if (vis != GONE) { onWindowVisibilityChanged(vis); } + if ((mPrivateFlags&DRAWABLE_STATE_DIRTY) != 0) { + // If nobody has evaluated the drawable state yet, then do it now. + refreshDrawableState(); + } } void dispatchDetachedFromWindow() { @@ -8562,6 +8569,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if ((privateFlags & SELECTED) != 0) viewStateIndex |= VIEW_STATE_SELECTED; if (hasWindowFocus()) viewStateIndex |= VIEW_STATE_WINDOW_FOCUSED; if ((privateFlags & ACTIVATED) != 0) viewStateIndex |= VIEW_STATE_ACTIVATED; + if (mAttachInfo != null && mAttachInfo.mHardwareAccelerationRequested) { + // This is set if HW acceleration is requested, even if the current + // process doesn't allow it. This is just to allow app preview + // windows to better match their app. + viewStateIndex |= VIEW_STATE_ACCELERATED; + } drawableState = VIEW_STATE_SETS[viewStateIndex]; @@ -10503,6 +10516,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility Surface mSurface; boolean mHardwareAccelerated; + boolean mHardwareAccelerationRequested; HardwareRenderer mHardwareRenderer; /** diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index e04916f..1972692 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -467,19 +467,25 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn } private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) { - // Only enable hardware acceleration if we are not in the system process - // The window manager creates ViewRoots to display animated preview windows - // of launching apps and we don't want those to be hardware accelerated - if (!HardwareRenderer.sRendererDisabled) { - // Try to enable hardware acceleration if requested - if (attrs != null && - (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0) { + mAttachInfo.mHardwareAccelerated = false; + mAttachInfo.mHardwareAccelerationRequested = false; + + // Try to enable hardware acceleration if requested + if (attrs != null && + (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0) { + // Only enable hardware acceleration if we are not in the system process + // The window manager creates ViewRoots to display animated preview windows + // of launching apps and we don't want those to be hardware accelerated + if (!HardwareRenderer.sRendererDisabled) { final boolean translucent = attrs.format != PixelFormat.OPAQUE; if (mAttachInfo.mHardwareRenderer != null) { mAttachInfo.mHardwareRenderer.destroy(true); } mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent); - mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareRenderer != null; + mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested + = mAttachInfo.mHardwareRenderer != null; + } else if (HardwareRenderer.isAvailable()) { + mAttachInfo.mHardwareAccelerationRequested = true; } } } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 9fadc58..5385cd9 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -313,12 +313,31 @@ public abstract class Window { public boolean onSearchRequested(); /** - * Called when an action mode is being started. + * Called when an action mode is being started for this window. Gives the + * callback an opportunity to handle the action mode in its own unique and + * beautiful way. If this method returns null the system can choose a way + * to present the mode or choose not to start the mode at all. * * @param callback Callback to control the lifecycle of this action mode - * @return The ActionMode that was started, or null if it was canceled + * @return The ActionMode that was started, or null if the system should present it */ - public ActionMode onStartActionMode(ActionMode.Callback callback); + public ActionMode onWindowStartingActionMode(ActionMode.Callback callback); + + /** + * Called when an action mode has been started. The appropriate mode callback + * method will have already been invoked. + * + * @param mode The new mode that has just been started. + */ + public void onActionModeStarted(ActionMode mode); + + /** + * Called when an action mode has been finished. The appropriate mode callback + * method will have already been invoked. + * + * @param mode The mode that was just finished. + */ + public void onActionModeFinished(ActionMode mode); } public Window(Context context) { @@ -428,6 +447,10 @@ public abstract class Window { mHardwareAccelerated = hardwareAccelerated; } + public boolean isHardwareAccelerated() { + return mHardwareAccelerated; + } + public final void addView(View view, ViewGroup.LayoutParams params) { // Let this throw an exception on a bad params. WindowManager.LayoutParams wp = (WindowManager.LayoutParams)params; diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index c657a1c..f9e7d18 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -65,6 +65,14 @@ public interface WindowManager extends ViewManager { */ public void removeViewImmediate(View view); + /** + * Return true if this window manager is configured to request hardware + * accelerated windows. This does <em>not</em> guarantee that they will + * actually be accelerated, since that depends on the device supporting them. + * @hide + */ + public boolean isHardwareAccelerated(); + public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable { /** diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 0973599..07953d6 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -80,6 +80,10 @@ public class WindowManagerImpl implements WindowManager { return mWindowManager; } + public boolean isHardwareAccelerated() { + return false; + } + public void addView(View view) { addView(view, new WindowManager.LayoutParams( diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 4deff5e..5a9cd97 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -473,6 +473,7 @@ public interface WindowManagerPolicy { * no data is found in the resource. * @param labelRes The resource ID the application would like to use as its name. * @param icon The resource ID the application would like to use as its icon. + * @param windowFlags Window layout flags. * * @return Optionally you can return the View that was used to create the * window, for easy removal in removeStartingWindow. @@ -481,7 +482,7 @@ public interface WindowManagerPolicy { */ public View addStartingWindow(IBinder appToken, String packageName, int theme, CharSequence nonLocalizedLabel, - int labelRes, int icon); + int labelRes, int icon, int windowFlags); /** * Called when the first window of an application has been displayed, while diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 1d75b42..5e7a133 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -1432,6 +1432,17 @@ public final class InputMethodManager { } } + public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) { + synchronized (mH) { + try { + return mService.setCurrentInputMethodSubtype(subtype); + } catch (RemoteException e) { + Log.w(TAG, "IME died: " + mCurId, e); + return false; + } + } + } + public boolean switchToLastInputMethod(IBinder imeToken) { synchronized (mH) { try { diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index cce7914..d3c0ffd 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -26,6 +26,7 @@ import android.graphics.Bitmap; import android.net.ParseException; import android.net.Uri; import android.net.WebAddress; +import android.net.http.ErrorStrings; import android.net.http.SslCertificate; import android.os.Handler; import android.os.Message; @@ -323,16 +324,20 @@ class BrowserFrame extends Handler { * native callback * Report an error to an activity. * @param errorCode The HTTP error code. - * @param description A String description. + * @param description Optional human-readable description. If no description + * is given, we'll use a standard localized error message. + * @param failingUrl The URL that was being loaded when the error occurred. * TODO: Report all errors including resource errors but include some kind * of domain identifier. Change errorCode to an enum for a cleaner * interface. */ - private void reportError(final int errorCode, final String description, - final String failingUrl) { + private void reportError(int errorCode, String description, String failingUrl) { // As this is called for the main resource and loading will be stopped // after, reset the state variables. resetLoadingStates(); + if (description == null || description.isEmpty()) { + description = ErrorStrings.getString(errorCode, mContext); + } mCallbackProxy.onReceivedError(errorCode, description, failingUrl); } @@ -649,23 +654,6 @@ class BrowserFrame extends Handler { } /** - * Called by JNI. - * Read from an InputStream into a supplied byte[] - * This method catches any exceptions so they don't crash the JVM. - * @param inputStream InputStream to read from. - * @param output Bytearray that gets the output. - * @return the number of bytes read, or -i if then end of stream has been reached - */ - private static int readFromStream(InputStream inputStream, byte[] output) { - try { - return inputStream.read(output); - } catch(java.io.IOException e) { - // If we get an exception, return end of stream - return -1; - } - } - - /** * Get the InputStream for an Android resource * There are three different kinds of android resources: * - file:///android_res @@ -854,8 +842,6 @@ class BrowserFrame extends Handler { this, url, loaderHandle, synchronous, isMainFramePage, mainResource, userGesture, postDataIdentifier, username, password); - mCallbackProxy.onLoadResource(url); - if (LoadListener.getNativeLoaderCount() > MAX_OUTSTANDING_REQUESTS) { // send an error message, so that loadListener can be deleted // after this is returned. This is important as LoadListener's @@ -867,7 +853,11 @@ class BrowserFrame extends Handler { return loadListener; } - FrameLoader loader = new FrameLoader(loadListener, mSettings, method); + // Note that we are intentionally skipping + // inputStreamForAndroidResource. This is so that FrameLoader will use + // the various StreamLoader classes to handle assets. + FrameLoader loader = new FrameLoader(loadListener, mSettings, method, + mCallbackProxy.shouldInterceptRequest(url)); loader.setHeaders(headers); loader.setPostData(postData); // Set the load mode to the mode used for the current page. @@ -884,6 +874,15 @@ class BrowserFrame extends Handler { return !synchronous ? loadListener : null; } + // Called by jni from the chrome network stack. + private WebResourceResponse shouldInterceptRequest(String url) { + InputStream androidResource = inputStreamForAndroidResource(url); + if (androidResource != null) { + return new WebResourceResponse(null, null, androidResource); + } + return mCallbackProxy.shouldInterceptRequest(url); + } + /** * Set the progress for the browser activity. Called by native code. * Uses a delay so it does not happen too often. diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java index 0078e7a..160fc2e 100644 --- a/core/java/android/webkit/CallbackProxy.java +++ b/core/java/android/webkit/CallbackProxy.java @@ -1008,13 +1008,17 @@ class CallbackProxy extends Handler { sendMessage(obtainMessage(UPDATE_VISITED, isReload ? 1 : 0, 0, url)); } - public void onLoadResource(String url) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. + WebResourceResponse shouldInterceptRequest(String url) { if (mWebViewClient == null) { - return; + return null; + } + // Note: This method does _not_ send a message. + WebResourceResponse r = + mWebViewClient.shouldInterceptRequest(mWebView, url); + if (r == null) { + sendMessage(obtainMessage(LOAD_RESOURCE, url)); } - sendMessage(obtainMessage(LOAD_RESOURCE, url)); + return r; } public void onUnhandledKeyEvent(KeyEvent event) { diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java index 021b53c..951dab3 100644 --- a/core/java/android/webkit/FrameLoader.java +++ b/core/java/android/webkit/FrameLoader.java @@ -16,6 +16,7 @@ package android.webkit; +import android.net.http.ErrorStrings; import android.net.http.EventHandler; import android.net.http.RequestHandle; import android.os.Build; @@ -37,6 +38,7 @@ class FrameLoader { private String mReferrer; private String mContentType; private final String mUaprofHeader; + private final WebResourceResponse mInterceptResponse; private static final int URI_PROTOCOL = 0x100; @@ -53,12 +55,13 @@ class FrameLoader { private static final String LOGTAG = "webkit"; FrameLoader(LoadListener listener, WebSettings settings, - String method) { + String method, WebResourceResponse interceptResponse) { mListener = listener; mHeaders = null; mMethod = method; mCacheMode = WebSettings.LOAD_NORMAL; mSettings = settings; + mInterceptResponse = interceptResponse; mUaprofHeader = mListener.getContext().getResources().getString( com.android.internal.R.string.config_useragentprofile_url, Build.MODEL); } @@ -99,7 +102,17 @@ class FrameLoader { public boolean executeLoad() { String url = mListener.url(); - if (URLUtil.isNetworkUrl(url)){ + // Process intercepted requests first as they could be any url. + if (mInterceptResponse != null) { + if (mListener.isSynchronous()) { + mInterceptResponse.loader(mListener).load(); + } else { + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_ADD_STREAMLOADER, + mInterceptResponse.loader(mListener)).sendToTarget(); + } + return true; + } else if (URLUtil.isNetworkUrl(url)){ if (mSettings.getBlockNetworkLoads()) { mListener.error(EventHandler.ERROR_BAD_URL, mListener.getContext().getString( @@ -247,8 +260,7 @@ class FrameLoader { error = EventHandler.ERROR_BAD_URL; } if (!ret) { - mListener.error(error, mListener.getContext().getText( - EventHandler.errorStringResources[Math.abs(error)]).toString()); + mListener.error(error, ErrorStrings.getString(error, mListener.getContext())); return false; } return true; @@ -303,9 +315,8 @@ class FrameLoader { // it has gone. // Generate a file not found error int err = EventHandler.FILE_NOT_FOUND_ERROR; - mListener.error(err, mListener.getContext().getText( - EventHandler.errorStringResources[Math.abs(err)]) - .toString()); + mListener.error(err, + ErrorStrings.getString(err, mListener.getContext())); } return true; } diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java index 33f5655..a29299d 100644 --- a/core/java/android/webkit/LoadListener.java +++ b/core/java/android/webkit/LoadListener.java @@ -836,8 +836,7 @@ class LoadListener extends Handler implements EventHandler { mRequestHandle.handleSslErrorResponse(proceed); } if (!proceed) { - // Commit whatever data we have and tear down the loader. - commitLoad(); + mBrowserFrame.stopLoading(); tearDown(); } } diff --git a/core/java/android/webkit/WebResourceResponse.java b/core/java/android/webkit/WebResourceResponse.java new file mode 100644 index 0000000..e786838 --- /dev/null +++ b/core/java/android/webkit/WebResourceResponse.java @@ -0,0 +1,116 @@ +/* + * 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.webkit; + +import android.net.http.Headers; + +import java.io.InputStream; + +/** + * A WebResourceResponse is return by + * {@link WebViewClient#shouldInterceptRequest} and + * contains the response information for a particular resource. + */ +public class WebResourceResponse { + + private class Loader extends StreamLoader { + Loader(LoadListener loadListener) { + super(loadListener); + mDataStream = mInputStream; + } + @Override + protected boolean setupStreamAndSendStatus() { + mLoadListener.status(1, 1, mDataStream != null ? 200 : 404, ""); + return true; + } + @Override + protected void buildHeaders(Headers headers) { + headers.setContentType(mMimeType); + headers.setContentEncoding(mEncoding); + } + } + + // Accessed by jni, do not rename without modifying the jni code. + private String mMimeType; + private String mEncoding; + private InputStream mInputStream; + + /** + * Construct a response with the given mime type, encoding, and data. + * @param mimeType The mime type of the data (i.e. text/html). + * @param encoding The encoding of the bytes read from data. + * @param data An InputStream for reading custom data. The implementation + * must implement {@link InputStream#read(byte[])}. + */ + public WebResourceResponse(String mimeType, String encoding, + InputStream data) { + mMimeType = mimeType; + mEncoding = encoding; + mInputStream = data; + } + + /** + * Set the mime type of the response data (i.e. text/html). + * @param mimeType + */ + public void setMimeType(String mimeType) { + mMimeType = mimeType; + } + + /** + * @see #setMimeType + */ + public String getMimeType() { + return mMimeType; + } + + /** + * Set the encoding of the response data (i.e. utf-8). This will be used to + * decode the raw bytes from the input stream. + * @param encoding + */ + public void setEncoding(String encoding) { + mEncoding = encoding; + } + + /** + * @see #setEncoding + */ + public String getEncoding() { + return mEncoding; + } + + /** + * Set the input stream containing the data for this resource. + * @param data An InputStream for reading custom data. The implementation + * must implement {@link InputStream#read(byte[])}. + */ + public void setData(InputStream data) { + mInputStream = data; + } + + /** + * @see #setData + */ + public InputStream getData() { + return mInputStream; + } + + StreamLoader loader(LoadListener listener) { + return new Loader(listener); + } +} diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 14dbfe2..06800d5 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -2384,7 +2384,6 @@ public class WebView extends AbsoluteLayout Rect sendOurVisibleRect() { if (mZoomManager.isPreventingWebkitUpdates()) return mLastVisibleRectSent; - Rect rect = new Rect(); calcOurContentVisibleRect(rect); // Rect.equals() checks for null input. @@ -2937,7 +2936,8 @@ public class WebView extends AbsoluteLayout postInvalidate(); // So we draw again if (oldX != mScrollX || oldY != mScrollY) { onScrollChanged(mScrollX, mScrollY, oldX, oldY); - } else { + } else if (mScroller.getStartX() != mScrollX + || mScroller.getStartY() != mScrollY) { abortAnimation(); mPrivateHandler.removeMessages(RESUME_WEBCORE_PRIORITY); WebViewCore.resumePriority(); @@ -2970,6 +2970,7 @@ public class WebView extends AbsoluteLayout if ((dx | dy) == 0) { return false; } + abortAnimation(); if (animate) { // Log.d(LOGTAG, "startScroll: " + dx + " " + dy); mScroller.startScroll(mScrollX, mScrollY, dx, dy, @@ -2977,7 +2978,6 @@ public class WebView extends AbsoluteLayout awakenScrollBars(mScroller.getDuration()); invalidate(); } else { - abortAnimation(); // just in case scrollTo(x, y); } return true; diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index 1f8eeba..db605de 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -78,6 +78,26 @@ public class WebViewClient { } /** + * Notify the host application of a resource request and allow the + * application to return the data. If the return value is null, the WebView + * will continue to load the resource as usual. Otherwise, the return + * response and data will be used. NOTE: This method is called by the + * network thread so clients should exercise caution when accessing private + * data. + * + * @param view The {@link android.webkit.WebView} that is requesting the + * resource. + * @param url The raw url of the resource. + * @return A {@link android.webkit.WebResourceResponse} containing the + * response information or null if the WebView should load the + * resource itself. + */ + public WebResourceResponse shouldInterceptRequest(WebView view, + String url) { + return null; + } + + /** * Notify the host application that there have been an excessive number of * HTTP redirects. As the host application if it would like to continue * trying to load the resource. The default behavior is to send the cancel diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 0fcd26c..8c515db 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -283,7 +283,9 @@ final class WebViewCore { */ /* package */ static boolean isSupportedMediaMimeType(String mimeType) { int fileType = MediaFile.getFileTypeForMimeType(mimeType); - return MediaFile.isAudioFileType(fileType) || MediaFile.isVideoFileType(fileType); + return MediaFile.isAudioFileType(fileType) + || MediaFile.isVideoFileType(fileType) + || MediaFile.isPlayListFileType(fileType); } /** diff --git a/core/java/android/webkit/ZoomControlEmbedded.java b/core/java/android/webkit/ZoomControlEmbedded.java index c29e72b..e505614 100644 --- a/core/java/android/webkit/ZoomControlEmbedded.java +++ b/core/java/android/webkit/ZoomControlEmbedded.java @@ -40,13 +40,15 @@ class ZoomControlEmbedded implements ZoomControlBase { mZoomButtonsController.setVisible(true); - WebSettings settings = mWebView.getSettings(); - int count = settings.getDoubleTapToastCount(); - if (mZoomManager.isInZoomOverview() && count > 0) { - settings.setDoubleTapToastCount(--count); - Toast.makeText(mWebView.getContext(), - com.android.internal.R.string.double_tap_toast, - Toast.LENGTH_LONG).show(); + if (mZoomManager.isDoubleTapEnabled()) { + WebSettings settings = mWebView.getSettings(); + int count = settings.getDoubleTapToastCount(); + if (mZoomManager.isInZoomOverview() && count > 0) { + settings.setDoubleTapToastCount(--count); + Toast.makeText(mWebView.getContext(), + com.android.internal.R.string.double_tap_toast, + Toast.LENGTH_LONG).show(); + } } } } diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java index c788a53..69db6b2 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -510,6 +510,11 @@ class ZoomManager { } } + public boolean isDoubleTapEnabled() { + WebSettings settings = mWebView.getSettings(); + return settings != null && settings.getUseWideViewPort(); + } + /** * The double tap gesture can result in different behaviors depending on the * content that is tapped. @@ -528,7 +533,7 @@ class ZoomManager { */ public void handleDoubleTap(float lastTouchX, float lastTouchY) { WebSettings settings = mWebView.getSettings(); - if (settings == null || settings.getUseWideViewPort() == false) { + if (!isDoubleTapEnabled()) { return; } @@ -808,7 +813,9 @@ class ZoomManager { setZoomOverviewWidth(Math.min(WebView.sMaxViewportWidth, Math.max((int) (viewWidth * mInvDefaultScale), Math.max(drawData.mMinPrefWidth, drawData.mViewSize.x)))); - } else { + } else if (drawData.mContentSize.x > 0) { + // The webkitDraw for layers will not populate contentSize, and it'll be + // ignored for zoom overview width update. final int contentWidth = Math.max(drawData.mContentSize.x, drawData.mMinPrefWidth); final int newZoomOverviewWidth = Math.min(WebView.sMaxViewportWidth, contentWidth); if (newZoomOverviewWidth != mZoomOverviewWidth) { diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index ab5ff3d..30b1e5d 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -2893,8 +2893,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te void reportScrollStateChange(int newState) { if (newState != mLastScrollState) { if (mOnScrollListener != null) { - mOnScrollListener.onScrollStateChanged(this, newState); mLastScrollState = newState; + mOnScrollListener.onScrollStateChanged(this, newState); } } } @@ -3431,12 +3431,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te public void smoothScrollBy(int distance, int duration) { if (mFlingRunnable == null) { mFlingRunnable = new FlingRunnable(); - } else { - mFlingRunnable.endFling(); } // No sense starting to scroll if we're not going anywhere if (distance != 0) { + reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); mFlingRunnable.startScroll(distance, duration); + } else { + mFlingRunnable.endFling(); } } diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index bad74d4..555d993 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -34,6 +34,7 @@ import android.util.AttributeSet; import android.util.Log; import android.view.RemotableViewMethod; import android.view.View; +import android.view.ViewDebug; import android.widget.RemoteViews.RemoteView; @@ -84,7 +85,8 @@ public class ImageView extends View { private boolean mCropToPadding; - private boolean mBaselineAligned = false; + private int mBaseline = -1; + private boolean mBaselineAlignBottom = false; private static final ScaleType[] sScaleTypeArray = { ScaleType.MATRIX, @@ -118,9 +120,12 @@ public class ImageView extends View { setImageDrawable(d); } - mBaselineAligned = a.getBoolean( + mBaselineAlignBottom = a.getBoolean( com.android.internal.R.styleable.ImageView_baselineAlignBottom, false); - + + mBaseline = a.getDimensionPixelSize( + com.android.internal.R.styleable.ImageView_baseline, -1); + setAdjustViewBounds( a.getBoolean(com.android.internal.R.styleable.ImageView_adjustViewBounds, false)); @@ -180,7 +185,7 @@ public class ImageView extends View { super.invalidateDrawable(dr); } } - + @Override protected boolean onSetAlpha(int alpha) { if (getBackground() == null) { @@ -878,9 +883,63 @@ public class ImageView extends View { } } + /** + * <p>Return the offset of the widget's text baseline from the widget's top + * boundary. </p> + * + * @return the offset of the baseline within the widget's bounds or -1 + * if baseline alignment is not supported. + */ @Override + @ViewDebug.ExportedProperty(category = "layout") public int getBaseline() { - return mBaselineAligned ? getMeasuredHeight() : -1; + if (mBaselineAlignBottom) { + return getMeasuredHeight(); + } else { + return mBaseline; + } + } + + /** + * <p>Set the offset of the widget's text baseline from the widget's top + * boundary. This value is overridden by the {@link #setBaselineAlignBottom} + * property.</p> + * + * @param baseline The baseline to use, or -1 if none is to be provided. + * + * @see #setBaseline + * @attr ref android.R.styleable#ImageView_baseline + */ + public void setBaseline(int baseline) { + if (mBaseline != baseline) { + mBaseline = baseline; + requestLayout(); + } + } + + /** + * Set whether to set the baseline of this view to the bottom of the view. + * Setting this value overrides any calls to setBaseline. + * + * @param aligned If true, the image view will be baseline aligned with + * based on its bottom edge. + * + * @attr ref android.R.styleable#ImageView_baselineAlignBottom + */ + public void setBaselineAlignBottom(boolean aligned) { + if (mBaselineAlignBottom != aligned) { + mBaselineAlignBottom = aligned; + requestLayout(); + } + } + + /** + * Return whether this view's baseline will be considered the bottom of the view. + * + * @see #setBaselineAlignBottom(boolean) + */ + public boolean getBaselineAlignBottom() { + return mBaselineAlignBottom; } /** diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index b7048fc..63fb3e9 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -154,6 +154,8 @@ public class ProgressBar extends View { private boolean mInDrawing; + private int mAnimationResolution; + /** * Create a new progress bar with range 0...100 and initial progress of 0. * @param context the application environment @@ -167,12 +169,19 @@ public class ProgressBar extends View { } public ProgressBar(Context context, AttributeSet attrs, int defStyle) { + this(context, attrs, defStyle, 0); + } + + /** + * @hide + */ + public ProgressBar(Context context, AttributeSet attrs, int defStyle, int styleRes) { super(context, attrs, defStyle); mUiThreadId = Thread.currentThread().getId(); initProgressBar(); TypedArray a = - context.obtainStyledAttributes(attrs, R.styleable.ProgressBar, defStyle, 0); + context.obtainStyledAttributes(attrs, R.styleable.ProgressBar, defStyle, styleRes); mNoInvalidate = true; @@ -222,6 +231,9 @@ public class ProgressBar extends View { setIndeterminate(mOnlyIndeterminate || a.getBoolean( R.styleable.ProgressBar_indeterminate, mIndeterminate)); + mAnimationResolution = a.getInteger(R.styleable.ProgressBar_animationResolution, + ANIMATION_RESOLUTION); + a.recycle(); } @@ -852,9 +864,9 @@ public class ProgressBar extends View { } finally { mInDrawing = false; } - if (SystemClock.uptimeMillis() - mLastDrawTime >= ANIMATION_RESOLUTION) { + if (SystemClock.uptimeMillis() - mLastDrawTime >= mAnimationResolution) { mLastDrawTime = SystemClock.uptimeMillis(); - postInvalidateDelayed(ANIMATION_RESOLUTION); + postInvalidateDelayed(mAnimationResolution); } } d.draw(canvas); diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index 26eb57c..52d9c08 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -664,7 +664,7 @@ public class RemoteViewsAdapter extends BaseAdapter { itemId = factory.getItemId(position); } catch (Exception e) { // Print the error - Log.e(TAG, "Error in updateRemoteViewsInfo(" + position + "): " + + Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage()); e.printStackTrace(); @@ -673,6 +673,14 @@ public class RemoteViewsAdapter extends BaseAdapter { return; } + if (remoteViews == null) { + // If a null view was returned, we break early to prevent it from getting + // into our cache and causing problems later. The effect is that the child at this + // position will remain as a loading view until it is updated. + Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + " null RemoteViews " + + "returned from RemoteViewsFactory."); + return; + } synchronized (mCache) { // Cache the RemoteViews we loaded mCache.insert(position, remoteViews, itemId); diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java index 7c38714..432dd4a 100644 --- a/core/java/android/widget/StackView.java +++ b/core/java/android/widget/StackView.java @@ -118,6 +118,8 @@ public class StackView extends AdapterViewAnimator { private static HolographicHelper sHolographicHelper; private ImageView mHighlight; + private ImageView mClickFeedback; + private boolean mClickFeedbackIsValid = false; private StackSlider mStackSlider; private boolean mFirstLayoutHappened = false; private int mStackMode; @@ -145,6 +147,12 @@ public class StackView extends AdapterViewAnimator { mHighlight = new ImageView(getContext()); mHighlight.setLayoutParams(new LayoutParams(mHighlight)); addViewInLayout(mHighlight, -1, new LayoutParams(mHighlight)); + + mClickFeedback = new ImageView(getContext()); + mClickFeedback.setLayoutParams(new LayoutParams(mClickFeedback)); + addViewInLayout(mClickFeedback, -1, new LayoutParams(mClickFeedback)); + mClickFeedback.setVisibility(INVISIBLE); + mStackSlider = new StackSlider(); if (sHolographicHelper == null) { @@ -310,6 +318,34 @@ public class StackView extends AdapterViewAnimator { } } mTransitionIsSetup = false; + mClickFeedbackIsValid = false; + } + + void updateClickFeedback() { + if (!mClickFeedbackIsValid) { + View v = getViewAtRelativeIndex(0); + if (v != null) { + mClickFeedback.setImageBitmap(sHolographicHelper.createOutline(v, + HolographicHelper.CLICK_FEEDBACK)); + mClickFeedback.setTranslationX(v.getTranslationX()); + mClickFeedback.setTranslationY(v.getTranslationY()); + } + mClickFeedbackIsValid = true; + } + } + + @Override + void showTapFeedback(View v) { + updateClickFeedback(); + mClickFeedback.setVisibility(VISIBLE); + mClickFeedback.bringToFront(); + invalidate(); + } + + @Override + void hideTapFeedback(View v) { + mClickFeedback.setVisibility(INVISIBLE); + invalidate(); } private void updateChildTransforms() { @@ -1010,23 +1046,36 @@ public class StackView extends AdapterViewAnimator { private final Paint mHolographicPaint = new Paint(); private final Paint mErasePaint = new Paint(); private final Paint mBlurPaint = new Paint(); + private static final int RES_OUT = 0; + private static final int CLICK_FEEDBACK = 1; + private float mDensity; HolographicHelper(Context context) { initializePaints(context); } void initializePaints(Context context) { - final float density = context.getResources().getDisplayMetrics().density; + mDensity = context.getResources().getDisplayMetrics().density; - 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*density, BlurMaskFilter.Blur.NORMAL)); } Bitmap createOutline(View v) { + return createOutline(v, RES_OUT); + } + + Bitmap createOutline(View v, int type) { + if (type == RES_OUT) { + mHolographicPaint.setColor(0xff6699ff); + mBlurPaint.setMaskFilter(new BlurMaskFilter(2*mDensity, BlurMaskFilter.Blur.NORMAL)); + } else if (type == CLICK_FEEDBACK) { + mHolographicPaint.setColor(0x886699ff); + mBlurPaint.setMaskFilter(new BlurMaskFilter(4*mDensity, BlurMaskFilter.Blur.NORMAL)); + } + if (v.getMeasuredWidth() == 0 || v.getMeasuredHeight() == 0) { return null; } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 5320b10..fbc8549 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -305,6 +305,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int mTextSelectHandleRes; int mTextEditPasteWindowLayout; int mTextEditNoPasteWindowLayout; + Drawable mEditTextMultilineBackground; + Drawable mEditTextSingleLineBackground; Drawable mSelectHandleLeft; Drawable mSelectHandleRight; @@ -751,6 +753,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mTextEditNoPasteWindowLayout = a.getResourceId(attr, 0); break; + case com.android.internal.R.styleable.TextView_multilineBackground: + mEditTextMultilineBackground = a.getDrawable(attr); + break; + case com.android.internal.R.styleable.TextView_textLineHeight: int lineHeight = a.getDimensionPixelSize(attr, 0); if (lineHeight != 0) { @@ -765,6 +771,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } a.recycle(); + mEditTextSingleLineBackground = getBackground(); BufferType bufferType = BufferType.EDITABLE; final int variation = @@ -3869,7 +3876,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // - ExtractEditText does not call onFocus when it is displayed. Fixing this issue would // allow to test for hasSelection in onFocusChanged, which would trigger a // startTextSelectionMode here. TODO - if (selectionController != null && hasSelection()) { + if (this instanceof ExtractEditText && selectionController != null && hasSelection()) { startSelectionActionMode(); } @@ -5042,6 +5049,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } public void beginBatchEdit() { + mInBatchEditControllers = true; final InputMethodState ims = mInputMethodState; if (ims != null) { int nesting = ++ims.mBatchEditNesting; @@ -5064,6 +5072,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } public void endBatchEdit() { + mInBatchEditControllers = false; final InputMethodState ims = mInputMethodState; if (ims != null) { int nesting = --ims.mBatchEditNesting; @@ -6190,12 +6199,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (applyTransformation) { setTransformationMethod(SingleLineTransformationMethod.getInstance()); } + setBackgroundDrawable(mEditTextSingleLineBackground); } else { setMaxLines(Integer.MAX_VALUE); setHorizontallyScrolling(false); if (applyTransformation) { setTransformationMethod(null); } + setBackgroundDrawable(mEditTextMultilineBackground); } } @@ -6991,26 +7002,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Restore previous selection Selection.setSelection((Spannable)mText, prevStart, prevEnd); - if (mSelectionModifierCursorController != null && - !mSelectionModifierCursorController.isShowing()) { + if (hasSelectionController() && !getSelectionController().isShowing()) { // If the anchors aren't showing, revive them. - mSelectionModifierCursorController.show(); - } else { - // Tapping inside the selection displays the cut/copy/paste context menu - // as long as the anchors are already showing. - showContextMenu(); + getSelectionController().show(); } return; } else { // Tapping outside stops selection mode, if any stopSelectionActionMode(); - if (mInsertionPointCursorController != null) { - mInsertionPointCursorController.show(); + if (hasInsertionController()) { + getInsertionController().show(); } } - } else if (hasSelection() && mSelectionModifierCursorController != null) { - mSelectionModifierCursorController.show(); + } else if (hasSelection() && hasSelectionController()) { + getSelectionController().show(); } } @@ -7043,11 +7049,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public boolean onTouchEvent(MotionEvent event) { final int action = event.getActionMasked(); - if (mInsertionPointCursorController != null) { - mInsertionPointCursorController.onTouchEvent(event); + + if (hasInsertionController()) { + getInsertionController().onTouchEvent(event); } - if (mSelectionModifierCursorController != null) { - mSelectionModifierCursorController.onTouchEvent(event); + if (hasSelectionController()) { + getSelectionController().onTouchEvent(event); } if (action == MotionEvent.ACTION_DOWN) { @@ -7129,21 +7136,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener || windowParams.type > WindowManager.LayoutParams.LAST_SUB_WINDOW; } - if (windowSupportsHandles && isTextEditable() && mCursorVisible && mLayout != null && - !mTextIsSelectable) { - if (mInsertionPointCursorController == null) { - mInsertionPointCursorController = new InsertionPointCursorController(); - } - } else { - hideInsertionPointCursorController(); + // TODO Add an extra android:cursorController flag to disable the controller? + mInsertionControllerEnabled = windowSupportsHandles && isTextEditable() && mCursorVisible && + mLayout != null && !mTextIsSelectable; + mSelectionControllerEnabled = windowSupportsHandles && textCanBeSelected() && + mLayout != null; + + if (!mInsertionControllerEnabled) { mInsertionPointCursorController = null; } - if (windowSupportsHandles && textCanBeSelected() && mLayout != null) { - if (mSelectionModifierCursorController == null) { - mSelectionModifierCursorController = new SelectionModifierCursorController(); - } - } else { + if (!mSelectionControllerEnabled) { // Stop selection mode if the controller becomes unavailable. if (mSelectionModifierCursorController != null) { stopSelectionActionMode(); @@ -8348,6 +8351,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return true; } + if (isInBatchEditMode()) { + return false; + } + final int extendedPaddingTop = getExtendedPaddingTop(); final int extendedPaddingBottom = getExtendedPaddingBottom(); final int compoundPaddingLeft = getCompoundPaddingLeft(); @@ -8387,7 +8394,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mPositionY = y - TextView.this.mScrollY; if (isPositionVisible()) { int[] coords = null; - if (mContainer.isShowing()){ + if (mContainer.isShowing()) { coords = mTempCoords; TextView.this.getLocationInWindow(coords); final int containerPositionX = coords[0] + mPositionX; @@ -8626,6 +8633,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } public void show() { + if (isInBatchEditMode()) { + return; + } + mIsShowing = true; updatePosition(); mStartHandle.show(); @@ -8689,6 +8700,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } public void updatePosition() { + if (!isShowing()) { + return; + } + final int selectionStart = getSelectionStart(); final int selectionEnd = getSelectionEnd(); @@ -8913,8 +8928,62 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + /** + * @return True if this view supports insertion handles. + */ + boolean hasInsertionController() { + return mInsertionControllerEnabled; + } + + /** + * @return True if this view supports selection handles. + */ + boolean hasSelectionController() { + return mSelectionControllerEnabled; + } + + CursorController getInsertionController() { + if (!mInsertionControllerEnabled) { + return null; + } + + if (mInsertionPointCursorController == null) { + mInsertionPointCursorController = new InsertionPointCursorController(); + + final ViewTreeObserver observer = getViewTreeObserver(); + if (observer != null) { + observer.addOnTouchModeChangeListener(mInsertionPointCursorController); + } + } + + return mInsertionPointCursorController; + } + + CursorController getSelectionController() { + if (!mSelectionControllerEnabled) { + return null; + } + + if (mSelectionModifierCursorController == null) { + mSelectionModifierCursorController = new SelectionModifierCursorController(); + + final ViewTreeObserver observer = getViewTreeObserver(); + if (observer != null) { + observer.addOnTouchModeChangeListener(mSelectionModifierCursorController); + } + } + + return mSelectionModifierCursorController; + } + + boolean isInBatchEditMode() { + final InputMethodState ims = mInputMethodState; + if (ims != null) { + return ims.mBatchEditNesting > 0; + } + return mInBatchEditControllers; + } - @ViewDebug.ExportedProperty(category = "text") private CharSequence mText; private CharSequence mTransformed; private BufferType mBufferType = BufferType.NORMAL; @@ -8942,10 +9011,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private Blink mBlink; private boolean mCursorVisible = true; - // Cursor Controllers. Null when disabled. + // Cursor Controllers. private CursorController mInsertionPointCursorController; private CursorController mSelectionModifierCursorController; private ActionMode mSelectionActionMode; + private boolean mInsertionControllerEnabled; + private boolean mSelectionControllerEnabled; + private boolean mInBatchEditControllers; + // These are needed to desambiguate a long click. If the long click comes from ones of these, we // select from the current cursor position. Otherwise, select from long pressed position. private boolean mDPadCenterIsDown = false; diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index cd1cae6..86523ac 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -212,6 +212,10 @@ public class ActionBarImpl extends ActionBar { } public View getCustomNavigationView() { + return getCustomView(); + } + + public View getCustomView() { return mActionView.getCustomNavigationView(); } diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java index b0778dd..0891acc 100644 --- a/core/java/com/android/internal/util/AsyncChannel.java +++ b/core/java/com/android/internal/util/AsyncChannel.java @@ -32,51 +32,51 @@ import android.util.Slog; import java.util.Stack; /** - * An asynchronous channel between two handlers. + * <p>An asynchronous channel between two handlers.</p> * - * The handlers maybe in the same process or in another process. There + * <p>The handlers maybe in the same process or in another process. There * are two protocol styles that can be used with an AysncChannel. The * first is a simple request/reply protocol where the server does - * not need to know which client is issuing the request. + * not need to know which client is issuing the request.</p> * - * In a simple request/reply protocol the client/source sends requests to the + * <p>In a simple request/reply protocol the client/source sends requests to the * server/destination. And the server uses the replyToMessage methods. * In this usage model there is no need for the destination to - * use the connect methods. The typical sequence of operations is: - * - * 1) Client calls AsyncChannel#connect - * 2) Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel - - * 3) Client calls AsyncChannel#sendMessage(msgX) - * 4) Server receives and processes msgX - * 5) Server optionally calls AsyncChannel#replyToMessage(msgY) - * and if sent Client receives and processes msgY - * 6) Loop to step 3 until done - * - * 7) When done Client calls {@link AsyncChannel#disconnect(int)} - * 8) Client receives CMD_CHANNEL_DISCONNECTED from AsyncChannel - * - * A second usage model is where the server/destination needs to know + * use the connect methods. The typical sequence of operations is:</p> + *<ol> + * <li>Client calls AsyncChannel#connect</li> + * <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li> + * <li><code>comm-loop:</code></li> + * <li>Client calls AsyncChannel#sendMessage(msgX)</li> + * <li>Server receives and processes msgX</li> + * <li>Server optionally calls AsyncChannel#replyToMessage(msgY) + * and if sent Client receives and processes msgY</li> + * <li>Loop to <code>comm-loop</code> until done</li> + * <li>When done Client calls {@link AsyncChannel#disconnect(int)}</li> + * <li>Client receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li> + *</ol> + *<br/> + * <p>A second usage model is where the server/destination needs to know * which client it's connected too. For example the server needs to * send unsolicited messages back to the client. Or the server keeps * different state for each client. In this model the server will also - * use the connect methods. The typical sequence of operation is: - * - * 1) Client calls AsyncChannel#connect - * 2) Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel - * 3) Client calls AsyncChannel#sendMessage(CMD_CHANNEL_FULL_CONNECTION) - * 4) Server receives CMD_CHANNEL_FULL_CONNECTION - * 5) Server calls AsyncChannel#connect - * 6) Server receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel - * 7) Server sends AsyncChannel#sendMessage(CMD_CHANNEL_FULLY_CONNECTED) - * 8) Client receives CMD_CHANNEL_FULLY_CONNECTED - * - * 9) Client/Server uses AsyncChannel#sendMessage/replyToMessage - * to communicate and perform work - * 10) Loop to step 9 until done - * - * 11) When done Client/Server calls {@link AsyncChannel#disconnect(int)} - * 12) Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel + * use the connect methods. The typical sequence of operation is:</p> + *<ol> + * <li>Client calls AsyncChannel#connect</li> + * <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li> + * <li>Client calls AsyncChannel#sendMessage(CMD_CHANNEL_FULL_CONNECTION)</li> + * <li>Server receives CMD_CHANNEL_FULL_CONNECTION</li> + * <li>Server calls AsyncChannel#connect</li> + * <li>Server receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li> + * <li>Server sends AsyncChannel#sendMessage(CMD_CHANNEL_FULLY_CONNECTED)</li> + * <li>Client receives CMD_CHANNEL_FULLY_CONNECTED</li> + * <li><code>comm-loop:</code></li> + * <li>Client/Server uses AsyncChannel#sendMessage/replyToMessage + * to communicate and perform work</li> + * <li>Loop to <code>comm-loop</code> until done</li> + * <li>When done Client/Server calls {@link AsyncChannel#disconnect(int)}</li> + * <li>Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li> + *</ol> */ public class AsyncChannel { /** Log tag */ diff --git a/core/java/com/android/internal/util/HierarchicalStateMachine.java b/core/java/com/android/internal/util/HierarchicalStateMachine.java index 7920b72..f43f459 100644 --- a/core/java/com/android/internal/util/HierarchicalStateMachine.java +++ b/core/java/com/android/internal/util/HierarchicalStateMachine.java @@ -28,64 +28,66 @@ import java.util.HashMap; /** * {@hide} * - * A hierarchical state machine is a state machine which processes messages - * and can have states arranged hierarchically. A state is a <code>HierarchicalState</code> - * object and must implement <code>processMessage</code> and optionally <code>enter/exit/getName</code>. + * <p>A hierarchical state machine is a state machine which processes messages + * and can have states arranged hierarchically.</p> + * + * <p>A state is a <code>HierarchicalState</code> object and must implement + * <code>processMessage</code> and optionally <code>enter/exit/getName</code>. * The enter/exit methods are equivalent to the construction and destruction * in Object Oriented programming and are used to perform initialization and * cleanup of the state respectively. The <code>getName</code> method returns the * name of the state the default implementation returns the class name it may be * desirable to have this return the name of the state instance name instead. - * In particular if a particular state class has multiple instances. + * In particular if a particular state class has multiple instances.</p> * - * When a state machine is created <code>addState</code> is used to build the + * <p>When a state machine is created <code>addState</code> is used to build the * hierarchy and <code>setInitialState</code> is used to identify which of these * is the initial state. After construction the programmer calls <code>start</code> * which initializes the state machine and calls <code>enter</code> for all of the initial * state's hierarchy, starting at its eldest parent. For example given the simple * state machine below after start is called mP1.enter will have been called and - * then mS1.enter. + * then mS1.enter.</p> <code> mP1 / \ mS2 mS1 ----> initial state </code> - * After the state machine is created and started, messages are sent to a state + * <p>After the state machine is created and started, messages are sent to a state * machine using <code>sendMessage</code> and the messages are created using * <code>obtainMessage</code>. When the state machine receives a message the * current state's <code>processMessage</code> is invoked. In the above example * mS1.processMessage will be invoked first. The state may use <code>transitionTo</code> - * to change the current state to a new state + * to change the current state to a new state</p> * - * Each state in the state machine may have a zero or one parent states and if + * <p>Each state in the state machine may have a zero or one parent states and if * a child state is unable to handle a message it may have the message processed * by its parent by returning false or NOT_HANDLED. If a message is never processed * <code>unhandledMessage</code> will be invoked to give one last chance for the state machine - * to process the message. + * to process the message.</p> * - * When all processing is completed a state machine may choose to call + * <p>When all processing is completed a state machine may choose to call * <code>transitionToHaltingState</code>. When the current <code>processingMessage</code> * returns the state machine will transfer to an internal <code>HaltingState</code> * and invoke <code>halting</code>. Any message subsequently received by the state - * machine will cause <code>haltedProcessMessage</code> to be invoked. + * machine will cause <code>haltedProcessMessage</code> to be invoked.</p> * - * If it is desirable to completely stop the state machine call <code>quit</code>. This + * <p>If it is desirable to completely stop the state machine call <code>quit</code>. This * will exit the current state and its parent and then exit from the controlling thread - * and no further messages will be processed. + * and no further messages will be processed.</p> * - * In addition to <code>processMessage</code> each <code>HierarchicalState</code> has - * an <code>enter</code> method and <code>exit</exit> method which may be overridden. + * <p>In addition to <code>processMessage</code> each <code>HierarchicalState</code> has + * an <code>enter</code> method and <code>exit</exit> method which may be overridden.</p> * - * Since the states are arranged in a hierarchy transitioning to a new state + * <p>Since the states are arranged in a hierarchy transitioning to a new state * causes current states to be exited and new states to be entered. To determine * the list of states to be entered/exited the common parent closest to * the current state is found. We then exit from the current state and its * parent's up to but not including the common parent state and then enter all * of the new states below the common parent down to the destination state. * If there is no common parent all states are exited and then the new states - * are entered. + * are entered.</p> * - * Two other methods that states can use are <code>deferMessage</code> and + * <p>Two other methods that states can use are <code>deferMessage</code> and * <code>sendMessageAtFrontOfQueue</code>. The <code>sendMessageAtFrontOfQueue</code> sends * a message but places it on the front of the queue rather than the back. The * <code>deferMessage</code> causes the message to be saved on a list until a @@ -93,10 +95,10 @@ import java.util.HashMap; * will be put on the front of the state machine queue with the oldest message * at the front. These will then be processed by the new current state before * any other messages that are on the queue or might be added later. Both of - * these are protected and may only be invoked from within a state machine. + * these are protected and may only be invoked from within a state machine.</p> * - * To illustrate some of these properties we'll use state machine with an 8 - * state hierarchy: + * <p>To illustrate some of these properties we'll use state machine with an 8 + * state hierarchy:</p> <code> mP0 / \ @@ -106,22 +108,21 @@ import java.util.HashMap; / \ \ mS3 mS4 mS5 ---> initial state </code> - * - * After starting mS5 the list of active states is mP0, mP1, mS1 and mS5. + * <p>After starting mS5 the list of active states is mP0, mP1, mS1 and mS5. * So the order of calling processMessage when a message is received is mS5, * mS1, mP1, mP0 assuming each processMessage indicates it can't handle this - * message by returning false or NOT_HANDLED. + * message by returning false or NOT_HANDLED.</p> * - * Now assume mS5.processMessage receives a message it can handle, and during + * <p>Now assume mS5.processMessage receives a message it can handle, and during * the handling determines the machine should change states. It could call * transitionTo(mS4) and return true or HANDLED. Immediately after returning from * processMessage the state machine runtime will find the common parent, * which is mP1. It will then call mS5.exit, mS1.exit, mS2.enter and then * mS4.enter. The new list of active states is mP0, mP1, mS2 and mS4. So - * when the next message is received mS4.processMessage will be invoked. + * when the next message is received mS4.processMessage will be invoked.</p> * - * Now for some concrete examples, here is the canonical HelloWorld as an HSM. - * It responds with "Hello World" being printed to the log for every message. + * <p>Now for some concrete examples, here is the canonical HelloWorld as an HSM. + * It responds with "Hello World" being printed to the log for every message.</p> <code> class HelloWorld extends HierarchicalStateMachine { Hsm1(String name) { @@ -150,79 +151,76 @@ void testHelloWorld() { hw.sendMessage(hw.obtainMessage()); } </code> - * - * A more interesting state machine is one with four states - * with two independent parent states. + * <p>A more interesting state machine is one with four states + * with two independent parent states.</p> <code> mP1 mP2 / \ mS2 mS1 </code> - * - * Here is a description of this state machine using pseudo code. - * - * - * state mP1 { - * enter { log("mP1.enter"); } - * exit { log("mP1.exit"); } - * on msg { - * CMD_2 { - * send(CMD_3); - * defer(msg); - * transitonTo(mS2); - * return HANDLED; - * } - * return NOT_HANDLED; - * } - * } - * - * INITIAL - * state mS1 parent mP1 { - * enter { log("mS1.enter"); } - * exit { log("mS1.exit"); } - * on msg { - * CMD_1 { - * transitionTo(mS1); - * return HANDLED; - * } - * return NOT_HANDLED; - * } - * } - * - * state mS2 parent mP1 { - * enter { log("mS2.enter"); } - * exit { log("mS2.exit"); } - * on msg { - * CMD_2 { - * send(CMD_4); - * return HANDLED; - * } - * CMD_3 { - * defer(msg); - * transitionTo(mP2); - * return HANDLED; - * } - * return NOT_HANDLED; - * } - * } - * - * state mP2 { - * enter { - * log("mP2.enter"); - * send(CMD_5); - * } - * exit { log("mP2.exit"); } - * on msg { - * CMD_3, CMD_4 { return HANDLED; } - * CMD_5 { - * transitionTo(HaltingState); - * return HANDLED; - * } - * return NOT_HANDLED; - * } - * } - * - * The implementation is below and also in HierarchicalStateMachineTest: + * <p>Here is a description of this state machine using pseudo code.</p> + <code> +state mP1 { + enter { log("mP1.enter"); } + exit { log("mP1.exit"); } + on msg { + CMD_2 { + send(CMD_3); + defer(msg); + transitonTo(mS2); + return HANDLED; + } + return NOT_HANDLED; + } +} + +INITIAL +state mS1 parent mP1 { + enter { log("mS1.enter"); } + exit { log("mS1.exit"); } + on msg { + CMD_1 { + transitionTo(mS1); + return HANDLED; + } + return NOT_HANDLED; + } +} + +state mS2 parent mP1 { + enter { log("mS2.enter"); } + exit { log("mS2.exit"); } + on msg { + CMD_2 { + send(CMD_4); + return HANDLED; + } + CMD_3 { + defer(msg); + transitionTo(mP2); + return HANDLED; + } + return NOT_HANDLED; + } +} + +state mP2 { + enter { + log("mP2.enter"); + send(CMD_5); + } + exit { log("mP2.exit"); } + on msg { + CMD_3, CMD_4 { return HANDLED; } + CMD_5 { + transitionTo(HaltingState); + return HANDLED; + } + return NOT_HANDLED; + } +} +</code> + * <p>The implementation is below and also in HierarchicalStateMachineTest:</p> <code> class Hsm1 extends HierarchicalStateMachine { private static final String TAG = "hsm1"; @@ -368,49 +366,47 @@ class Hsm1 extends HierarchicalStateMachine { P2 mP2 = new P2(); } </code> - * - * If this is executed by sending two messages CMD_1 and CMD_2 - * (Note the synchronize is only needed because we use hsm.wait()) - * - * Hsm1 hsm = makeHsm1(); - * synchronize(hsm) { - * hsm.sendMessage(obtainMessage(hsm.CMD_1)); - * hsm.sendMessage(obtainMessage(hsm.CMD_2)); - * try { - * // wait for the messages to be handled - * hsm.wait(); - * } catch (InterruptedException e) { - * Log.e(TAG, "exception while waiting " + e.getMessage()); - * } - * } - * - * - * The output is: - * - * D/hsm1 ( 1999): makeHsm1 E - * D/hsm1 ( 1999): ctor E - * D/hsm1 ( 1999): ctor X - * D/hsm1 ( 1999): mP1.enter - * D/hsm1 ( 1999): mS1.enter - * D/hsm1 ( 1999): makeHsm1 X - * D/hsm1 ( 1999): mS1.processMessage what=1 - * D/hsm1 ( 1999): mS1.exit - * D/hsm1 ( 1999): mS1.enter - * D/hsm1 ( 1999): mS1.processMessage what=2 - * D/hsm1 ( 1999): mP1.processMessage what=2 - * D/hsm1 ( 1999): mS1.exit - * D/hsm1 ( 1999): mS2.enter - * D/hsm1 ( 1999): mS2.processMessage what=2 - * D/hsm1 ( 1999): mS2.processMessage what=3 - * D/hsm1 ( 1999): mS2.exit - * D/hsm1 ( 1999): mP1.exit - * D/hsm1 ( 1999): mP2.enter - * D/hsm1 ( 1999): mP2.processMessage what=3 - * D/hsm1 ( 1999): mP2.processMessage what=4 - * D/hsm1 ( 1999): mP2.processMessage what=5 - * D/hsm1 ( 1999): mP2.exit - * D/hsm1 ( 1999): halting - * + * <p>If this is executed by sending two messages CMD_1 and CMD_2 + * (Note the synchronize is only needed because we use hsm.wait())</p> +<code> +Hsm1 hsm = makeHsm1(); +synchronize(hsm) { + hsm.sendMessage(obtainMessage(hsm.CMD_1)); + hsm.sendMessage(obtainMessage(hsm.CMD_2)); + try { + // wait for the messages to be handled + hsm.wait(); + } catch (InterruptedException e) { + Log.e(TAG, "exception while waiting " + e.getMessage()); + } +} +</code> + * <p>The output is:</p> +<code> +D/hsm1 ( 1999): makeHsm1 E +D/hsm1 ( 1999): ctor E +D/hsm1 ( 1999): ctor X +D/hsm1 ( 1999): mP1.enter +D/hsm1 ( 1999): mS1.enter +D/hsm1 ( 1999): makeHsm1 X +D/hsm1 ( 1999): mS1.processMessage what=1 +D/hsm1 ( 1999): mS1.exit +D/hsm1 ( 1999): mS1.enter +D/hsm1 ( 1999): mS1.processMessage what=2 +D/hsm1 ( 1999): mP1.processMessage what=2 +D/hsm1 ( 1999): mS1.exit +D/hsm1 ( 1999): mS2.enter +D/hsm1 ( 1999): mS2.processMessage what=2 +D/hsm1 ( 1999): mS2.processMessage what=3 +D/hsm1 ( 1999): mS2.exit +D/hsm1 ( 1999): mP1.exit +D/hsm1 ( 1999): mP2.enter +D/hsm1 ( 1999): mP2.processMessage what=3 +D/hsm1 ( 1999): mP2.processMessage what=4 +D/hsm1 ( 1999): mP2.processMessage what=5 +D/hsm1 ( 1999): mP2.exit +D/hsm1 ( 1999): halting +</code> */ public class HierarchicalStateMachine { diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 7d8e624..7592e8b 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -57,7 +57,7 @@ interface IInputMethodManager { void updateStatusIcon(in IBinder token, String packageName, int iconId); void setIMEButtonVisible(in IBinder token, boolean visible); InputMethodSubtype getCurrentInputMethodSubtype(); + boolean setCurrentInputMethodSubtype(in InputMethodSubtype subtype); boolean switchToLastInputMethod(in IBinder token); - boolean setInputMethodEnabled(String id, boolean enabled); } diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index 1d103ed..1406e4e 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -110,8 +110,10 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On if (isShowing()) { mPopup.dismiss(); } - mTreeObserver.removeGlobalOnLayoutListener(this); - mTreeObserver = null; + if (mTreeObserver != null) { + mTreeObserver.removeGlobalOnLayoutListener(this); + mTreeObserver = null; + } } public boolean isShowing() { diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 7a64da0..e18f58f 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -26,7 +26,6 @@ import android.app.ActionBar.NavigationCallback; import android.app.Activity; import android.content.Context; import android.content.pm.ApplicationInfo; -import android.content.pm.ComponentInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.TypedArray; @@ -41,10 +40,12 @@ import android.view.Menu; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; +import android.view.Window; import android.widget.AdapterView; import android.widget.HorizontalScrollView; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.ProgressBar; import android.widget.Spinner; import android.widget.SpinnerAdapter; import android.widget.TextView; @@ -94,9 +95,15 @@ public class ActionBarView extends ViewGroup { private HorizontalScrollView mTabScrollView; private LinearLayout mTabLayout; private View mCustomNavView; + private ProgressBar mProgressView; + private ProgressBar mIndeterminateProgressView; + + private int mProgressBarPadding; private int mTitleStyleRes; private int mSubtitleStyleRes; + private int mProgressStyle; + private int mIndeterminateProgressStyle; private boolean mShowMenu; private boolean mUserTitle; @@ -185,6 +192,11 @@ public class ActionBarView extends ViewGroup { mTitleStyleRes = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0); mSubtitleStyleRes = a.getResourceId(R.styleable.ActionBar_subtitleTextStyle, 0); + mProgressStyle = a.getResourceId(R.styleable.ActionBar_progressBarStyle, 0); + mIndeterminateProgressStyle = a.getResourceId( + R.styleable.ActionBar_indeterminateProgressStyle, 0); + + mProgressBarPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_progressBarPadding, 0); setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT)); @@ -208,7 +220,7 @@ public class ActionBarView extends ViewGroup { Context context = getContext(); if (context instanceof Activity) { Activity activity = (Activity) context; - activity.onOptionsItemSelected(mLogoNavItem); + activity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem); } } }); @@ -216,6 +228,20 @@ public class ActionBarView extends ViewGroup { mHomeLayout.setFocusable(true); } + public void initProgress() { + mProgressView = new ProgressBar(mContext, null, 0, mProgressStyle); + mProgressView.setId(R.id.progress_horizontal); + mProgressView.setMax(10000); + addView(mProgressView); + } + + public void initIndeterminateProgress() { + mIndeterminateProgressView = new ProgressBar(mContext, null, 0, + mIndeterminateProgressStyle); + mIndeterminateProgressView.setId(R.id.progress_circular); + addView(mIndeterminateProgressView); + } + @Override public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) { // No starting an action mode for an action bar child! (Where would it go?) @@ -665,6 +691,13 @@ public class ActionBarView extends ViewGroup { break; } + if (mIndeterminateProgressView != null && + mIndeterminateProgressView.getVisibility() != GONE) { + availableWidth = measureChildView(mIndeterminateProgressView, availableWidth, + childSpecHeight, 0); + rightOfCenter -= mIndeterminateProgressView.getMeasuredWidth(); + } + if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) { final LayoutParams lp = generateLayoutParams(mCustomNavView.getLayoutParams()); final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ? @@ -726,6 +759,12 @@ public class ActionBarView extends ViewGroup { if (mContextView != null) { mContextView.setHeight(getMeasuredHeight()); } + + if (mProgressView != null && mProgressView.getVisibility() != GONE) { + mProgressView.measure(MeasureSpec.makeMeasureSpec( + contentWidth - mProgressBarPadding * 2, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST)); + } } private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) { @@ -764,6 +803,7 @@ public class ActionBarView extends ViewGroup { if (mTabScrollView != null) { x += positionChild(mTabScrollView, x, y, contentHeight); } + break; } int menuLeft = r - l - getPaddingRight(); @@ -772,6 +812,12 @@ public class ActionBarView extends ViewGroup { menuLeft -= mMenuView.getMeasuredWidth(); } + if (mIndeterminateProgressView != null && + mIndeterminateProgressView.getVisibility() != GONE) { + positionChildInverse(mIndeterminateProgressView, menuLeft, y, contentHeight); + menuLeft -= mIndeterminateProgressView.getMeasuredWidth(); + } + if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { LayoutParams lp = mCustomNavView.getLayoutParams(); final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ? @@ -830,6 +876,13 @@ public class ActionBarView extends ViewGroup { } x += positionChild(mCustomNavView, xpos, ypos, contentHeight); } + + if (mProgressView != null) { + mProgressView.bringToFront(); + final int halfProgressHeight = mProgressView.getMeasuredHeight() / 2; + mProgressView.layout(mProgressBarPadding, -halfProgressHeight, + mProgressBarPadding + mProgressView.getMeasuredWidth(), halfProgressHeight); + } } private int positionChild(View child, int x, int y, int contentHeight) { diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 1018ddb..e50233e 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -1194,6 +1194,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_text_AndroidBidi), REG_JNI(register_android_text_KeyCharacterMap), REG_JNI(register_android_os_Process), + REG_JNI(register_android_os_SystemProperties), REG_JNI(register_android_os_Binder), REG_JNI(register_android_view_Display), REG_JNI(register_android_nio_utils), @@ -1251,7 +1252,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_os_ParcelFileDescriptor), REG_JNI(register_android_os_Power), REG_JNI(register_android_os_StatFs), - REG_JNI(register_android_os_SystemProperties), REG_JNI(register_android_os_UEventObserver), REG_JNI(register_android_net_LocalSocketImpl), REG_JNI(register_android_net_NetworkUtils), diff --git a/core/jni/android_emoji_EmojiFactory.cpp b/core/jni/android_emoji_EmojiFactory.cpp index 63550fb..f653b36 100644 --- a/core/jni/android_emoji_EmojiFactory.cpp +++ b/core/jni/android_emoji_EmojiFactory.cpp @@ -4,6 +4,7 @@ #define LOG_TAG "EmojiFactory_jni" #include <utils/Log.h> #include <utils/String8.h> +#include <utils/String16.h> #include "EmojiFactory.h" #include <nativehelper/JNIHelp.h> diff --git a/core/res/res/drawable-hdpi/progress_bg_holo_dark.9.png b/core/res/res/drawable-hdpi/progress_bg_holo_dark.9.png Binary files differindex 192df6d..079de61 100644 --- a/core/res/res/drawable-hdpi/progress_bg_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/progress_bg_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/progress_bg_holo_light.9.png b/core/res/res/drawable-hdpi/progress_bg_holo_light.9.png Binary files differindex 223416d..2272b41 100644 --- a/core/res/res/drawable-hdpi/progress_bg_holo_light.9.png +++ b/core/res/res/drawable-hdpi/progress_bg_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/progress_primary_holo_dark.9.png b/core/res/res/drawable-hdpi/progress_primary_holo_dark.9.png Binary files differindex 1a59124..16b7349 100644 --- a/core/res/res/drawable-hdpi/progress_primary_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/progress_primary_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/progress_primary_holo_light.9.png b/core/res/res/drawable-hdpi/progress_primary_holo_light.9.png Binary files differindex f119c6a..7229343 100644 --- a/core/res/res/drawable-hdpi/progress_primary_holo_light.9.png +++ b/core/res/res/drawable-hdpi/progress_primary_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/progress_secondary_holo_dark.9.png b/core/res/res/drawable-hdpi/progress_secondary_holo_dark.9.png Binary files differindex 3c6c5ed..894dfcf 100644 --- a/core/res/res/drawable-hdpi/progress_secondary_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/progress_secondary_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/progress_secondary_holo_light.9.png b/core/res/res/drawable-hdpi/progress_secondary_holo_light.9.png Binary files differindex cab3888..f553ba6 100644 --- a/core/res/res/drawable-hdpi/progress_secondary_holo_light.9.png +++ b/core/res/res/drawable-hdpi/progress_secondary_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/spinner_16_inner_holo.png b/core/res/res/drawable-hdpi/spinner_16_inner_holo.png Binary files differnew file mode 100644 index 0000000..ff363f0 --- /dev/null +++ b/core/res/res/drawable-hdpi/spinner_16_inner_holo.png diff --git a/core/res/res/drawable-hdpi/spinner_16_outer_holo.png b/core/res/res/drawable-hdpi/spinner_16_outer_holo.png Binary files differnew file mode 100644 index 0000000..a5a0b98 --- /dev/null +++ b/core/res/res/drawable-hdpi/spinner_16_outer_holo.png diff --git a/core/res/res/drawable-hdpi/spinner_20_inner_holo.png b/core/res/res/drawable-hdpi/spinner_20_inner_holo.png Binary files differnew file mode 100644 index 0000000..16742e8 --- /dev/null +++ b/core/res/res/drawable-hdpi/spinner_20_inner_holo.png diff --git a/core/res/res/drawable-hdpi/spinner_20_outer_holo.png b/core/res/res/drawable-hdpi/spinner_20_outer_holo.png Binary files differnew file mode 100644 index 0000000..d6aa73b --- /dev/null +++ b/core/res/res/drawable-hdpi/spinner_20_outer_holo.png diff --git a/core/res/res/drawable-hdpi/spinner_48_inner_holo.png b/core/res/res/drawable-hdpi/spinner_48_inner_holo.png Binary files differnew file mode 100644 index 0000000..ee3f4c2 --- /dev/null +++ b/core/res/res/drawable-hdpi/spinner_48_inner_holo.png diff --git a/core/res/res/drawable-hdpi/spinner_48_outer_holo.png b/core/res/res/drawable-hdpi/spinner_48_outer_holo.png Binary files differnew file mode 100644 index 0000000..7c59e2f --- /dev/null +++ b/core/res/res/drawable-hdpi/spinner_48_outer_holo.png diff --git a/core/res/res/drawable-hdpi/spinner_76_inner_holo.png b/core/res/res/drawable-hdpi/spinner_76_inner_holo.png Binary files differnew file mode 100644 index 0000000..a1ef44c --- /dev/null +++ b/core/res/res/drawable-hdpi/spinner_76_inner_holo.png diff --git a/core/res/res/drawable-hdpi/spinner_76_outer_holo.png b/core/res/res/drawable-hdpi/spinner_76_outer_holo.png Binary files differnew file mode 100644 index 0000000..69e3ab7 --- /dev/null +++ b/core/res/res/drawable-hdpi/spinner_76_outer_holo.png diff --git a/core/res/res/drawable-hdpi/textfield_active_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_active_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..a38c03a --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_active_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/textfield_active_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_active_holo_light.9.png Binary files differnew file mode 100644 index 0000000..6a88a69 --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_active_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png Binary files differindex 7ec2192..87d9c21 100644 --- a/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png Binary files differindex c03e4f6..720ee78 100644 --- a/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png +++ b/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..4275da0 --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png Binary files differnew file mode 100644 index 0000000..3ec9c1f --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png Binary files differindex 6642717..227bde2 100644 --- a/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png Binary files differindex 9572752..6ddfab0 100644 --- a/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png +++ b/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_dark.9.png Binary files differdeleted file mode 100644 index 0ad248c..0000000 --- a/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_dark.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_light.9.png Binary files differdeleted file mode 100644 index b7a07c4..0000000 --- a/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_light.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/textfield_multiline_active_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_active_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..7528479 --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_multiline_active_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/textfield_multiline_active_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_active_holo_light.9.png Binary files differnew file mode 100644 index 0000000..4c7d9e7 --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_multiline_active_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/textfield_multiline_default_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_default_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..09ca253 --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_multiline_default_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/textfield_multiline_default_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_default_holo_light.9.png Binary files differnew file mode 100644 index 0000000..0a7d3a1 --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_multiline_default_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..54a1519 --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_light.9.png Binary files differnew file mode 100644 index 0000000..06ca0d4 --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..9015299 --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_light.9.png Binary files differnew file mode 100644 index 0000000..b355cb3 --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/textfield_pressed.9.png b/core/res/res/drawable-hdpi/textfield_pressed.9.png Binary files differdeleted file mode 100644 index a42d87f..0000000 --- a/core/res/res/drawable-hdpi/textfield_pressed.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/textfield_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_pressed_holo_dark.9.png Binary files differdeleted file mode 100644 index a271ac9..0000000 --- a/core/res/res/drawable-hdpi/textfield_pressed_holo_dark.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/textfield_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_pressed_holo_light.9.png Binary files differdeleted file mode 100644 index 521722d..0000000 --- a/core/res/res/drawable-hdpi/textfield_pressed_holo_light.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/textfield_selected_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_selected_holo_dark.9.png Binary files differdeleted file mode 100644 index a271ac9..0000000 --- a/core/res/res/drawable-hdpi/textfield_selected_holo_dark.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/textfield_selected_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_selected_holo_light.9.png Binary files differdeleted file mode 100644 index 521722d..0000000 --- a/core/res/res/drawable-hdpi/textfield_selected_holo_light.9.png +++ /dev/null diff --git a/core/res/res/drawable-ldpi/textfield_pressed.9.png b/core/res/res/drawable-ldpi/textfield_pressed.9.png Binary files differdeleted file mode 100644 index 1433365..0000000 --- a/core/res/res/drawable-ldpi/textfield_pressed.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/progress_bg_holo_dark.9.png b/core/res/res/drawable-mdpi/progress_bg_holo_dark.9.png Binary files differindex 316af64..0376e53 100644 --- a/core/res/res/drawable-mdpi/progress_bg_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/progress_bg_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/progress_bg_holo_light.9.png b/core/res/res/drawable-mdpi/progress_bg_holo_light.9.png Binary files differindex e286136..e825119 100644 --- a/core/res/res/drawable-mdpi/progress_bg_holo_light.9.png +++ b/core/res/res/drawable-mdpi/progress_bg_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/progress_primary_holo_dark.9.png b/core/res/res/drawable-mdpi/progress_primary_holo_dark.9.png Binary files differindex 0502669..e525eaf 100644 --- a/core/res/res/drawable-mdpi/progress_primary_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/progress_primary_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/progress_primary_holo_light.9.png b/core/res/res/drawable-mdpi/progress_primary_holo_light.9.png Binary files differindex 1ba9e34..0532f0e 100644 --- a/core/res/res/drawable-mdpi/progress_primary_holo_light.9.png +++ b/core/res/res/drawable-mdpi/progress_primary_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/progress_secondary_holo_dark.9.png b/core/res/res/drawable-mdpi/progress_secondary_holo_dark.9.png Binary files differindex a2fe2b3..99f8f06 100644 --- a/core/res/res/drawable-mdpi/progress_secondary_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/progress_secondary_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/progress_secondary_holo_light.9.png b/core/res/res/drawable-mdpi/progress_secondary_holo_light.9.png Binary files differindex 3b264ab..198d7d9 100644 --- a/core/res/res/drawable-mdpi/progress_secondary_holo_light.9.png +++ b/core/res/res/drawable-mdpi/progress_secondary_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/spinner_16_inner_holo.png b/core/res/res/drawable-mdpi/spinner_16_inner_holo.png Binary files differnew file mode 100644 index 0000000..2703553 --- /dev/null +++ b/core/res/res/drawable-mdpi/spinner_16_inner_holo.png diff --git a/core/res/res/drawable-mdpi/spinner_16_outer_holo.png b/core/res/res/drawable-mdpi/spinner_16_outer_holo.png Binary files differnew file mode 100644 index 0000000..87d2c87 --- /dev/null +++ b/core/res/res/drawable-mdpi/spinner_16_outer_holo.png diff --git a/core/res/res/drawable-mdpi/spinner_20_inner_holo.png b/core/res/res/drawable-mdpi/spinner_20_inner_holo.png Binary files differnew file mode 100644 index 0000000..3c97355 --- /dev/null +++ b/core/res/res/drawable-mdpi/spinner_20_inner_holo.png diff --git a/core/res/res/drawable-mdpi/spinner_20_outer_holo.png b/core/res/res/drawable-mdpi/spinner_20_outer_holo.png Binary files differnew file mode 100644 index 0000000..96155e3 --- /dev/null +++ b/core/res/res/drawable-mdpi/spinner_20_outer_holo.png diff --git a/core/res/res/drawable-mdpi/spinner_48_inner_holo.png b/core/res/res/drawable-mdpi/spinner_48_inner_holo.png Binary files differnew file mode 100644 index 0000000..26dcfbf --- /dev/null +++ b/core/res/res/drawable-mdpi/spinner_48_inner_holo.png diff --git a/core/res/res/drawable-mdpi/spinner_48_outer_holo.png b/core/res/res/drawable-mdpi/spinner_48_outer_holo.png Binary files differnew file mode 100644 index 0000000..49dad0c --- /dev/null +++ b/core/res/res/drawable-mdpi/spinner_48_outer_holo.png diff --git a/core/res/res/drawable-mdpi/spinner_76_inner_holo.png b/core/res/res/drawable-mdpi/spinner_76_inner_holo.png Binary files differnew file mode 100644 index 0000000..ebccabd --- /dev/null +++ b/core/res/res/drawable-mdpi/spinner_76_inner_holo.png diff --git a/core/res/res/drawable-mdpi/spinner_76_outer_holo.png b/core/res/res/drawable-mdpi/spinner_76_outer_holo.png Binary files differnew file mode 100644 index 0000000..37d3f58 --- /dev/null +++ b/core/res/res/drawable-mdpi/spinner_76_outer_holo.png diff --git a/core/res/res/drawable-mdpi/textfield_active_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_active_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..d37c8b2 --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_active_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/textfield_active_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_active_holo_light.9.png Binary files differnew file mode 100644 index 0000000..16f2197 --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_active_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png Binary files differindex 3a5f36d..c98c951 100644 --- a/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png Binary files differindex b8cc76f..7691f81 100644 --- a/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png +++ b/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/textfield_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_disabled_focused_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..500ede3 --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_disabled_focused_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/textfield_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_disabled_focused_holo_light.9.png Binary files differnew file mode 100644 index 0000000..99f7f38 --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_disabled_focused_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png Binary files differindex a1f0c71..fab86ac 100644 --- a/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png Binary files differindex 71e3103..876eb794 100644 --- a/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png +++ b/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_dark.9.png Binary files differdeleted file mode 100644 index ac6d406..0000000 --- a/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_dark.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_light.9.png Binary files differdeleted file mode 100644 index bb6e953..0000000 --- a/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_light.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/textfield_multiline_active_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_active_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..2646899 --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_multiline_active_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/textfield_multiline_active_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_active_holo_light.9.png Binary files differnew file mode 100644 index 0000000..374d457 --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_multiline_active_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/textfield_multiline_default_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_default_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..65c87ba --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_multiline_default_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/textfield_multiline_default_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_default_holo_light.9.png Binary files differnew file mode 100644 index 0000000..724b3fd --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_multiline_default_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/textfield_multiline_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_disabled_focused_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..5f0ad56 --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_multiline_disabled_focused_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/textfield_multiline_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_disabled_focused_holo_light.9.png Binary files differnew file mode 100644 index 0000000..df03a15 --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_multiline_disabled_focused_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..2cc7f62 --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_light.9.png Binary files differnew file mode 100644 index 0000000..a2d9d8a --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/textfield_pressed.9.png b/core/res/res/drawable-mdpi/textfield_pressed.9.png Binary files differdeleted file mode 100644 index c909ad2..0000000 --- a/core/res/res/drawable-mdpi/textfield_pressed.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/textfield_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_pressed_holo_dark.9.png Binary files differdeleted file mode 100644 index 7667d95..0000000 --- a/core/res/res/drawable-mdpi/textfield_pressed_holo_dark.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/textfield_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_pressed_holo_light.9.png Binary files differdeleted file mode 100644 index 269affd..0000000 --- a/core/res/res/drawable-mdpi/textfield_pressed_holo_light.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/textfield_selected_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_selected_holo_dark.9.png Binary files differdeleted file mode 100644 index 7667d95..0000000 --- a/core/res/res/drawable-mdpi/textfield_selected_holo_dark.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/textfield_selected_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_selected_holo_light.9.png Binary files differdeleted file mode 100644 index 269affd..0000000 --- a/core/res/res/drawable-mdpi/textfield_selected_holo_light.9.png +++ /dev/null diff --git a/core/res/res/drawable/edit_text.xml b/core/res/res/drawable/edit_text.xml index 315278d..e9ba84b 100644 --- a/core/res/res/drawable/edit_text.xml +++ b/core/res/res/drawable/edit_text.xml @@ -15,11 +15,8 @@ --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_window_focused="false" android:state_enabled="true" - android:drawable="@drawable/textfield_default" /> - <item android:state_window_focused="false" android:state_enabled="false" - android:drawable="@drawable/textfield_disabled" /> - <item android:state_pressed="true" android:drawable="@drawable/textfield_pressed" /> + <item android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/textfield_default" /> + <item android:state_window_focused="false" android:state_enabled="false" android:drawable="@drawable/textfield_disabled" /> <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_selected" /> <item android:state_enabled="true" android:drawable="@drawable/textfield_default" /> <item android:state_focused="true" android:drawable="@drawable/textfield_disabled_selected" /> diff --git a/core/res/res/drawable/edit_text_holo_dark.xml b/core/res/res/drawable/edit_text_holo_dark.xml index b7d24ff..63ccd1d 100644 --- a/core/res/res/drawable/edit_text_holo_dark.xml +++ b/core/res/res/drawable/edit_text_holo_dark.xml @@ -15,14 +15,11 @@ --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_window_focused="false" android:state_enabled="true" - android:drawable="@drawable/textfield_default_holo_dark" /> - <item android:state_window_focused="false" android:state_enabled="false" - android:drawable="@drawable/textfield_disabled_holo_dark" /> - <item android:state_pressed="true" android:drawable="@drawable/textfield_pressed_holo_dark" /> - <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_selected_holo_dark" /> + <item android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/textfield_default_holo_dark" /> + <item android:state_window_focused="false" android:state_enabled="false" android:drawable="@drawable/textfield_disabled_holo_dark" /> + <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_active_holo_dark" /> <item android:state_enabled="true" android:drawable="@drawable/textfield_default_holo_dark" /> - <item android:state_focused="true" android:drawable="@drawable/textfield_disabled_selected_holo_dark" /> + <item android:state_focused="true" android:drawable="@drawable/textfield_disabled_focused_holo_dark" /> <item android:drawable="@drawable/textfield_disabled_holo_dark" /> </selector> diff --git a/core/res/res/drawable/edit_text_holo_light.xml b/core/res/res/drawable/edit_text_holo_light.xml index dae39e3..324acda 100644 --- a/core/res/res/drawable/edit_text_holo_light.xml +++ b/core/res/res/drawable/edit_text_holo_light.xml @@ -15,14 +15,11 @@ --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_window_focused="false" android:state_enabled="true" - android:drawable="@drawable/textfield_default_holo_light" /> - <item android:state_window_focused="false" android:state_enabled="false" - android:drawable="@drawable/textfield_disabled_holo_light" /> - <item android:state_pressed="true" android:drawable="@drawable/textfield_pressed_holo_light" /> - <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_selected_holo_light" /> + <item android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/textfield_default_holo_light" /> + <item android:state_window_focused="false" android:state_enabled="false" android:drawable="@drawable/textfield_disabled_holo_light" /> + <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_active_holo_light" /> <item android:state_enabled="true" android:drawable="@drawable/textfield_default_holo_light" /> - <item android:state_focused="true" android:drawable="@drawable/textfield_disabled_selected_holo_light" /> + <item android:state_focused="true" android:drawable="@drawable/textfield_disabled_focused_holo_light" /> <item android:drawable="@drawable/textfield_disabled_holo_light" /> </selector> diff --git a/core/res/res/drawable/edit_text_multiline_holo_dark.xml b/core/res/res/drawable/edit_text_multiline_holo_dark.xml new file mode 100644 index 0000000..67d2748 --- /dev/null +++ b/core/res/res/drawable/edit_text_multiline_holo_dark.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/textfield_multiline_default_holo_dark" /> + <item android:state_window_focused="false" android:state_enabled="false" android:drawable="@drawable/textfield_multiline_disabled_holo_dark" /> + <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_multiline_active_holo_dark" /> + <item android:state_enabled="true" android:drawable="@drawable/textfield_multiline_default_holo_dark" /> + <item android:state_focused="true" android:drawable="@drawable/textfield_multiline_disabled_focused_holo_dark" /> + <item android:drawable="@drawable/textfield_multiline_disabled_holo_dark" /> +</selector> diff --git a/core/res/res/drawable/edit_text_multiline_holo_light.xml b/core/res/res/drawable/edit_text_multiline_holo_light.xml new file mode 100644 index 0000000..08b3ec6 --- /dev/null +++ b/core/res/res/drawable/edit_text_multiline_holo_light.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/textfield_multiline_default_holo_light" /> + <item android:state_window_focused="false" android:state_enabled="false" android:drawable="@drawable/textfield_multiline_disabled_holo_light" /> + <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_multiline_active_holo_light" /> + <item android:state_enabled="true" android:drawable="@drawable/textfield_multiline_default_holo_light" /> + <item android:state_focused="true" android:drawable="@drawable/textfield_multiline_disabled_focused_holo_light" /> + <item android:drawable="@drawable/textfield_multiline_disabled_holo_light" /> +</selector> diff --git a/core/res/res/drawable/progress_large_holo.xml b/core/res/res/drawable/progress_large_holo.xml new file mode 100644 index 0000000..5865780 --- /dev/null +++ b/core/res/res/drawable/progress_large_holo.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 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. +--> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item> + <rotate + android:drawable="@drawable/spinner_76_outer_holo" + android:pivotX="50%" + android:pivotY="50%" + android:fromDegrees="0" + android:toDegrees="1080" /> + </item> + <item> + <rotate + android:drawable="@drawable/spinner_76_inner_holo" + android:pivotX="50%" + android:pivotY="50%" + android:fromDegrees="720" + android:toDegrees="0" /> + </item> +</layer-list> diff --git a/core/res/res/drawable/progress_medium_holo.xml b/core/res/res/drawable/progress_medium_holo.xml new file mode 100644 index 0000000..6772f58 --- /dev/null +++ b/core/res/res/drawable/progress_medium_holo.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 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. +--> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item> + <rotate + android:drawable="@drawable/spinner_48_outer_holo" + android:pivotX="50%" + android:pivotY="50%" + android:fromDegrees="0" + android:toDegrees="1080" /> + </item> + <item> + <rotate + android:drawable="@drawable/spinner_48_inner_holo" + android:pivotX="50%" + android:pivotY="50%" + android:fromDegrees="720" + android:toDegrees="0" /> + </item> +</layer-list> diff --git a/core/res/res/drawable/progress_small_holo.xml b/core/res/res/drawable/progress_small_holo.xml new file mode 100644 index 0000000..1d3b62b --- /dev/null +++ b/core/res/res/drawable/progress_small_holo.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 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. +--> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item> + <rotate + android:drawable="@drawable/spinner_16_outer_holo" + android:pivotX="50%" + android:pivotY="50%" + android:fromDegrees="0" + android:toDegrees="1080" /> + </item> + <item> + <rotate + android:drawable="@drawable/spinner_16_inner_holo" + android:pivotX="50%" + android:pivotY="50%" + android:fromDegrees="720" + android:toDegrees="0" /> + </item> +</layer-list> diff --git a/packages/SystemUI/res/anim/navigation_out.xml b/core/res/res/drawable/screen_background_selector_dark.xml index 4717e47..2a81669 100644 --- a/packages/SystemUI/res/anim/navigation_out.xml +++ b/core/res/res/drawable/screen_background_selector_dark.xml @@ -14,9 +14,8 @@ limitations under the License. --> -<set xmlns:android="http://schemas.android.com/apk/res/android" - > - <alpha android:toAlpha="0.0" android:fromAlpha="1.0" - android:duration="@android:integer/config_longAnimTime" - /> -</set> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_accelerated="false" + android:drawable="@android:drawable/screen_background_dark" /> + <item android:drawable="@android:drawable/background_holo_dark" /> +</selector> diff --git a/packages/SystemUI/res/anim/navigation_in.xml b/core/res/res/drawable/screen_background_selector_light.xml index 630fd72..6992cad 100644 --- a/packages/SystemUI/res/anim/navigation_in.xml +++ b/core/res/res/drawable/screen_background_selector_light.xml @@ -14,9 +14,8 @@ limitations under the License. --> -<set xmlns:android="http://schemas.android.com/apk/res/android" - > - <alpha android:fromAlpha="0.0" android:toAlpha="1.0" - android:duration="@android:integer/config_longAnimTime" - /> -</set> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_accelerated="false" + android:drawable="@android:drawable/screen_background_light" /> + <item android:drawable="@android:drawable/background_holo_light" /> +</selector> diff --git a/core/res/res/layout/progress_dialog.xml b/core/res/res/layout/progress_dialog.xml index 298173a..08e720f 100644 --- a/core/res/res/layout/progress_dialog.xml +++ b/core/res/res/layout/progress_dialog.xml @@ -33,7 +33,7 @@ android:paddingBottom="10dip"> <ProgressBar android:id="@android:id/progress" - style="@android:style/Widget.ProgressBar" + style="?android:attr/progressBarStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:max="10000" diff --git a/core/res/res/values-large/themes.xml b/core/res/res/values-large/themes.xml index cdf58fe..8f9b03d 100644 --- a/core/res/res/values-large/themes.xml +++ b/core/res/res/values-large/themes.xml @@ -19,4 +19,6 @@ <resources> <style name="Theme.Holo.DialogWhenLarge" parent="@android:style/Theme.Holo.Dialog"> </style> + <style name="Theme.Holo.Light.DialogWhenLarge" parent="@android:style/Theme.Holo.Light.Dialog"> + </style> </resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index d6684fe..55b3258 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -34,9 +34,11 @@ <!-- Color that matches (as closely as possible) the window background. --> <attr name="colorBackground" format="color" /> <!-- This is a hint for a solid color that can be used for caching - rendered views. This will be the color of the background when - there is a solid background color; it will be null when the - background is a texture or translucent. --> + rendered views. This should be the color of the background when + there is a solid background color; it should be null when the + background is a texture or translucent. When a device is able + to use accelerated drawing (thus setting state_accelerated), the + cache hint is ignored and always assumed to be transparent. --> <attr name="colorBackgroundCacheHint" format="color" /> <!-- Default disabled alpha for widgets that set enabled/disabled alpha programmatically. --> <attr name="disabledAlpha" format="float" /> @@ -140,6 +142,8 @@ <attr name="editTextColor" format="reference|color" /> <!-- EditText background drawable. --> <attr name="editTextBackground" format="reference" /> + <!-- EditText background drawable for multiline EditText. --> + <attr name="editTextMultilineBackground" format="reference" /> <!-- A styled string, specifying the style to be used for showing inline candidate text when composing with an input method. The @@ -227,8 +231,13 @@ <!-- ============= --> <eat-comment /> - <!-- Drawable to use as the overall window background. There are a - few special considerations you should use when settings this + <!-- Drawable to use as the overall window background. As of + {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this may + be a selector that uses state_accelerated to pick a non-solid + color when running on devices that can draw such a bitmap + with complex compositing on top at 60fps. + + <p>There are a few special considerations to use when setting this drawable: <ul> <li> This information will be used to infer the pixel format @@ -2048,6 +2057,9 @@ <attr name="baselineAlignBottom" format="boolean" /> <!-- If true, the image will be cropped to fit within its padding. --> <attr name="cropToPadding" format="boolean" /> + <!-- The offset of the baseline within this view. See {see android.view.View#getBaseline} + for details --> + <attr name="baseline" format="dimension" /> </declare-styleable> <declare-styleable name="ToggleButton"> <!-- The text for the button when it is checked. --> @@ -2165,6 +2177,8 @@ <attr name="minHeight" format="dimension" /> <attr name="maxHeight" /> <attr name="interpolator" format="reference" /> + <!-- Timeout between frames of animation in milliseconds --> + <attr name="animationResolution" format="integer" /> </declare-styleable> <declare-styleable name="SeekBar"> @@ -2496,7 +2510,8 @@ <attr name="textLineHeight" /> <!-- Indicates that a non-editable text can be selected. --> <attr name="textIsSelectable" /> - + <!-- A specific background drawable used by multi-line EditText only. --> + <attr name="multilineBackground" format="reference"/> </declare-styleable> <!-- An <code>input-extras</code> is a container for extra data to supply to an input method. Contains @@ -3394,16 +3409,24 @@ marked it as being of interest. This is an alternative representation of state_checked for when the state should be propagated down the view hierarchy. --> <attr name="state_activated" format="boolean" /> - <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.--> + <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.--> <attr name="state_active" format="boolean" /> - <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.--> + <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.--> <attr name="state_single" format="boolean" /> - <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.--> + <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.--> <attr name="state_first" format="boolean" /> - <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.--> + <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.--> <attr name="state_middle" format="boolean" /> - <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.--> + <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.--> <attr name="state_last" format="boolean" /> + <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}, + indicating that the Drawable is in a view that is hardware accelerated. + This means that the device can at least render a full-screen scaled + bitmap with one layer of text and bitmaps composited on top of it + at 60fps. When this is set, the colorBackgroundCacheHint will be + ignored even if it specifies a solid color, since that optimization + is not needed. --> + <attr name="state_accelerated" format="boolean" /> </declare-styleable> <declare-styleable name="ViewDrawableStates"> <attr name="state_pressed" /> @@ -3412,6 +3435,7 @@ <attr name="state_window_focused" /> <attr name="state_enabled" /> <attr name="state_activated" /> + <attr name="state_accelerated" /> </declare-styleable> <!-- State array representing a menu item that is currently checked. --> <declare-styleable name="MenuItemCheckedState"> @@ -4379,6 +4403,12 @@ <attr name="height" /> <!-- Specifies a drawable to use for the 'home as up' indicator. --> <attr name="homeAsUpIndicator" format="reference" /> + <!-- Specifies a style resource to use for an embedded progress bar. --> + <attr name="progressBarStyle" /> + <!-- Specifies a style resource to use for an indeterminate progress spinner. --> + <attr name="indeterminateProgressStyle" format="reference" /> + <!-- Specifies the horizontal padding on either end for an embedded progress bar. --> + <attr name="progressBarPadding" format="dimension" /> </declare-styleable> <declare-styleable name="ActionMode"> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 28df995..ad4e7a2 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1372,6 +1372,11 @@ <public type="attr" name="textEditNoPasteWindowLayout" /> <public type="attr" name="textIsSelectable" /> <public type="attr" name="windowEnableSplitTouch" /> + <public type="attr" name="indeterminateProgressStyle" /> + <public type="attr" name="progressBarPadding" /> + <public type="attr" name="animationResolution" /> + <public type="attr" name="state_accelerated" /> + <public type="attr" name="baseline" /> <public type="anim" name="animator_fade_in" /> <public type="anim" name="animator_fade_out" /> @@ -1439,6 +1444,7 @@ <public type="style" name="Widget.FragmentBreadCrumbs" /> <public type="style" name="Theme.Holo.DialogWhenLarge" /> + <public type="style" name="Theme.Holo.Light.DialogWhenLarge" /> <public type="string" name="selectTextMode" /> </resources> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 457175b..dc67f45 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -155,12 +155,6 @@ <item name="windowExitAnimation">@anim/shrink_fade_out_from_bottom</item> </style> - <!-- {@hide} --> - <style name="Animation.SlidingCard"> - <item name="windowEnterAnimation">@anim/slide_in_up</item> - <item name="windowExitAnimation">@anim/slide_out_down</item> - </style> - <!-- Window animations that are applied to input method overlay windows. --> <style name="Animation.InputMethod"> <item name="windowEnterAnimation">@anim/input_method_enter</item> @@ -428,6 +422,7 @@ <item name="android:focusableInTouchMode">true</item> <item name="android:clickable">true</item> <item name="android:background">?android:attr/editTextBackground</item> + <item name="android:multilineBackground">?android:attr/editTextMultilineBackground</item> <item name="android:textAppearance">?android:attr/textAppearanceMediumInverse</item> <item name="android:textColor">?android:attr/editTextColor</item> <item name="android:gravity">center_vertical</item> @@ -920,6 +915,8 @@ <item name="android:paddingBottom">0dip</item> <item name="android:titleTextStyle">@android:style/TextAppearance.Widget.ActionBar.Title</item> <item name="android:subtitleTextStyle">@android:style/TextAppearance.Widget.ActionBar.Subtitle</item> + <item name="android:progressBarStyle">@android:style/Widget.ProgressBar.Horizontal</item> + <item name="android:indeterminateProgressStyle">@android:style/Widget.ProgressBar.Small</item> </style> <style name="Widget.ActionMode"> @@ -1356,30 +1353,34 @@ </style> <style name="Widget.Holo.ProgressBar" parent="Widget.ProgressBar"> + <item name="android:indeterminateDrawable">@android:drawable/progress_medium_holo</item> + <item name="android:animationResolution">33</item> </style> <style name="Widget.Holo.ProgressBar.Horizontal" parent="Widget.ProgressBar.Horizontal"> <item name="android:progressDrawable">@android:drawable/progress_horizontal_holo_dark</item> - <item name="android:minHeight">16dip</item> - <item name="android:maxHeight">16dip</item> + <item name="android:minHeight">24dip</item> + <item name="android:maxHeight">24dip</item> </style> - <style name="Widget.Holo.ProgressBar.Small" parent="Widget.ProgressBar.Horizontal"> + <style name="Widget.Holo.ProgressBar.Small" parent="Widget.ProgressBar.Small"> + <item name="android:indeterminateDrawable">@android:drawable/progress_small_holo</item> </style> - <style name="Widget.Holo.ProgressBar.Small.Title" parent="Widget.ProgressBar.Small.Title"> + <style name="Widget.Holo.ProgressBar.Small.Title"> </style> <style name="Widget.Holo.ProgressBar.Large" parent="Widget.ProgressBar.Large"> + <item name="android:indeterminateDrawable">@android:drawable/progress_large_holo</item> </style> - <style name="Widget.Holo.ProgressBar.Inverse" parent="Widget.ProgressBar.Inverse"> + <style name="Widget.Holo.ProgressBar.Inverse"> </style> - <style name="Widget.Holo.ProgressBar.Small.Inverse" parent="Widget.ProgressBar.Small.Inverse"> + <style name="Widget.Holo.ProgressBar.Small.Inverse"> </style> - <style name="Widget.Holo.ProgressBar.Large.Inverse" parent="Widget.ProgressBar.Large.Inverse"> + <style name="Widget.Holo.ProgressBar.Large.Inverse"> </style> <style name="Widget.Holo.SeekBar"> @@ -1540,6 +1541,9 @@ <item name="android:background">@null</item> <item name="android:divider">?android:attr/dividerVertical</item> <item name="android:homeAsUpIndicator">@android:drawable/ic_ab_back_holo_dark</item> + <item name="android:progressBarStyle">@android:style/Widget.Holo.ProgressBar.Horizontal</item> + <item name="android:indeterminateProgressStyle">@android:style/Widget.Holo.ProgressBar</item> + <item name="android:progressBarPadding">32dip</item> </style> <!-- Light widget styles --> @@ -1645,29 +1649,29 @@ <style name="Widget.Holo.Light.PopupWindow" parent="Widget.PopupWindow"> </style> - <style name="Widget.Holo.Light.ProgressBar" parent="Widget.ProgressBar"> + <style name="Widget.Holo.Light.ProgressBar" parent="Widget.Holo.ProgressBar"> </style> - <style name="Widget.Holo.Light.ProgressBar.Horizontal" parent="Widget.ProgressBar.Horizontal"> + <style name="Widget.Holo.Light.ProgressBar.Horizontal" parent="Widget.Holo.ProgressBar.Horizontal"> <item name="android:progressDrawable">@android:drawable/progress_horizontal_holo_light</item> </style> - <style name="Widget.Holo.Light.ProgressBar.Small" parent="Widget.ProgressBar.Small"> + <style name="Widget.Holo.Light.ProgressBar.Small" parent="Widget.Holo.ProgressBar.Small"> </style> - <style name="Widget.Holo.Light.ProgressBar.Small.Title" parent="Widget.ProgressBar.Small.Title"> + <style name="Widget.Holo.Light.ProgressBar.Small.Title" parent="Widget.Holo.ProgressBar.Small.Title"> </style> - <style name="Widget.Holo.Light.ProgressBar.Large" parent="Widget.ProgressBar.Large"> + <style name="Widget.Holo.Light.ProgressBar.Large" parent="Widget.Holo.ProgressBar.Large"> </style> - <style name="Widget.Holo.Light.ProgressBar.Inverse" parent="Widget.ProgressBar.Inverse"> + <style name="Widget.Holo.Light.ProgressBar.Inverse" parent="Widget.Holo.ProgressBar.Inverse"> </style> - <style name="Widget.Holo.Light.ProgressBar.Small.Inverse" parent="Widget.ProgressBar.Small.Inverse"> + <style name="Widget.Holo.Light.ProgressBar.Small.Inverse" parent="Widget.Holo.ProgressBar.Small.Inverse"> </style> - <style name="Widget.Holo.Light.ProgressBar.Large.Inverse" parent="Widget.ProgressBar.Large.Inverse"> + <style name="Widget.Holo.Light.ProgressBar.Large.Inverse" parent="Widget.Holo.ProgressBar.Large.Inverse"> </style> <style name="Widget.Holo.Light.SeekBar" parent="Widget.Holo.SeekBar"> @@ -1794,6 +1798,8 @@ <item name="android:subtitleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionBar.Subtitle</item> <item name="android:background">@null</item> <item name="android:homeAsUpIndicator">@android:drawable/ic_ab_back_holo_light</item> + <item name="android:progressBarStyle">@android:style/Widget.Holo.Light.ProgressBar.Horizontal</item> + <item name="android:indeterminateProgressStyle">@android:style/Widget.Holo.Light.ProgressBar.Small</item> </style> <!-- Animation Styles --> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index c8cce81..dd7c8e48 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -72,6 +72,7 @@ <item name="editTextColor">?android:attr/textColorPrimaryInverse</item> <item name="editTextBackground">@android:drawable/edit_text</item> + <item name="editTextMultilineBackground">@android:drawable/edit_text</item> <item name="candidatesTextStyleSpans">@android:string/candidates_style</item> @@ -123,7 +124,7 @@ <item name="galleryItemBackground">@android:drawable/gallery_item_background</item> <!-- Window attributes --> - <item name="windowBackground">@android:drawable/screen_background_dark</item> + <item name="windowBackground">@android:drawable/screen_background_selector_dark</item> <item name="windowFrame">@null</item> <item name="windowNoTitle">false</item> <item name="windowFullscreen">false</item> @@ -279,7 +280,7 @@ default theme, you should try to assume little more than that the background will be a light color. --> <style name="Theme.Light"> - <item name="windowBackground">@drawable/screen_background_light</item> + <item name="windowBackground">@android:drawable/screen_background_selector_light</item> <item name="colorBackground">@android:color/background_light</item> <item name="colorForeground">@color/bright_foreground_light</item> <item name="colorForegroundInverse">@android:color/bright_foreground_light_inverse</item> @@ -685,6 +686,7 @@ <item name="editTextColor">?android:attr/textColorPrimary</item> <item name="editTextBackground">@android:drawable/edit_text_holo_dark</item> + <item name="editTextMultilineBackground">@android:drawable/edit_text_multiline_holo_dark</item> <item name="candidatesTextStyleSpans">@android:string/candidates_style</item> @@ -736,7 +738,6 @@ <item name="galleryItemBackground">@android:drawable/gallery_item_background</item> <!-- Window attributes --> - <item name="windowBackground">?android:attr/colorBackground</item> <item name="windowFrame">@null</item> <item name="windowNoTitle">false</item> <item name="windowFullscreen">false</item> @@ -918,10 +919,11 @@ <item name="textAppearanceSearchResultSubtitle">@android:style/TextAppearance.Holo.Light.SearchResult.Subtitle</item> <item name="textAppearanceButton">@android:style/TextAppearance.Holo.Light.Widget.Button</item> - + <item name="editTextColor">?android:attr/textColorPrimary</item> <item name="editTextBackground">@android:drawable/edit_text_holo_light</item> - + <item name="editTextMultilineBackground">@android:drawable/edit_text_multiline_holo_light</item> + <item name="candidatesTextStyleSpans">@android:string/candidates_style</item> <item name="textCheckMark">@android:drawable/indicator_check_mark_light</item> @@ -972,7 +974,6 @@ <item name="galleryItemBackground">@android:drawable/gallery_item_background</item> <!-- Window attributes --> - <item name="windowBackground">?android:attr/colorBackground</item> <item name="windowFrame">@null</item> <item name="windowNoTitle">false</item> <item name="windowFullscreen">false</item> @@ -1031,15 +1032,15 @@ <item name="imageWellStyle">@android:style/Widget.Holo.ImageWell</item> <item name="listViewStyle">@android:style/Widget.Holo.ListView</item> <item name="listViewWhiteStyle">@android:style/Widget.Holo.ListView.White</item> - <item name="popupWindowStyle">@android:style/Widget.Holo.PopupWindow</item> - <item name="progressBarStyle">@android:style/Widget.Holo.ProgressBar</item> - <item name="progressBarStyleHorizontal">@android:style/Widget.Holo.ProgressBar.Horizontal</item> - <item name="progressBarStyleSmall">@android:style/Widget.Holo.ProgressBar.Small</item> - <item name="progressBarStyleSmallTitle">@android:style/Widget.Holo.ProgressBar.Small.Title</item> - <item name="progressBarStyleLarge">@android:style/Widget.Holo.ProgressBar.Large</item> - <item name="progressBarStyleInverse">@android:style/Widget.Holo.ProgressBar.Inverse</item> - <item name="progressBarStyleSmallInverse">@android:style/Widget.Holo.ProgressBar.Small.Inverse</item> - <item name="progressBarStyleLargeInverse">@android:style/Widget.Holo.ProgressBar.Large.Inverse</item> + <item name="popupWindowStyle">@android:style/Widget.Holo.Light.PopupWindow</item> + <item name="progressBarStyle">@android:style/Widget.Holo.Light.ProgressBar</item> + <item name="progressBarStyleHorizontal">@android:style/Widget.Holo.Light.ProgressBar.Horizontal</item> + <item name="progressBarStyleSmall">@android:style/Widget.Holo.Light.ProgressBar.Small</item> + <item name="progressBarStyleSmallTitle">@android:style/Widget.Holo.Light.ProgressBar.Small.Title</item> + <item name="progressBarStyleLarge">@android:style/Widget.Holo.Light.ProgressBar.Large</item> + <item name="progressBarStyleInverse">@android:style/Widget.Holo.Light.ProgressBar.Inverse</item> + <item name="progressBarStyleSmallInverse">@android:style/Widget.Holo.Light.ProgressBar.Small.Inverse</item> + <item name="progressBarStyleLargeInverse">@android:style/Widget.Holo.Light.ProgressBar.Large.Inverse</item> <item name="seekBarStyle">@android:style/Widget.Holo.SeekBar</item> <item name="ratingBarStyle">@android:style/Widget.Holo.RatingBar</item> <item name="ratingBarStyleIndicator">@android:style/Widget.Holo.RatingBar.Indicator</item> @@ -1220,6 +1221,12 @@ <item name="textAppearanceInverse">@android:style/TextAppearance.Holo.Light.Inverse</item> </style> + <!-- Theme for a window that will be displayed either full-screen on + smaller screens (small, normal) or as a dialog on larger screens + (large, xlarge) --> + <style name="Theme.Holo.Light.DialogWhenLarge" parent="@android:style/Theme.Holo.Light"> + </style> + <!-- Holo light theme for alert dialog windows, which is used by the {@link android.app.AlertDialog} class. This is basically a dialog but sets the background to empty so it can do two-tone backgrounds. diff --git a/core/tests/ConnectivityManagerTest/assets/accesspoints.xml b/core/tests/ConnectivityManagerTest/assets/accesspoints.xml new file mode 100755 index 0000000..2b0e4af --- /dev/null +++ b/core/tests/ConnectivityManagerTest/assets/accesspoints.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <accesspoint> + <ssid>opennet</ssid> + <security>NONE</security> + </accesspoint> + <accesspoint> + <ssid>GoogleGuest</ssid> + <security>NONE</security> + </accesspoint> + <accesspoint> + <ssid>securenetdhcp</ssid> + <security>PSK</security> + <password>androidwifi</password> + </accesspoint> + <accesspoint> + <ssid>botnet</ssid> + <security>EAP</security> + <eap>PEAP</eap> + <phase2>MSCHAPV2</phase2> + <identity>donut</identity> + <password>android</password> + </accesspoint> +</resources> + diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java new file mode 100644 index 0000000..863fbe6 --- /dev/null +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java @@ -0,0 +1,262 @@ +/* + * 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 com.android.connectivitymanagertest; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiConfiguration.AuthAlgorithm; +import android.net.wifi.WifiConfiguration.KeyMgmt; + +import android.util.Log; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + + +/** + * Help class to process configurations of access points saved in an XML file. + * The configurations of an access point is included in tag + * <accesspoint></accesspoint>. The supported configuration includes: ssid, + * security, eap, phase2, identity, password, anonymousidentity, cacert, usercert, + * in which each is included in the corresponding tags. All access points have to be + * enclosed in tags of <resources></resources>. + * + * The following is a sample configuration file for an access point using EAP-PEAP with MSCHAP2. + * <resources> + * <accesspoint> + * <ssid>testnet</ssid> + * <security>EAP</security> + * <eap>PEAP</eap> + * <phase2>MSCHAP2</phase2> + * <identity>donut</identity</identity> + * <password>abcdefgh</password> + * </accesspoint> + * </resources> + */ +public class AccessPointParserHelper { + private static final String KEYSTORE_SPACE = "keystore://"; + private static final String TAG = "AccessPointParserHelper"; + static final int NONE = 0; + static final int WEP = 1; + static final int PSK = 2; + static final int EAP = 3; + + List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); + + private int getSecurityType (String security) { + if (security.equalsIgnoreCase("NONE")) { + return NONE; + } else if (security.equalsIgnoreCase("WEP")) { + return WEP; + } else if (security.equalsIgnoreCase("PSK")) { + return PSK; + } else if (security.equalsIgnoreCase("EAP")) { + return EAP; + } else { + return -1; + } + } + + private boolean validateEapValue(String value) { + if (value.equalsIgnoreCase("PEAP") || + value.equalsIgnoreCase("TLS") || + value.equalsIgnoreCase("TTLS")) { + return true; + } else { + return false; + } + } + + DefaultHandler mHandler = new DefaultHandler() { + + boolean ssid = false; + boolean security = false; + boolean password = false; + boolean ip = false; + boolean subnetmask = false; + boolean gateway = false; + boolean dns = false; + boolean eap = false; + boolean phase2 = false; + boolean identity = false; + boolean anonymousidentity = false; + boolean cacert = false; + boolean usercert = false; + WifiConfiguration config = null; + int securityType = NONE; + + @Override + public void startElement(String uri, String localName, String tagName, + Attributes attributes) throws SAXException { + if (tagName.equalsIgnoreCase("accesspoint")) { + config = new WifiConfiguration(); + } + if (tagName.equalsIgnoreCase("ssid")) { + ssid = true; + } + if (tagName.equalsIgnoreCase("security")) { + security = true; + } + if (tagName.equalsIgnoreCase("password")) { + password = true; + } + if (tagName.equalsIgnoreCase("eap")) { + eap = true; + } + if (tagName.equalsIgnoreCase("phase2")) { + phase2 = true; + } + if (tagName.equalsIgnoreCase("identity")) { + identity = true; + } + if (tagName.equalsIgnoreCase("anonymousidentity")) { + anonymousidentity = true; + } + if (tagName.equalsIgnoreCase("cacert")) { + cacert = true; + } + if (tagName.equalsIgnoreCase("usercert")) { + usercert = true; + } + } + + @Override + public void endElement(String uri, String localName, String tagName) throws SAXException { + Log.v(TAG, "endElement: " + tagName); + if (tagName.equalsIgnoreCase("accesspoint")) { + networks.add(config); + } + } + + @Override + public void characters(char ch[], int start, int length) throws SAXException { + if (ssid) { + config.SSID = new String(ch, start, length); + Log.v(TAG, "ssid: " + config.SSID); + ssid = false; + } + if (security) { + String securityStr = (new String(ch, start, length)).toUpperCase(); + Log.v(TAG, "security: " + securityStr); + securityType = getSecurityType(securityStr); + Log.v(TAG, "securityType = " + securityType); + switch (securityType) { + case NONE: + config.allowedKeyManagement.set(KeyMgmt.NONE); + break; + case WEP: + config.allowedKeyManagement.set(KeyMgmt.NONE); + config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN); + config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED); + break; + case PSK: + config.allowedKeyManagement.set(KeyMgmt.WPA_PSK); + break; + case EAP: + config.allowedKeyManagement.set(KeyMgmt.WPA_EAP); + config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); + break; + default: + throw new SAXException(); + } + security = false; + } + if (password) { + String passwordStr = new String(ch, start, length); + int len = passwordStr.length(); + if (len == 0) { + throw new SAXException(); + } + Log.v(TAG, "passwordStr:" + passwordStr); + if (securityType == WEP) { + if ((len == 10 || len == 26 || len == 58) && + passwordStr.matches("[0-9A-Fa-f]*")) { + config.wepKeys[0] = passwordStr; + } else { + config.wepKeys[0] = '"' + passwordStr + '"'; + } + } else if (securityType == PSK) { + if (passwordStr.matches("[0-9A-Fa-f]{64}")) { + config.preSharedKey = passwordStr; + } else { + config.preSharedKey = '"' + passwordStr + '"'; + } + } else if (securityType == EAP) { + config.password.setValue(passwordStr); + } else { + throw new SAXException(); + } + password = false; + } + if (eap) { + String eapValue = new String(ch, start, length); + if (!validateEapValue(eapValue)) { + throw new SAXException(); + } + config.eap.setValue(eapValue); + eap = false; + } + if (phase2) { + String phase2Value = new String(ch, start, length); + config.phase2.setValue("auth=" + phase2Value); + phase2 = false; + } + if (identity) { + String identityValue = new String(ch, start, length); + config.identity.setValue(identityValue); + identity = false; + } + if (anonymousidentity) { + String anonyId = new String(ch, start, length); + config.anonymous_identity.setValue(anonyId); + anonymousidentity = false; + } + if (cacert) { + String cacertValue = new String(ch, start, length); + // need to install the credentail to "keystore://" + config.ca_cert.setValue(KEYSTORE_SPACE); + cacert = false; + } + if (usercert) { + String usercertValue = new String(ch, start, length); + config.client_cert.setValue(KEYSTORE_SPACE); + usercert = false; + } + } + }; + + public AccessPointParserHelper() { + } + + /** + * Process the accesspoint.xml file + * @return List of WifiConfiguration + * @throws Exception when parsing the XML file + */ + public List<WifiConfiguration> processAccessPoint(InputStream in) throws Exception { + SAXParserFactory factory = SAXParserFactory.newInstance(); + SAXParser saxParser = factory.newSAXParser(); + saxParser.parse(in, mHandler); + return networks; + } +} diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java index e42b657..7c46e7a 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java @@ -16,8 +16,10 @@ package com.android.connectivitymanagertest; +import com.android.connectivitymanagertest.R; import android.app.Activity; import android.content.Context; +import android.content.res.Resources; import android.content.BroadcastReceiver; import android.content.Intent; import android.content.IntentFilter; @@ -25,19 +27,22 @@ import android.os.Bundle; import android.provider.Settings; import android.util.Log; import android.view.KeyEvent; + +import java.io.InputStream; +import java.util.ArrayList; import java.util.List; import android.widget.LinearLayout; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.NetworkInfo.State; +import android.net.wifi.SupplicantState; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.net.wifi.WifiInfo; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration.KeyMgmt; - /** * An activity registered with connectivity manager broadcast * provides network connectivity information and @@ -46,8 +51,11 @@ import android.net.wifi.WifiConfiguration.KeyMgmt; public class ConnectivityManagerTestActivity extends Activity { public static final String LOG_TAG = "ConnectivityManagerTestActivity"; - public static final int WAIT_FOR_SCAN_RESULT = 5 * 1000; //5 seconds + public static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; //10 seconds public static final int WIFI_SCAN_TIMEOUT = 20 * 1000; + public static final int SHORT_TIMEOUT = 5 * 1000; + public static final long LONG_TIMEOUT = 50 * 1000; + private static final String ACCESS_POINT_FILE = "accesspoints.xml"; public ConnectivityReceiver mConnectivityReceiver = null; public WifiReceiver mWifiReceiver = null; /* @@ -175,6 +183,7 @@ public class ConnectivityManagerTestActivity extends Activity { mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + mIntentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); registerReceiver(mWifiReceiver, mIntentFilter); // Get an instance of ConnectivityManager @@ -185,10 +194,26 @@ public class ConnectivityManagerTestActivity extends Activity { if (mWifiManager.isWifiEnabled()) { Log.v(LOG_TAG, "Clear Wifi before we start the test."); - clearWifi(); + removeConfiguredNetworksAndDisableWifi(); } } + public List<WifiConfiguration> loadNetworkConfigurations() throws Exception { + InputStream in = getAssets().open(ACCESS_POINT_FILE); + AccessPointParserHelper parseHelper = new AccessPointParserHelper(); + return parseHelper.processAccessPoint(in); + } + + private void printNetConfig(String[] configuration) { + for (int i = 0; i < configuration.length; i++) { + if (i == 0) { + Log.v(LOG_TAG, "SSID: " + configuration[0]); + } else { + Log.v(LOG_TAG, " " + configuration[i]); + } + } + } + // for each network type, initialize network states to UNKNOWN, and no verification flag is set public void initializeNetworkStates() { for (int networkType = NUM_NETWORK_TYPES - 1; networkType >=0; networkType--) { @@ -245,6 +270,68 @@ public class ConnectivityManagerTestActivity extends Activity { } } + // Wait for network connectivity state: CONNECTING, CONNECTED, SUSPENDED, + // DISCONNECTING, DISCONNECTED, UNKNOWN + public boolean waitForNetworkState(int networkType, State expectedState, long timeout) { + long startTime = System.currentTimeMillis(); + while (true) { + if ((System.currentTimeMillis() - startTime) > timeout) { + if (mCM.getNetworkInfo(networkType).getState() != expectedState) { + return false; + } else { + // the broadcast has been sent out. the state has been changed. + Log.v(LOG_TAG, "networktype: " + networkType + " state: " + + mCM.getNetworkInfo(networkType)); + return true; + } + } + Log.v(LOG_TAG, "Wait for the connectivity state for network: " + networkType + + " to be " + expectedState.toString()); + synchronized (connectivityObject) { + try { + connectivityObject.wait(SHORT_TIMEOUT); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if ((mNetworkInfo.getType() != networkType) || + (mNetworkInfo.getState() != expectedState)) { + Log.v(LOG_TAG, "network state for " + mNetworkInfo.getType() + + "is: " + mNetworkInfo.getState()); + continue; + } + return true; + } + } + } + + // Wait for Wifi state: WIFI_STATE_DISABLED, WIFI_STATE_DISABLING, WIFI_STATE_ENABLED, + // WIFI_STATE_ENALBING, WIFI_STATE_UNKNOWN + public boolean waitForWifiState(int expectedState, long timeout) { + long startTime = System.currentTimeMillis(); + while (true) { + if ((System.currentTimeMillis() - startTime) > timeout) { + if (mWifiState != expectedState) { + return false; + } else { + return true; + } + } + Log.v(LOG_TAG, "Wait for wifi state to be: " + expectedState); + synchronized (wifiObject) { + try { + wifiObject.wait(SHORT_TIMEOUT); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if (mWifiState != expectedState) { + Log.v(LOG_TAG, "Wifi state is: " + mWifiNetworkInfo.getState()); + continue; + } + return true; + } + } + } + // Return true if device is currently connected to mobile network public boolean isConnectedToMobile() { return (mNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE); @@ -265,6 +352,22 @@ public class ConnectivityManagerTestActivity extends Activity { * We don't verify whether the connection is successful or not, leave this to the test */ public boolean connectToWifi(String knownSSID) { + WifiConfiguration config = new WifiConfiguration(); + config.SSID = knownSSID; + config.allowedKeyManagement.set(KeyMgmt.NONE); + return connectToWifiWithConfiguration(config); + } + + /** + * Connect to Wi-Fi with the given configuration. Note the SSID in the configuration + * is pure string, we need to convert it to quoted string. + * @param config + * @return + */ + public boolean connectToWifiWithConfiguration(WifiConfiguration config) { + String ssid = config.SSID; + config.SSID = convertToQuotedString(ssid); + //If Wifi is not enabled, enable it if (!mWifiManager.isWifiEnabled()) { Log.v(LOG_TAG, "Wifi is not enabled, enable it"); @@ -273,6 +376,7 @@ public class ConnectivityManagerTestActivity extends Activity { List<ScanResult> netList = mWifiManager.getScanResults(); if (netList == null) { + Log.v(LOG_TAG, "scan results are null"); // if no scan results are available, start active scan mWifiManager.startScanActive(); mScanResultIsAvailable = false; @@ -299,17 +403,20 @@ public class ConnectivityManagerTestActivity extends Activity { } netList = mWifiManager.getScanResults(); + for (int i = 0; i < netList.size(); i++) { ScanResult sr= netList.get(i); - if (sr.SSID.equals(knownSSID)) { - Log.v(LOG_TAG, "found " + knownSSID + " in the scan result list"); - WifiConfiguration config = new WifiConfiguration(); - config.SSID = convertToQuotedString(sr.SSID); - config.allowedKeyManagement.set(KeyMgmt.NONE); + if (sr.SSID.equals(ssid)) { + Log.v(LOG_TAG, "found " + ssid + " in the scan result list"); int networkId = mWifiManager.addNetwork(config); // Connect to network by disabling others. mWifiManager.enableNetwork(networkId, true); mWifiManager.saveConfiguration(); + List<WifiConfiguration> wifiNetworks = mWifiManager.getConfiguredNetworks(); + for (WifiConfiguration netConfig : wifiNetworks) { + Log.v(LOG_TAG, netConfig.toString()); + } + mWifiManager.reconnect(); break; } @@ -317,14 +424,14 @@ public class ConnectivityManagerTestActivity extends Activity { List<WifiConfiguration> netConfList = mWifiManager.getConfiguredNetworks(); if (netConfList.size() <= 0) { - Log.v(LOG_TAG, knownSSID + " is not available"); + Log.v(LOG_TAG, ssid + " is not available"); return false; } return true; } /* - * Disconnect from the current AP + * Disconnect from the current AP and remove configured networks. */ public boolean disconnectAP() { if (mWifiManager.isWifiEnabled()) { @@ -360,9 +467,9 @@ public class ConnectivityManagerTestActivity extends Activity { } /** - * Disconnect from the current Wifi and clear the configuration list + * Remove configured networks and disable wifi */ - public boolean clearWifi() { + public boolean removeConfiguredNetworksAndDisableWifi() { if (!disconnectAP()) { return false; } diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java index 592be92..3d4dc3d 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java @@ -21,6 +21,7 @@ import android.test.InstrumentationTestRunner; import android.test.InstrumentationTestSuite; import android.util.Log; import com.android.connectivitymanagertest.functional.ConnectivityManagerMobileTest; +import com.android.connectivitymanagertest.functional.WifiConnectionTest; import junit.framework.TestSuite; @@ -38,6 +39,7 @@ public class ConnectivityManagerTestRunner extends InstrumentationTestRunner { public TestSuite getAllTests() { TestSuite suite = new InstrumentationTestSuite(this); suite.addTestSuite(ConnectivityManagerMobileTest.class); + suite.addTestSuite(WifiConnectionTest.class); return suite; } diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java index ad8d444..5959cf3 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java @@ -41,8 +41,6 @@ public class ConnectivityManagerMobileTest extends ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> { private static final String LOG_TAG = "ConnectivityManagerMobileTest"; private static final String PKG_NAME = "com.android.connectivitymanagertest"; - private static final long STATE_TRANSITION_SHORT_TIMEOUT = 5 * 1000; - private static final long STATE_TRANSITION_LONG_TIMEOUT = 30 * 1000; private String TEST_ACCESS_POINT; private ConnectivityManagerTestActivity cmActivity; @@ -64,9 +62,14 @@ public class ConnectivityManagerMobileTest wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "CMWakeLock"); wl.acquire(); // Each test case will start with cellular connection - waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); - verifyCellularConnection(); + if (!cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)) { + // Note: When the test fails in setUp(), tearDown is not called. In that case, + // the activity is destroyed which blocks the next test at "getActivity()". + // tearDown() is called hear to avoid that situation. + tearDown(); + fail("Device is not connected to Mobile, setUp failed"); + } } @Override @@ -74,86 +77,26 @@ public class ConnectivityManagerMobileTest cmActivity.finish(); Log.v(LOG_TAG, "tear down ConnectivityManagerTestActivity"); wl.release(); - cmActivity.clearWifi(); + cmActivity.removeConfiguredNetworksAndDisableWifi(); super.tearDown(); } // help function to verify 3G connection public void verifyCellularConnection() { - NetworkInfo extraNetInfo = cmActivity.mNetworkInfo; + NetworkInfo extraNetInfo = cmActivity.mCM.getActiveNetworkInfo(); assertEquals("network type is not MOBILE", ConnectivityManager.TYPE_MOBILE, - extraNetInfo.getType()); + extraNetInfo.getType()); assertTrue("not connected to cellular network", extraNetInfo.isConnected()); assertTrue("no data connection", cmActivity.mState.equals(State.CONNECTED)); } - // Wait for network connectivity state: CONNECTING, CONNECTED, SUSPENDED, - // DISCONNECTING, DISCONNECTED, UNKNOWN - private void waitForNetworkState(int networkType, State expectedState, long timeout) { - long startTime = System.currentTimeMillis(); - while (true) { - if ((System.currentTimeMillis() - startTime) > timeout) { - if (cmActivity.mCM.getNetworkInfo(networkType).getState() != expectedState) { - assertFalse("Wait for network state timeout", true); - } else { - // the broadcast has been sent out. the state has been changed. - return; - } - } - Log.v(LOG_TAG, "Wait for the connectivity state for network: " + networkType + - " to be " + expectedState.toString()); - synchronized (cmActivity.connectivityObject) { - try { - cmActivity.connectivityObject.wait(STATE_TRANSITION_SHORT_TIMEOUT); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if ((cmActivity.mNetworkInfo.getType() != networkType) || - (cmActivity.mNetworkInfo.getState() != expectedState)) { - Log.v(LOG_TAG, "network state for " + cmActivity.mNetworkInfo.getType() + - "is: " + cmActivity.mNetworkInfo.getState()); - continue; - } - break; - } - } - } - - // Wait for Wifi state: WIFI_STATE_DISABLED, WIFI_STATE_DISABLING, WIFI_STATE_ENABLED, - // WIFI_STATE_ENALBING, WIFI_STATE_UNKNOWN - private void waitForWifiState(int expectedState, long timeout) { - long startTime = System.currentTimeMillis(); - while (true) { - if ((System.currentTimeMillis() - startTime) > timeout) { - if (cmActivity.mWifiState != expectedState) { - assertFalse("Wait for Wifi state timeout", true); - } else { - return; - } - } - Log.v(LOG_TAG, "Wait for wifi state to be: " + expectedState); - synchronized (cmActivity.wifiObject) { - try { - cmActivity.wifiObject.wait(5*1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if (cmActivity.mWifiState != expectedState) { - Log.v(LOG_TAG, "Wifi state is: " + cmActivity.mWifiNetworkInfo.getState()); - continue; - } - break; - } - } - } - // Test case 1: Test enabling Wifi without associating with any AP @LargeTest public void test3GToWifiNotification() { // To avoid UNKNOWN state when device boots up cmActivity.enableWifi(); try { - Thread.sleep(2 * STATE_TRANSITION_SHORT_TIMEOUT); + Thread.sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT); } catch (Exception e) { Log.v(LOG_TAG, "exception: " + e.toString()); } @@ -170,7 +113,7 @@ public class ConnectivityManagerMobileTest // Eanble Wifi cmActivity.enableWifi(); try { - Thread.sleep(2 * STATE_TRANSITION_SHORT_TIMEOUT); + Thread.sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT); } catch (Exception e) { Log.v(LOG_TAG, "exception: " + e.toString()); } @@ -208,12 +151,13 @@ public class ConnectivityManagerMobileTest assertTrue("failed to connect to " + TEST_ACCESS_POINT, cmActivity.connectToWifi(TEST_ACCESS_POINT)); - waitForWifiState(WifiManager.WIFI_STATE_ENABLED, STATE_TRANSITION_LONG_TIMEOUT); + assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_ENABLED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); Log.v(LOG_TAG, "wifi state is enabled"); - waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); - waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); // validate states if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { @@ -237,12 +181,13 @@ public class ConnectivityManagerMobileTest // Connect to TEST_ACCESS_POINT assertTrue("failed to connect to " + TEST_ACCESS_POINT, cmActivity.connectToWifi(TEST_ACCESS_POINT)); - waitForWifiState(WifiManager.WIFI_STATE_ENABLED, STATE_TRANSITION_LONG_TIMEOUT); - waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); + assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_ENABLED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); try { - Thread.sleep(STATE_TRANSITION_SHORT_TIMEOUT); + Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT); } catch (Exception e) { Log.v(LOG_TAG, "exception: " + e.toString()); } @@ -255,11 +200,12 @@ public class ConnectivityManagerMobileTest } // Wait for the Wifi state to be DISABLED - waitForWifiState(WifiManager.WIFI_STATE_DISABLED, STATE_TRANSITION_LONG_TIMEOUT); - waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); - waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); + assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_DISABLED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); //Prepare for connectivity state verification NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); @@ -275,10 +221,10 @@ public class ConnectivityManagerMobileTest cmActivity.enableWifi(); // Wait for Wifi to be connected and mobile to be disconnected - waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); - waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); // validate wifi states if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { @@ -298,12 +244,12 @@ public class ConnectivityManagerMobileTest assertTrue("failed to connect to " + TEST_ACCESS_POINT, cmActivity.connectToWifi(TEST_ACCESS_POINT)); - waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); // Wait for a few seconds to avoid the state that both Mobile and Wifi is connected try { - Thread.sleep(STATE_TRANSITION_SHORT_TIMEOUT); + Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT); } catch (Exception e) { Log.v(LOG_TAG, "exception: " + e.toString()); } @@ -318,12 +264,12 @@ public class ConnectivityManagerMobileTest NetworkState.TO_DISCONNECTION, State.DISCONNECTED); // clear Wifi - cmActivity.clearWifi(); + cmActivity.removeConfiguredNetworksAndDisableWifi(); - waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); - waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); // validate states if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { @@ -357,7 +303,7 @@ public class ConnectivityManagerMobileTest // Enable airplane mode cmActivity.setAirplaneMode(getInstrumentation().getContext(), true); try { - Thread.sleep(STATE_TRANSITION_SHORT_TIMEOUT); + Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT); } catch (Exception e) { Log.v(LOG_TAG, "exception: " + e.toString()); } @@ -389,8 +335,8 @@ public class ConnectivityManagerMobileTest // disable airplane mode cmActivity.setAirplaneMode(getInstrumentation().getContext(), false); - waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); // Validate the state transition if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) { @@ -414,8 +360,8 @@ public class ConnectivityManagerMobileTest // Eanble airplane mode cmActivity.setAirplaneMode(getInstrumentation().getContext(), true); - waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, @@ -429,8 +375,8 @@ public class ConnectivityManagerMobileTest // Connect to Wifi assertTrue("failed to connect to " + TEST_ACCESS_POINT, cmActivity.connectToWifi(TEST_ACCESS_POINT)); - waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); // validate state and broadcast if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { @@ -457,11 +403,11 @@ public class ConnectivityManagerMobileTest assertTrue("failed to connect to " + TEST_ACCESS_POINT, cmActivity.connectToWifi(TEST_ACCESS_POINT)); - waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); try { - Thread.sleep(STATE_TRANSITION_SHORT_TIMEOUT); + Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT); } catch (Exception e) { Log.v(LOG_TAG, "exception: " + e.toString()); } @@ -469,11 +415,11 @@ public class ConnectivityManagerMobileTest // Enable airplane mode without clearing Wifi cmActivity.setAirplaneMode(getInstrumentation().getContext(), true); - waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); try { - Thread.sleep(STATE_TRANSITION_SHORT_TIMEOUT); + Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT); } catch (Exception e) { Log.v(LOG_TAG, "exception: " + e.toString()); } @@ -487,10 +433,10 @@ public class ConnectivityManagerMobileTest // Disable airplane mode cmActivity.setAirplaneMode(getInstrumentation().getContext(), false); - waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); - waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); // validate the state transition if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { @@ -508,14 +454,15 @@ public class ConnectivityManagerMobileTest //Connect to TEST_ACCESS_POINT assertTrue("failed to connect to " + TEST_ACCESS_POINT, cmActivity.connectToWifi(TEST_ACCESS_POINT)); - waitForWifiState(WifiManager.WIFI_STATE_ENABLED, STATE_TRANSITION_LONG_TIMEOUT); - waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); + assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_ENABLED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); assertNotNull("Not associated with any AP", cmActivity.mWifiManager.getConnectionInfo().getBSSID()); try { - Thread.sleep(STATE_TRANSITION_SHORT_TIMEOUT); + Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT); } catch (Exception e) { Log.v(LOG_TAG, "exception: " + e.toString()); } @@ -527,13 +474,14 @@ public class ConnectivityManagerMobileTest } // Verify the connectivity state for Wifi is DISCONNECTED - waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED, - STATE_TRANSITION_LONG_TIMEOUT); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); if (!cmActivity.disableWifi()) { Log.v(LOG_TAG, "disable Wifi failed"); return; } - waitForWifiState(WifiManager.WIFI_STATE_DISABLED, STATE_TRANSITION_LONG_TIMEOUT); + assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_DISABLED, + ConnectivityManagerTestActivity.LONG_TIMEOUT)); } } diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java new file mode 100644 index 0000000..69eb5db --- /dev/null +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java @@ -0,0 +1,134 @@ +/* + * 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 com.android.connectivitymanagertest.functional; + +import com.android.connectivitymanagertest.ConnectivityManagerTestActivity; +import com.android.connectivitymanagertest.NetworkState; + +import android.R; +import android.app.Activity; +import android.content.Intent; +import android.content.Context; +import android.content.res.Resources; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.NetworkInfo.State; + +import android.test.suitebuilder.annotation.LargeTest; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * Test Wi-Fi connection with different configuration + * To run this tests: + * adb shell am instrument -e class + * com.android.connectivitymanagertest.functional.WifiConnectionTest + * -w com.android.connectivitymanagertest/.ConnectivityManagerTestRunner + */ +public class WifiConnectionTest + extends ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> { + private static final String TAG = "WifiConnectionTest"; + private static final boolean DEBUG = true; + private static final String PKG_NAME = "com.android.connectivitymanagertests"; + private List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); + private ConnectivityManagerTestActivity mAct; + + public WifiConnectionTest() { + super(PKG_NAME, ConnectivityManagerTestActivity.class); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + mAct = getActivity(); + networks = mAct.loadNetworkConfigurations(); + if (DEBUG) { + printNetworkConfigurations(); + } + + // enable Wifi and verify wpa_supplicant is started + assertTrue("enable Wifi failed", mAct.enableWifi()); + try { + Thread.sleep( 2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT); + } catch (Exception e) { + fail("interrupted while waiting for WPA_SUPPLICANT to start"); + } + WifiInfo mConnection = mAct.mWifiManager.getConnectionInfo(); + assertNotNull(mConnection); + assertTrue("wpa_supplicant is not started ", mAct.mWifiManager.pingSupplicant()); + } + + private void printNetworkConfigurations() { + Log.v(TAG, "==== print network configurations parsed from XML file ===="); + Log.v(TAG, "number of access points: " + networks.size()); + for (WifiConfiguration config : networks) { + Log.v(TAG, config.toString()); + } + } + + @Override + public void tearDown() throws Exception { + mAct.removeConfiguredNetworksAndDisableWifi(); + super.tearDown(); + } + + /** + * Connect to the provided Wi-Fi network + * @param config is the network configuration + * @return true if the connection is successful. + */ + private void connectToWifi(WifiConfiguration config) { + // step 1: connect to the test access point + assertTrue("failed to connect to " + config.SSID, + mAct.connectToWifiWithConfiguration(config)); + + // step 2: verify Wifi state and network state; + assertTrue(mAct.waitForWifiState(WifiManager.WIFI_STATE_ENABLED, + ConnectivityManagerTestActivity.SHORT_TIMEOUT)); + assertTrue(mAct.waitForNetworkState(ConnectivityManager.TYPE_WIFI, + State.CONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT)); + + // step 3: verify the current connected network is the given SSID + if (DEBUG) { + Log.v(TAG, "config.SSID = " + config.SSID); + Log.v(TAG, "mAct.mWifiManager.getConnectionInfo.getSSID()" + + mAct.mWifiManager.getConnectionInfo().getSSID()); + } + assertTrue(config.SSID.contains(mAct.mWifiManager.getConnectionInfo().getSSID())); + + // Maintain the connection for 50 seconds before switching + try { + Thread.sleep(50*1000); + } catch (Exception e) { + fail("interrupted while waiting for WPA_SUPPLICANT to start"); + } + } + + @LargeTest + public void testWifiConnections() { + for (int i = 0; i < networks.size(); i++) { + connectToWifi(networks.get(i)); + mAct.removeConfiguredNetworksAndDisableWifi(); + } + } +} diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java index f019599..43cf06a 100644 --- a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java +++ b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java @@ -110,6 +110,21 @@ public class BluetoothStressTest extends InstrumentationTestCase { mTestUtils.disable(adapter); } + public void testAcceptPair() { + int iterations = BluetoothTestRunner.sPairIterations; + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sPairAddress); + mTestUtils.enable(adapter); + + for (int i = 0; i < iterations; i++) { + mTestUtils.writeOutput("acceptPair iteration " + (i + 1) + " of " + iterations); + mTestUtils.acceptPair(adapter, device, BluetoothTestRunner.sPairPasskey, + BluetoothTestRunner.sPairPin); + mTestUtils.unpair(adapter, device); + } + mTestUtils.disable(adapter); + } + public void testConnectA2dp() { int iterations = BluetoothTestRunner.sConnectA2dpIterations; if (iterations == 0) { diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java index 328891c..29dee34 100644 --- a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java +++ b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java @@ -137,7 +137,6 @@ public class BluetoothTestUtils extends Assert { @Override public void onReceive(Context context, Intent intent) { - Log.i("BT", intent.toString()); if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) { setFiredFlag(DISCOVERY_STARTED_FLAG); } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) { @@ -203,7 +202,7 @@ public class BluetoothTestUtils extends Assert { if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(intent.getAction())) { int varient = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, -1); assertNotSame(-1, varient); - switch(varient) { + switch (varient) { case BluetoothDevice.PAIRING_VARIANT_PIN: mDevice.setPin(mPin); break; @@ -252,7 +251,7 @@ public class BluetoothTestUtils extends Assert { mDevice = device; mProfile = profile; - switch(mProfile) { + switch (mProfile) { case BluetoothProfile.A2DP: mConnectionAction = BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED; break; @@ -384,11 +383,14 @@ public class BluetoothTestUtils extends Assert { mask = 0; // Don't check for received intents since we might have missed them. break; case BluetoothAdapter.STATE_OFF: - case BluetoothAdapter.STATE_TURNING_OFF: assertFalse(adapter.isEnabled()); start = System.currentTimeMillis(); assertTrue(adapter.enable()); break; + case BluetoothAdapter.STATE_TURNING_OFF: + start = System.currentTimeMillis(); + assertTrue(adapter.enable()); + break; default: removeReceiver(receiver); fail(String.format("enable() invalid state: state=%d", state)); @@ -410,7 +412,6 @@ public class BluetoothTestUtils extends Assert { return; } } else { - assertFalse(adapter.isEnabled()); assertEquals(BluetoothAdapter.STATE_TURNING_ON, state); } sleep(POLL_TIME); @@ -437,7 +438,6 @@ public class BluetoothTestUtils extends Assert { case BluetoothAdapter.STATE_TURNING_ON: assertFalse(adapter.isEnabled()); start = System.currentTimeMillis(); - assertTrue(adapter.disable()); break; case BluetoothAdapter.STATE_ON: assertTrue(adapter.isEnabled()); @@ -470,7 +470,6 @@ public class BluetoothTestUtils extends Assert { return; } } else { - assertFalse(adapter.isEnabled()); assertEquals(BluetoothAdapter.STATE_TURNING_OFF, state); } sleep(POLL_TIME); @@ -629,11 +628,22 @@ public class BluetoothTestUtils extends Assert { } public void pair(BluetoothAdapter adapter, BluetoothDevice device, int passkey, byte[] pin) { + pairOrAcceptPair(adapter, device, passkey, pin, true); + } + + public void acceptPair(BluetoothAdapter adapter, BluetoothDevice device, int passkey, + byte[] pin) { + pairOrAcceptPair(adapter, device, passkey, pin, false); + } + + private void pairOrAcceptPair(BluetoothAdapter adapter, BluetoothDevice device, int passkey, + byte[] pin, boolean pair) { int mask = PairReceiver.STATE_BONDING_FLAG | PairReceiver.STATE_BONDED_FLAG; long start = -1; + String methodName = pair ? "pair()" : "acceptPair()"; if (!adapter.isEnabled()) { - fail("pair() bluetooth not enabled"); + fail(methodName + " bluetooth not enabled"); } PairReceiver receiver = getPairReceiver(device, passkey, pin, mask); @@ -643,7 +653,9 @@ public class BluetoothTestUtils extends Assert { case BluetoothDevice.BOND_NONE: assertFalse(adapter.getBondedDevices().contains(device)); start = System.currentTimeMillis(); - assertTrue(device.createBond()); + if (pair) { + assertTrue(device.createBond()); + } break; case BluetoothDevice.BOND_BONDING: mask = 0; // Don't check for received intents since we might have missed them. @@ -653,7 +665,8 @@ public class BluetoothTestUtils extends Assert { return; default: removeReceiver(receiver); - fail(String.format("pair() invalid state: device=%s, state=%d", device, state)); + fail(String.format("%s invalid state: device=%s, state=%d", methodName, device, + state)); } long s = System.currentTimeMillis(); @@ -664,10 +677,10 @@ public class BluetoothTestUtils extends Assert { if ((receiver.getFiredFlags() & mask) == mask) { long finish = receiver.getCompletedTime(); if (start != -1 && finish != -1) { - writeOutput(String.format("pair() completed in %d ms: device=%s", + writeOutput(String.format("%s completed in %d ms: device=%s", methodName, (finish - start), device)); } else { - writeOutput(String.format("pair() completed: device=%s", device)); + writeOutput(String.format("%s completed: device=%s", methodName, device)); } removeReceiver(receiver); return; @@ -678,9 +691,9 @@ public class BluetoothTestUtils extends Assert { int firedFlags = receiver.getFiredFlags(); removeReceiver(receiver); - fail(String.format("pair() timeout: device=%s, state=%d (expected %d), " - + "flags=0x%x (expected 0x%x)", device, state, BluetoothDevice.BOND_BONDED, - firedFlags, mask)); + fail(String.format("%s timeout: device=%s, state=%d (expected %d), " + + "flags=0x%x (expected 0x%x)", methodName, device, state, + BluetoothDevice.BOND_BONDED, firedFlags, mask)); } public void unpair(BluetoothAdapter adapter, BluetoothDevice device) { @@ -788,7 +801,7 @@ public class BluetoothTestUtils extends Assert { long finish = receiver.getCompletedTime(); if (start != -1 && finish != -1) { writeOutput(String.format("connectProfile() completed in %d ms: " - +"device=%s, profile=%d", (finish - start), device, profile)); + + "device=%s, profile=%d", (finish - start), device, profile)); } else { writeOutput(String.format("connectProfile() completed: device=%s, " + "profile=%d", device, profile)); @@ -857,7 +870,7 @@ public class BluetoothTestUtils extends Assert { long finish = receiver.getCompletedTime(); if (start != -1 && finish != -1) { writeOutput(String.format("disconnectProfile() completed in %d ms: " - +"device=%s, profile=%d", (finish - start), device, profile)); + + "device=%s, profile=%d", (finish - start), device, profile)); } else { writeOutput(String.format("disconnectProfile() completed: device=%s, " + "profile=%d", device, profile)); @@ -934,14 +947,12 @@ public class BluetoothTestUtils extends Assert { long s = System.currentTimeMillis(); switch (profile) { case BluetoothProfile.A2DP: - while (mA2dp != null - && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { + while (mA2dp != null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { sleep(POLL_TIME); } return mA2dp; case BluetoothProfile.HEADSET: - while (mHeadset != null - && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { + while (mHeadset != null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { sleep(POLL_TIME); } return mHeadset; diff --git a/core/tests/coretests/src/android/os/AsyncChannelTest.java b/core/tests/coretests/src/com/android/internal/util/AsyncChannelTest.java index 43c8290..7088650 100644 --- a/core/tests/coretests/src/android/os/AsyncChannelTest.java +++ b/core/tests/coretests/src/com/android/internal/util/AsyncChannelTest.java @@ -14,8 +14,9 @@ * limitations under the License. */ -package android.os; +package com.android.internal.util; +import android.os.Debug; import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; @@ -26,13 +27,17 @@ import junit.framework.TestCase; */ public class AsyncChannelTest extends TestCase { private static final boolean DBG = true; - private static final boolean WAIT_FOR_DEBUGGER = true; + private static final boolean WAIT_FOR_DEBUGGER = false; private static final String TAG = "AsyncChannelTest"; @SmallTest public void test1() throws Exception { + if (DBG) log("test1"); if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger(); - Log.d(TAG, "test1"); assertTrue(1 == 1); } + + protected void log(String s) { + Log.d(TAG, s); + } } diff --git a/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java b/core/tests/coretests/src/com/android/internal/util/HierarchicalStateMachineTest.java index 6820987..36666c4 100644 --- a/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java +++ b/core/tests/coretests/src/com/android/internal/util/HierarchicalStateMachineTest.java @@ -14,7 +14,13 @@ * limitations under the License. */ -package android.os; +package com.android.internal.util; + +import android.os.Debug; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.os.SystemClock; import com.android.internal.util.HierarchicalState; import com.android.internal.util.HierarchicalStateMachine; @@ -28,8 +34,6 @@ import junit.framework.TestCase; /** * Test for HierarchicalStateMachine. - * - * @author wink@google.com (Wink Saville) */ public class HierarchicalStateMachineTest extends TestCase { private static final int TEST_CMD_1 = 1; @@ -40,7 +44,7 @@ public class HierarchicalStateMachineTest extends TestCase { private static final int TEST_CMD_6 = 6; private static final boolean DBG = true; - private static final boolean WAIT_FOR_DEBUGGER = true; + private static final boolean WAIT_FOR_DEBUGGER = false; private static final String TAG = "HierarchicalStateMachineTest"; /** @@ -92,7 +96,7 @@ public class HierarchicalStateMachineTest extends TestCase { @SmallTest public void testStateMachineQuitTest() throws Exception { - //if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger(); + if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger(); StateMachineQuitTest smQuitTest = new StateMachineQuitTest("smQuitTest"); smQuitTest.start(); diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java index 9c47dab..f3f3653 100644 --- a/graphics/java/android/graphics/drawable/RotateDrawable.java +++ b/graphics/java/android/graphics/drawable/RotateDrawable.java @@ -232,8 +232,6 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { float toDegrees = a.getFloat( com.android.internal.R.styleable.RotateDrawable_toDegrees, 360.0f); - toDegrees = Math.max(fromDegrees, toDegrees); - int res = a.getResourceId( com.android.internal.R.styleable.RotateDrawable_drawable, 0); Drawable drawable = null; diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h index 3d77278..bba7ed7 100644 --- a/include/media/IMediaPlayer.h +++ b/include/media/IMediaPlayer.h @@ -34,7 +34,6 @@ public: virtual void disconnect() = 0; - virtual status_t setVideoISurface(const sp<ISurface>& surface) = 0; virtual status_t setVideoSurface(const sp<Surface>& surface) = 0; virtual status_t prepareAsync() = 0; virtual status_t start() = 0; diff --git a/include/media/IOMX.h b/include/media/IOMX.h index fa775e7..cb36bbb 100644 --- a/include/media/IOMX.h +++ b/include/media/IOMX.h @@ -120,30 +120,6 @@ public: node_id node, const char *parameter_name, OMX_INDEXTYPE *index) = 0; - - virtual sp<IOMXRenderer> createRenderer( - const sp<ISurface> &surface, - const char *componentName, - OMX_COLOR_FORMATTYPE colorFormat, - size_t encodedWidth, size_t encodedHeight, - size_t displayWidth, size_t displayHeight) = 0; - - // Note: These methods are _not_ virtual, it exists as a wrapper around - // the virtual "createRenderer" method above facilitating extraction - // of the ISurface from a regular Surface or a java Surface object. - sp<IOMXRenderer> createRenderer( - const sp<Surface> &surface, - const char *componentName, - OMX_COLOR_FORMATTYPE colorFormat, - size_t encodedWidth, size_t encodedHeight, - size_t displayWidth, size_t displayHeight); - - sp<IOMXRenderer> createRendererFromJavaSurface( - JNIEnv *env, jobject javaSurface, - const char *componentName, - OMX_COLOR_FORMATTYPE colorFormat, - size_t encodedWidth, size_t encodedHeight, - size_t displayWidth, size_t displayHeight); }; struct omx_message { @@ -190,13 +166,6 @@ public: virtual void onMessage(const omx_message &msg) = 0; }; -class IOMXRenderer : public IInterface { -public: - DECLARE_META_INTERFACE(OMXRenderer); - - virtual void render(IOMX::buffer_id buffer) = 0; -}; - //////////////////////////////////////////////////////////////////////////////// class BnOMX : public BnInterface<IOMX> { @@ -213,13 +182,6 @@ public: uint32_t flags = 0); }; -class BnOMXRenderer : public BnInterface<IOMXRenderer> { -public: - virtual status_t onTransact( - uint32_t code, const Parcel &data, Parcel *reply, - uint32_t flags = 0); -}; - } // namespace android #endif // ANDROID_IOMX_H_ diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h index 2d55a55..672931e 100644 --- a/include/media/MediaPlayerInterface.h +++ b/include/media/MediaPlayerInterface.h @@ -106,7 +106,6 @@ public: const KeyedVector<String8, String8> *headers = NULL) = 0; virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0; - virtual status_t setVideoISurface(const sp<ISurface>& surface) = 0; virtual status_t setVideoSurface(const sp<Surface>& surface) = 0; virtual status_t prepare() = 0; virtual status_t prepareAsync() = 0; diff --git a/include/media/PVMediaRecorder.h b/include/media/PVMediaRecorder.h deleted file mode 100644 index 4b44ccc..0000000 --- a/include/media/PVMediaRecorder.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - ** - ** Copyright 2008, 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. - */ - -#ifndef ANDROID_PVMEDIARECORDER_H -#define ANDROID_PVMEDIARECORDER_H - -#include <media/IMediaRecorderClient.h> -#include <media/MediaRecorderBase.h> - -namespace android { - -class Surface; -class ICamera; -class AuthorDriverWrapper; - -class PVMediaRecorder : public MediaRecorderBase { -public: - PVMediaRecorder(); - virtual ~PVMediaRecorder(); - - virtual status_t init(); - virtual status_t setAudioSource(audio_source as); - virtual status_t setVideoSource(video_source vs); - virtual status_t setOutputFormat(output_format of); - virtual status_t setAudioEncoder(audio_encoder ae); - virtual status_t setVideoEncoder(video_encoder ve); - virtual status_t setVideoSize(int width, int height); - virtual status_t setVideoFrameRate(int frames_per_second); - virtual status_t setCamera(const sp<ICamera>& camera); - virtual status_t setPreviewSurface(const sp<Surface>& surface); - virtual status_t setOutputFile(const char *path); - virtual status_t setOutputFile(int fd, int64_t offset, int64_t length); - virtual status_t setParameters(const String8& params); - virtual status_t setListener(const sp<IMediaRecorderClient>& listener); - virtual status_t prepare(); - virtual status_t start(); - virtual status_t stop(); - virtual status_t close(); - virtual status_t reset(); - virtual status_t getMaxAmplitude(int *max); - virtual status_t dump(int fd, const Vector<String16>& args) const; - -private: - status_t doStop(); - - AuthorDriverWrapper* mAuthorDriverWrapper; - - PVMediaRecorder(const PVMediaRecorder &); - PVMediaRecorder &operator=(const PVMediaRecorder &); -}; - -}; // namespace android - -#endif // ANDROID_PVMEDIARECORDER_H diff --git a/include/media/PVMetadataRetriever.h b/include/media/PVMetadataRetriever.h deleted file mode 100644 index c202dfe..0000000 --- a/include/media/PVMetadataRetriever.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -** -** Copyright (C) 2008 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. -*/ - -#ifndef ANDROID_PVMETADATARETRIEVER_H -#define ANDROID_PVMETADATARETRIEVER_H - -#include <utils/Errors.h> -#include <media/MediaMetadataRetrieverInterface.h> -#include <private/media/VideoFrame.h> - -namespace android { - -class MetadataDriver; - -class PVMetadataRetriever : public MediaMetadataRetrieverInterface -{ -public: - PVMetadataRetriever(); - virtual ~PVMetadataRetriever(); - - virtual status_t setDataSource(const char *url); - virtual status_t setDataSource(int fd, int64_t offset, int64_t length); - virtual status_t setMode(int mode); - virtual status_t getMode(int* mode) const; - virtual VideoFrame* captureFrame(); - virtual MediaAlbumArt* extractAlbumArt(); - virtual const char* extractMetadata(int keyCode); - -private: - mutable Mutex mLock; - MetadataDriver* mMetadataDriver; - char* mDataSourcePath; -}; - -}; // namespace android - -#endif // ANDROID_PVMETADATARETRIEVER_H diff --git a/include/media/PVPlayer.h b/include/media/PVPlayer.h deleted file mode 100644 index 657e7a6..0000000 --- a/include/media/PVPlayer.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2008 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. - */ - -#ifndef ANDROID_PVPLAYER_H -#define ANDROID_PVPLAYER_H - -#include <utils/Errors.h> -#include <media/MediaPlayerInterface.h> -#include <media/Metadata.h> - -#define MAX_OPENCORE_INSTANCES 25 - -#ifdef MAX_OPENCORE_INSTANCES -#include <cutils/atomic.h> -#endif - -class PlayerDriver; - -namespace android { - -class PVPlayer : public MediaPlayerInterface -{ -public: - PVPlayer(); - virtual ~PVPlayer(); - - virtual status_t initCheck(); - - virtual status_t setDataSource( - const char *url, const KeyedVector<String8, String8> *headers); - - virtual status_t setDataSource(int fd, int64_t offset, int64_t length); - virtual status_t setVideoISurface(const sp<ISurface>& surface); - virtual status_t setVideoSurface(const sp<Surface>& surface); - virtual status_t prepare(); - virtual status_t prepareAsync(); - virtual status_t start(); - virtual status_t stop(); - virtual status_t pause(); - virtual bool isPlaying(); - virtual status_t seekTo(int msec); - virtual status_t getCurrentPosition(int *msec); - virtual status_t getDuration(int *msec); - virtual status_t reset(); - virtual status_t setLooping(int loop); - virtual player_type playerType() { return PV_PLAYER; } - virtual status_t invoke(const Parcel& request, Parcel *reply); - virtual status_t getMetadata( - const SortedVector<media::Metadata::Type>& ids, - Parcel *records); - - // make available to PlayerDriver - void sendEvent(int msg, int ext1=0, int ext2=0) { MediaPlayerBase::sendEvent(msg, ext1, ext2); } - -private: - static void do_nothing(status_t s, void *cookie, bool cancelled) { } - static void run_init(status_t s, void *cookie, bool cancelled); - static void run_set_video_surface(status_t s, void *cookie, bool cancelled); - static void run_set_audio_output(status_t s, void *cookie, bool cancelled); - static void run_prepare(status_t s, void *cookie, bool cancelled); - static void check_for_live_streaming(status_t s, void *cookie, bool cancelled); - - PlayerDriver* mPlayerDriver; - char * mDataSourcePath; - bool mIsDataSourceSet; - sp<ISurface> mSurface; - int mSharedFd; - status_t mInit; - int mDuration; - -#ifdef MAX_OPENCORE_INSTANCES - static volatile int32_t sNumInstances; -#endif -}; - -}; // namespace android - -#endif // ANDROID_PVPLAYER_H diff --git a/include/media/stagefright/HardwareAPI.h b/include/media/stagefright/HardwareAPI.h index 4fd281b..17908b4 100644 --- a/include/media/stagefright/HardwareAPI.h +++ b/include/media/stagefright/HardwareAPI.h @@ -19,8 +19,6 @@ #define HARDWARE_API_H_ #include <media/stagefright/OMXPluginBase.h> -#include <media/stagefright/VideoRenderer.h> -#include <surfaceflinger/ISurface.h> #include <ui/android_native_buffer.h> #include <utils/RefBase.h> @@ -91,13 +89,6 @@ struct UseAndroidNativeBufferParams { } // namespace android -extern android::VideoRenderer *createRenderer( - const android::sp<android::ISurface> &surface, - const char *componentName, - OMX_COLOR_FORMATTYPE colorFormat, - size_t displayWidth, size_t displayHeight, - size_t decodedWidth, size_t decodedHeight); - extern android::OMXPluginBase *createOMXPlugin(); #endif // HARDWARE_API_H_ diff --git a/include/media/stagefright/VideoRenderer.h b/include/media/stagefright/VideoRenderer.h deleted file mode 100644 index f80b277..0000000 --- a/include/media/stagefright/VideoRenderer.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2009 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. - */ - -#ifndef VIDEO_RENDERER_H_ - -#define VIDEO_RENDERER_H_ - -#include <sys/types.h> - -namespace android { - -class VideoRenderer { -public: - virtual ~VideoRenderer() {} - - virtual void render( - const void *data, size_t size, void *platformPrivate) = 0; - -protected: - VideoRenderer() {} - - VideoRenderer(const VideoRenderer &); - VideoRenderer &operator=(const VideoRenderer &); -}; - -} // namespace android - -#endif // VIDEO_RENDERER_H_ diff --git a/include/utils/String16.h b/include/utils/String16.h index 07a0c11..584f53f 100644 --- a/include/utils/String16.h +++ b/include/utils/String16.h @@ -19,39 +19,12 @@ #include <utils/Errors.h> #include <utils/SharedBuffer.h> - -#include <stdint.h> -#include <sys/types.h> +#include <utils/Unicode.h> // --------------------------------------------------------------------------- extern "C" { -typedef uint16_t char16_t; - -// Standard string functions on char16 strings. -int strcmp16(const char16_t *, const char16_t *); -int strncmp16(const char16_t *s1, const char16_t *s2, size_t n); -size_t strlen16(const char16_t *); -size_t strnlen16(const char16_t *, size_t); -char16_t *strcpy16(char16_t *, const char16_t *); -char16_t *strncpy16(char16_t *, const char16_t *, size_t); - -// Version of comparison that supports embedded nulls. -// This is different than strncmp() because we don't stop -// at a nul character and consider the strings to be different -// if the lengths are different (thus we need to supply the -// lengths of both strings). This can also be used when -// your string is not nul-terminated as it will have the -// equivalent result as strcmp16 (unlike strncmp16). -int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2); - -// Version of strzcmp16 for comparing strings in different endianness. -int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2); - -// Convert UTF-8 to UTF-16 including surrogate pairs -void utf8_to_utf16(const uint8_t *src, size_t srcLen, char16_t* dst, const size_t dstLen); - } // --------------------------------------------------------------------------- diff --git a/include/utils/String8.h b/include/utils/String8.h index cef8eca..b36f128 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -18,122 +18,16 @@ #define ANDROID_STRING8_H #include <utils/Errors.h> +#include <utils/SharedBuffer.h> +#include <utils/Unicode.h> -// Need this for the char16_t type; String8.h should not -// be depedent on the String16 class. -#include <utils/String16.h> - -#include <stdint.h> -#include <string.h> -#include <sys/types.h> - -// --------------------------------------------------------------------------- - -extern "C" { - -typedef uint32_t char32_t; - -size_t strlen32(const char32_t *); -size_t strnlen32(const char32_t *, size_t); - -/* - * Returns the length of "src" when "src" is valid UTF-8 string. - * Returns 0 if src is NULL, 0-length string or non UTF-8 string. - * This function should be used to determine whether "src" is valid UTF-8 - * characters with valid unicode codepoints. "src" must be null-terminated. - * - * If you are going to use other GetUtf... functions defined in this header - * with string which may not be valid UTF-8 with valid codepoint (form 0 to - * 0x10FFFF), you should use this function before calling others, since the - * other functions do not check whether the string is valid UTF-8 or not. - * - * If you do not care whether "src" is valid UTF-8 or not, you should use - * strlen() as usual, which should be much faster. - */ -size_t utf8_length(const char *src); - -/* - * Returns the UTF-32 length of "src". - */ -size_t utf32_length(const char *src, size_t src_len); - -/* - * Returns the UTF-8 length of "src". - */ -size_t utf8_length_from_utf16(const char16_t *src, size_t src_len); - -/* - * Returns the UTF-8 length of "src". - */ -size_t utf8_length_from_utf32(const char32_t *src, size_t src_len); - -/* - * Returns the unicode value at "index". - * Returns -1 when the index is invalid (equals to or more than "src_len"). - * If returned value is positive, it is able to be converted to char32_t, which - * is unsigned. Then, if "next_index" is not NULL, the next index to be used is - * stored in "next_index". "next_index" can be NULL. - */ -int32_t utf32_at(const char *src, size_t src_len, - size_t index, size_t *next_index); - -/* - * Stores a UTF-32 string converted from "src" in "dst", if "dst_length" is not - * large enough to store the string, the part of the "src" string is stored - * into "dst". - * Returns the size actually used for storing the string. - * "dst" is not null-terminated when dst_len is fully used (like strncpy). - */ -size_t utf8_to_utf32(const char* src, size_t src_len, - char32_t* dst, size_t dst_len); - -/* - * Stores a UTF-8 string converted from "src" in "dst", if "dst_length" is not - * large enough to store the string, the part of the "src" string is stored - * into "dst" as much as possible. See the examples for more detail. - * Returns the size actually used for storing the string. - * dst" is not null-terminated when dst_len is fully used (like strncpy). - * - * Example 1 - * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) - * "src_len" == 2 - * "dst_len" >= 7 - * -> - * Returned value == 6 - * "dst" becomes \xE3\x81\x82\xE3\x81\x84\0 - * (note that "dst" is null-terminated) - * - * Example 2 - * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) - * "src_len" == 2 - * "dst_len" == 5 - * -> - * Returned value == 3 - * "dst" becomes \xE3\x81\x82\0 - * (note that "dst" is null-terminated, but \u3044 is not stored in "dst" - * since "dst" does not have enough size to store the character) - * - * Example 3 - * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) - * "src_len" == 2 - * "dst_len" == 6 - * -> - * Returned value == 6 - * "dst" becomes \xE3\x81\x82\xE3\x81\x84 - * (note that "dst" is NOT null-terminated, like strncpy) - */ -size_t utf32_to_utf8(const char32_t* src, size_t src_len, - char* dst, size_t dst_len); - -size_t utf16_to_utf8(const char16_t* src, size_t src_len, - char* dst, size_t dst_len); - -} +#include <string.h> // for strcmp // --------------------------------------------------------------------------- namespace android { +class String16; class TextOutput; //! This is a string holding UTF-8 characters. Does not allow the value more @@ -182,7 +76,7 @@ public: size_t getUtf32Length() const; int32_t getUtf32At(size_t index, size_t *next_index) const; - size_t getUtf32(char32_t* dst, size_t dst_len) const; + void getUtf32(char32_t* dst) const; inline String8& operator=(const String8& other); inline String8& operator=(const char* other); diff --git a/include/utils/Unicode.h b/include/utils/Unicode.h new file mode 100644 index 0000000..6afb291 --- /dev/null +++ b/include/utils/Unicode.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_UNICODE_H +#define ANDROID_UNICODE_H + +#include <sys/types.h> +#include <stdint.h> + +extern "C" { + +typedef uint32_t char32_t; +typedef uint16_t char16_t; + +// Standard string functions on char16_t strings. +int strcmp16(const char16_t *, const char16_t *); +int strncmp16(const char16_t *s1, const char16_t *s2, size_t n); +size_t strlen16(const char16_t *); +size_t strnlen16(const char16_t *, size_t); +char16_t *strcpy16(char16_t *, const char16_t *); +char16_t *strncpy16(char16_t *, const char16_t *, size_t); + +// Version of comparison that supports embedded nulls. +// This is different than strncmp() because we don't stop +// at a nul character and consider the strings to be different +// if the lengths are different (thus we need to supply the +// lengths of both strings). This can also be used when +// your string is not nul-terminated as it will have the +// equivalent result as strcmp16 (unlike strncmp16). +int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2); + +// Version of strzcmp16 for comparing strings in different endianness. +int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2); + +// Standard string functions on char32_t strings. +size_t strlen32(const char32_t *); +size_t strnlen32(const char32_t *, size_t); + +/** + * Measure the length of a UTF-32 string in UTF-8. If the string is invalid + * such as containing a surrogate character, -1 will be returned. + */ +ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len); + +/** + * Stores a UTF-8 string converted from "src" in "dst", if "dst_length" is not + * large enough to store the string, the part of the "src" string is stored + * into "dst" as much as possible. See the examples for more detail. + * Returns the size actually used for storing the string. + * dst" is not null-terminated when dst_len is fully used (like strncpy). + * + * Example 1 + * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) + * "src_len" == 2 + * "dst_len" >= 7 + * -> + * Returned value == 6 + * "dst" becomes \xE3\x81\x82\xE3\x81\x84\0 + * (note that "dst" is null-terminated) + * + * Example 2 + * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) + * "src_len" == 2 + * "dst_len" == 5 + * -> + * Returned value == 3 + * "dst" becomes \xE3\x81\x82\0 + * (note that "dst" is null-terminated, but \u3044 is not stored in "dst" + * since "dst" does not have enough size to store the character) + * + * Example 3 + * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) + * "src_len" == 2 + * "dst_len" == 6 + * -> + * Returned value == 6 + * "dst" becomes \xE3\x81\x82\xE3\x81\x84 + * (note that "dst" is NOT null-terminated, like strncpy) + */ +void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst); + +/** + * Returns the unicode value at "index". + * Returns -1 when the index is invalid (equals to or more than "src_len"). + * If returned value is positive, it is able to be converted to char32_t, which + * is unsigned. Then, if "next_index" is not NULL, the next index to be used is + * stored in "next_index". "next_index" can be NULL. + */ +int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index); + + +/** + * Returns the UTF-8 length of UTF-16 string "src". + */ +ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len); + +/** + * Converts a UTF-16 string to UTF-8. The destination buffer must be large + * enough to fit the UTF-16 as measured by utf16_to_utf8_length with an added + * NULL terminator. + */ +void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst); + +/** + * Returns the length of "src" when "src" is valid UTF-8 string. + * Returns 0 if src is NULL or 0-length string. Returns -1 when the source + * is an invalid string. + * + * This function should be used to determine whether "src" is valid UTF-8 + * characters with valid unicode codepoints. "src" must be null-terminated. + * + * If you are going to use other utf8_to_... functions defined in this header + * with string which may not be valid UTF-8 with valid codepoint (form 0 to + * 0x10FFFF), you should use this function before calling others, since the + * other functions do not check whether the string is valid UTF-8 or not. + * + * If you do not care whether "src" is valid UTF-8 or not, you should use + * strlen() as usual, which should be much faster. + */ +ssize_t utf8_length(const char *src); + +/** + * Measure the length of a UTF-32 string. + */ +size_t utf8_to_utf32_length(const char *src, size_t src_len); + +/** + * Stores a UTF-32 string converted from "src" in "dst". "dst" must be large + * enough to store the entire converted string as measured by + * utf8_to_utf32_length plus space for a NULL terminator. + */ +void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst); + +/** + * Returns the UTF-16 length of UTF-8 string "src". + */ +ssize_t utf8_to_utf16_length(const uint8_t* src, size_t srcLen); + +/** + * Convert UTF-8 to UTF-16 including surrogate pairs. The destination buffer + * must be large enough to hold the result as measured by utf8_to_utf16_length + * plus an added NULL terminator. + */ +void utf8_to_utf16(const uint8_t* src, size_t srcLen, char16_t* dst); + +} + +#endif diff --git a/libs/camera/Camera.cpp b/libs/camera/Camera.cpp index 148e864..450971d 100644 --- a/libs/camera/Camera.cpp +++ b/libs/camera/Camera.cpp @@ -372,6 +372,9 @@ void Camera::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp< } if (listener != NULL) { listener->postDataTimestamp(timestamp, msgType, dataPtr); + } else { + LOGW("No listener was set. Drop a recording frame."); + releaseRecordingFrame(dataPtr); } } diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs index 698540b..3e81115 100644 --- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs +++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs @@ -64,7 +64,7 @@ static void computeGaussianWeights() { static void copyInput() { - rs_allocation ain = {0}; + rs_allocation ain; rsSetObject(&ain,rsGetAllocation(InPixel)); uint32_t dimx = rsAllocationGetDimX(ain); uint32_t dimy = rsAllocationGetDimY(ain); @@ -73,7 +73,6 @@ static void copyInput() { ScratchPixel1[x + y * dimx] = convert_float4(InPixel[x + y * dimx]); } } - rsClearObject(&ain); } void filter() { diff --git a/libs/rs/java/Samples/src/com/android/samples/rslist.rs b/libs/rs/java/Samples/src/com/android/samples/rslist.rs index f29276a..0baccb8 100644 --- a/libs/rs/java/Samples/src/com/android/samples/rslist.rs +++ b/libs/rs/java/Samples/src/com/android/samples/rslist.rs @@ -46,7 +46,7 @@ int root(int launchID) { rsgBindFont(gItalic); color(0.2, 0.2, 0.2, 0); - rs_allocation listAlloc = {0}; + rs_allocation listAlloc; rsSetObject(&listAlloc, rsGetAllocation(gList)); int allocSize = rsAllocationGetDimX(listAlloc); @@ -67,7 +67,6 @@ int root(int launchID) { } currentYPos += itemHeight; } - rsClearObject(&listAlloc); return 10; } diff --git a/libs/rs/java/tests/src/com/android/rs/test/rslist.rs b/libs/rs/java/tests/src/com/android/rs/test/rslist.rs index d1fde57..f354a72 100644 --- a/libs/rs/java/tests/src/com/android/rs/test/rslist.rs +++ b/libs/rs/java/tests/src/com/android/rs/test/rslist.rs @@ -47,7 +47,7 @@ int root(int launchID) { rsgBindFont(gFont); color(0.2, 0.2, 0.2, 0); - rs_allocation listAlloc = {0}; + rs_allocation listAlloc; rsSetObject(&listAlloc, rsGetAllocation(gList)); int allocSize = rsAllocationGetDimX(listAlloc); @@ -103,7 +103,6 @@ int root(int launchID) { } currentYPos += itemHeight; } - rsClearObject(&listAlloc); return 10; } diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp index 33b0f51..96e350d 100644 --- a/libs/rs/rsFont.cpp +++ b/libs/rs/rsFont.cpp @@ -174,7 +174,7 @@ void Font::renderUTF(const char *text, uint32_t len, int32_t x, int32_t y, while (glyphsLeft > 0) { - int32_t utfChar = utf32_at(text, len, index, &nextIndex); + int32_t utfChar = utf32_from_utf8_at(text, len, index, &nextIndex); // Reached the end of the string or encountered if (utfChar < 0) { diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp index 033f316..072cc16 100644 --- a/libs/rs/rsScriptC.cpp +++ b/libs/rs/rsScriptC.cpp @@ -65,12 +65,11 @@ void ScriptC::setupScript(Context *rsc) { void **dest = ((void ***)mEnviroment.mFieldAddress)[ct]; if (rsc->props.mLogScripts) { - LOGV("%p ScriptC::setupScript slot=%i dst=%p src=%p type=%p", rsc, ct, dest, ptr, mSlots[ct]->getType()); - - //const uint32_t *p32 = (const uint32_t *)ptr; - //for (uint32_t ct2=0; ct2 < mSlots[ct]->getType()->getDimX(); ct2++) { - //LOGE(" %i = 0x%08x ", ct2, p32[ct2]); - //} + if (mSlots[ct].get() != NULL) { + LOGV("%p ScriptC::setupScript slot=%i dst=%p src=%p type=%p", rsc, ct, dest, ptr, mSlots[ct]->getType()); + } else { + LOGV("%p ScriptC::setupScript slot=%i dst=%p src=%p type=null", rsc, ct, dest, ptr); + } } if (dest) { diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp index ab8d9d7..7e4a247 100644 --- a/libs/rs/rsScriptC_Lib.cpp +++ b/libs/rs/rsScriptC_Lib.cpp @@ -345,6 +345,10 @@ int SC_divsi3(int a, int b) { return a / b; } +int SC_modsi3(int a, int b) { + return a % b; +} + int SC_getAllocation(const void *ptr) { GET_TLS(); const Allocation *alloc = sc->ptrToAllocation(ptr); @@ -402,6 +406,7 @@ void SC_ForEach2(RsScript vs, static ScriptCState::SymbolTable_t gSyms[] = { { "__divsi3", (void *)&SC_divsi3, true }, + { "__modsi3", (void *)&SC_modsi3, true }, // allocation { "_Z19rsAllocationGetDimX13rs_allocation", (void *)&SC_allocGetDimX, true }, @@ -528,4 +533,3 @@ const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbol(const char *sym) } return NULL; } - diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index eb75ed8..05a9674 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -41,6 +41,7 @@ commonSources:= \ TextOutput.cpp \ Threads.cpp \ Timers.cpp \ + Unicode.cpp \ VectorImpl.cpp \ ZipFileCRO.cpp \ ZipFileRO.cpp \ diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index f287298..bbf5093 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -444,15 +444,51 @@ void ResStringPool::uninit() } } -#define DECODE_LENGTH(str, chrsz, len) \ - len = *(str); \ - if (*(str)&(1<<(chrsz*8-1))) { \ - (str)++; \ - len = (((len)&((1<<(chrsz*8-1))-1))<<(chrsz*8)) + *(str); \ - } \ - (str)++; - -const uint16_t* ResStringPool::stringAt(size_t idx, size_t* outLen) const +/** + * Strings in UTF-16 format have length indicated by a length encoded in the + * stored data. It is either 1 or 2 characters of length data. This allows a + * maximum length of 0x7FFFFFF (2147483647 bytes), but if you're storing that + * much data in a string, you're abusing them. + * + * If the high bit is set, then there are two characters or 4 bytes of length + * data encoded. In that case, drop the high bit of the first character and + * add it together with the next character. + */ +static inline size_t +decodeLength(const char16_t** str) +{ + size_t len = **str; + if ((len & 0x8000) != 0) { + (*str)++; + len = ((len & 0x7FFF) << 16) | **str; + } + (*str)++; + return len; +} + +/** + * Strings in UTF-8 format have length indicated by a length encoded in the + * stored data. It is either 1 or 2 characters of length data. This allows a + * maximum length of 0x7FFF (32767 bytes), but you should consider storing + * text in another way if you're using that much data in a single string. + * + * If the high bit is set, then there are two characters or 2 bytes of length + * data encoded. In that case, drop the high bit of the first character and + * add it together with the next character. + */ +static inline size_t +decodeLength(const uint8_t** str) +{ + size_t len = **str; + if ((len & 0x80) != 0) { + (*str)++; + len = ((len & 0x7F) << 8) | **str; + } + (*str)++; + return len; +} + +const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const { if (mError == NO_ERROR && idx < mHeader->stringCount) { const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0; @@ -461,37 +497,51 @@ const uint16_t* ResStringPool::stringAt(size_t idx, size_t* outLen) const if (!isUTF8) { const char16_t* strings = (char16_t*)mStrings; const char16_t* str = strings+off; - DECODE_LENGTH(str, sizeof(char16_t), *outLen) - if ((uint32_t)(str+*outLen-strings) < mStringPoolSize) { + + *u16len = decodeLength(&str); + if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) { return str; } else { LOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+*outLen-strings), (int)mStringPoolSize); + (int)idx, (int)(str+*u16len-strings), (int)mStringPoolSize); } } else { const uint8_t* strings = (uint8_t*)mStrings; - const uint8_t* str = strings+off; - DECODE_LENGTH(str, sizeof(uint8_t), *outLen) - size_t encLen; - DECODE_LENGTH(str, sizeof(uint8_t), encLen) - if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { + const uint8_t* u8str = strings+off; + + *u16len = decodeLength(&u8str); + size_t u8len = decodeLength(&u8str); + + // encLen must be less than 0x7FFF due to encoding. + if ((uint32_t)(u8str+u8len-strings) < mStringPoolSize) { AutoMutex lock(mDecodeLock); + if (mCache[idx] != NULL) { return mCache[idx]; } - char16_t *u16str = (char16_t *)calloc(*outLen+1, sizeof(char16_t)); + + ssize_t actualLen = utf8_to_utf16_length(u8str, u8len); + if (actualLen < 0 || (size_t)actualLen != *u16len) { + LOGW("Bad string block: string #%lld decoded length is not correct " + "%lld vs %llu\n", + (long long)idx, (long long)actualLen, (long long)*u16len); + return NULL; + } + + char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t)); if (!u16str) { LOGW("No memory when trying to allocate decode cache for string #%d\n", (int)idx); return NULL; } - const unsigned char *u8src = reinterpret_cast<const unsigned char *>(str); - utf8_to_utf16(u8src, encLen, u16str, *outLen); + + utf8_to_utf16(u8str, u8len, u16str); mCache[idx] = u16str; return u16str; } else { - LOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); + LOGW("Bad string block: string #%lld extends to %lld, past end at %lld\n", + (long long)idx, (long long)(u8str+u8len-strings), + (long long)mStringPoolSize); } } } else { @@ -512,9 +562,8 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const if (isUTF8) { const uint8_t* strings = (uint8_t*)mStrings; const uint8_t* str = strings+off; - DECODE_LENGTH(str, sizeof(uint8_t), *outLen) - size_t encLen; - DECODE_LENGTH(str, sizeof(uint8_t), encLen) + *outLen = decodeLength(&str); + size_t encLen = decodeLength(&str); if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { return (const char*)str; } else { diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp index eab7b2b..4ce1664 100644 --- a/libs/utils/String16.cpp +++ b/libs/utils/String16.cpp @@ -18,228 +18,17 @@ #include <utils/Debug.h> #include <utils/Log.h> +#include <utils/Unicode.h> #include <utils/String8.h> #include <utils/TextOutput.h> #include <utils/threads.h> #include <private/utils/Static.h> -#ifdef HAVE_WINSOCK -# undef nhtol -# undef htonl -# undef nhtos -# undef htons - -# ifdef HAVE_LITTLE_ENDIAN -# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) -# define htonl(x) ntohl(x) -# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) -# define htons(x) ntohs(x) -# else -# define ntohl(x) (x) -# define htonl(x) (x) -# define ntohs(x) (x) -# define htons(x) (x) -# endif -#else -# include <netinet/in.h> -#endif - #include <memory.h> #include <stdio.h> #include <ctype.h> -// --------------------------------------------------------------------------- - -int strcmp16(const char16_t *s1, const char16_t *s2) -{ - char16_t ch; - int d = 0; - - while ( 1 ) { - d = (int)(ch = *s1++) - (int)*s2++; - if ( d || !ch ) - break; - } - - return d; -} - -int strncmp16(const char16_t *s1, const char16_t *s2, size_t n) -{ - char16_t ch; - int d = 0; - - while ( n-- ) { - d = (int)(ch = *s1++) - (int)*s2++; - if ( d || !ch ) - break; - } - - return d; -} - -char16_t *strcpy16(char16_t *dst, const char16_t *src) -{ - char16_t *q = dst; - const char16_t *p = src; - char16_t ch; - - do { - *q++ = ch = *p++; - } while ( ch ); - - return dst; -} - -size_t strlen16(const char16_t *s) -{ - const char16_t *ss = s; - while ( *ss ) - ss++; - return ss-s; -} - - -char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n) -{ - char16_t *q = dst; - const char16_t *p = src; - char ch; - - while (n) { - n--; - *q++ = ch = *p++; - if ( !ch ) - break; - } - - *q = 0; - - return dst; -} - -size_t strnlen16(const char16_t *s, size_t maxlen) -{ - const char16_t *ss = s; - - /* Important: the maxlen test must precede the reference through ss; - since the byte beyond the maximum may segfault */ - while ((maxlen > 0) && *ss) { - ss++; - maxlen--; - } - return ss-s; -} - -int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2) -{ - const char16_t* e1 = s1+n1; - const char16_t* e2 = s2+n2; - - while (s1 < e1 && s2 < e2) { - const int d = (int)*s1++ - (int)*s2++; - if (d) { - return d; - } - } - - return n1 < n2 - ? (0 - (int)*s2) - : (n1 > n2 - ? ((int)*s1 - 0) - : 0); -} - -int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2) -{ - const char16_t* e1 = s1H+n1; - const char16_t* e2 = s2N+n2; - - while (s1H < e1 && s2N < e2) { - const char16_t c2 = ntohs(*s2N); - const int d = (int)*s1H++ - (int)c2; - s2N++; - if (d) { - return d; - } - } - - return n1 < n2 - ? (0 - (int)ntohs(*s2N)) - : (n1 > n2 - ? ((int)*s1H - 0) - : 0); -} - -static inline size_t -utf8_char_len(uint8_t ch) -{ - return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1; -} - -#define UTF8_SHIFT_AND_MASK(unicode, byte) (unicode)<<=6; (unicode) |= (0x3f & (byte)); - -static inline uint32_t -utf8_to_utf32(const uint8_t *src, size_t length) -{ - uint32_t unicode; - - switch (length) - { - case 1: - return src[0]; - case 2: - unicode = src[0] & 0x1f; - UTF8_SHIFT_AND_MASK(unicode, src[1]) - return unicode; - case 3: - unicode = src[0] & 0x0f; - UTF8_SHIFT_AND_MASK(unicode, src[1]) - UTF8_SHIFT_AND_MASK(unicode, src[2]) - return unicode; - case 4: - unicode = src[0] & 0x07; - UTF8_SHIFT_AND_MASK(unicode, src[1]) - UTF8_SHIFT_AND_MASK(unicode, src[2]) - UTF8_SHIFT_AND_MASK(unicode, src[3]) - return unicode; - default: - return 0xffff; - } - - //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result); -} - -void -utf8_to_utf16(const uint8_t *src, size_t srcLen, - char16_t* dst, const size_t dstLen) -{ - const uint8_t* const end = src + srcLen; - const char16_t* const dstEnd = dst + dstLen; - while (src < end && dst < dstEnd) { - size_t len = utf8_char_len(*src); - uint32_t codepoint = utf8_to_utf32((const uint8_t*)src, len); - - // Convert the UTF32 codepoint to one or more UTF16 codepoints - if (codepoint <= 0xFFFF) { - // Single UTF16 character - *dst++ = (char16_t) codepoint; - } else { - // Multiple UTF16 characters with surrogates - codepoint = codepoint - 0x10000; - *dst++ = (char16_t) ((codepoint >> 10) + 0xD800); - *dst++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); - } - - src += len; - } - if (dst < dstEnd) { - *dst = 0; - } -} - -// --------------------------------------------------------------------------- namespace android { @@ -270,37 +59,33 @@ void terminate_string16() // --------------------------------------------------------------------------- -static char16_t* allocFromUTF8(const char* in, size_t len) +static char16_t* allocFromUTF8(const char* u8str, size_t u8len) { - if (len == 0) return getEmptyString(); - - size_t chars = 0; - const char* end = in+len; - const char* p = in; - - while (p < end) { - chars++; - int utf8len = utf8_char_len(*p); - uint32_t codepoint = utf8_to_utf32((const uint8_t*)p, utf8len); - if (codepoint > 0xFFFF) chars++; // this will be a surrogate pair in utf16 - p += utf8len; + if (u8len == 0) return getEmptyString(); + + const uint8_t* u8cur = (const uint8_t*) u8str; + + const ssize_t u16len = utf8_to_utf16_length(u8cur, u8len); + if (u16len < 0) { + return getEmptyString(); } - - size_t bufSize = (chars+1)*sizeof(char16_t); - SharedBuffer* buf = SharedBuffer::alloc(bufSize); + + const uint8_t* const u8end = u8cur + u8len; + + SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)*(u16len+1)); if (buf) { - p = in; - char16_t* str = (char16_t*)buf->data(); - - utf8_to_utf16((const uint8_t*)p, len, str, bufSize); + u8cur = (const uint8_t*) u8str; + char16_t* u16str = (char16_t*)buf->data(); + + utf8_to_utf16(u8cur, u8len, u16str); //printf("Created UTF-16 string from UTF-8 \"%s\":", in); //printHexData(1, str, buf->size(), 16, 1); //printf("\n"); - return str; + return u16str; } - + return getEmptyString(); } diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index 6358fc4..c8dc083 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -17,6 +17,8 @@ #include <utils/String8.h> #include <utils/Log.h> +#include <utils/Unicode.h> +#include <utils/SharedBuffer.h> #include <utils/String16.h> #include <utils/TextOutput.h> #include <utils/threads.h> @@ -34,94 +36,10 @@ namespace android { -static const char32_t kByteMask = 0x000000BF; -static const char32_t kByteMark = 0x00000080; - -// Surrogates aren't valid for UTF-32 characters, so define some -// constants that will let us screen them out. -static const char32_t kUnicodeSurrogateHighStart = 0x0000D800; -static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; -static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00; -static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; -static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; -static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; -static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF; - -// Mask used to set appropriate bits in first byte of UTF-8 sequence, -// indexed by number of bytes in the sequence. -// 0xxxxxxx -// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000 -// 110yyyyx 10xxxxxx -// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0 -// 1110yyyy 10yxxxxx 10xxxxxx -// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0 -// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx -// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0 -static const char32_t kFirstByteMark[] = { - 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 -}; - // Separator used by resource paths. This is not platform dependent contrary // to OS_PATH_SEPARATOR. #define RES_PATH_SEPARATOR '/' -// Return number of utf8 bytes required for the character. -static size_t utf32_to_utf8_bytes(char32_t srcChar) -{ - size_t bytesToWrite; - - // Figure out how many bytes the result will require. - if (srcChar < 0x00000080) - { - bytesToWrite = 1; - } - else if (srcChar < 0x00000800) - { - bytesToWrite = 2; - } - else if (srcChar < 0x00010000) - { - if ((srcChar < kUnicodeSurrogateStart) - || (srcChar > kUnicodeSurrogateEnd)) - { - bytesToWrite = 3; - } - else - { - // Surrogates are invalid UTF-32 characters. - return 0; - } - } - // Max code point for Unicode is 0x0010FFFF. - else if (srcChar <= kUnicodeMaxCodepoint) - { - bytesToWrite = 4; - } - else - { - // Invalid UTF-32 character. - return 0; - } - - return bytesToWrite; -} - -// Write out the source character to <dstP>. - -static void utf32_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes) -{ - dstP += bytes; - switch (bytes) - { /* note: everything falls through. */ - case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]); - } -} - -// --------------------------------------------------------------------------- - static SharedBuffer* gEmptyStringBuf = NULL; static char* gEmptyString = NULL; @@ -175,62 +93,47 @@ static char* allocFromUTF8(const char* in, size_t len) return getEmptyString(); } -template<typename T, typename L> -static char* allocFromUTF16OrUTF32(const T* in, L len) +static char* allocFromUTF16(const char16_t* in, size_t len) { if (len == 0) return getEmptyString(); - size_t bytes = 0; - const T* end = in+len; - const T* p = in; - - while (p < end) { - bytes += utf32_to_utf8_bytes(*p); - p++; + const ssize_t bytes = utf16_to_utf8_length(in, len); + if (bytes < 0) { + return getEmptyString(); } SharedBuffer* buf = SharedBuffer::alloc(bytes+1); LOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - p = in; - char* str = (char*)buf->data(); - char* d = str; - while (p < end) { - const T c = *p++; - size_t len = utf32_to_utf8_bytes(c); - utf32_to_utf8((uint8_t*)d, c, len); - d += len; - } - *d = 0; - - return str; + if (!buf) { + return getEmptyString(); } - return getEmptyString(); + char* str = (char*)buf->data(); + utf16_to_utf8(in, len, str); + return str; } -static char* allocFromUTF16(const char16_t* in, size_t len) +static char* allocFromUTF32(const char32_t* in, size_t len) { - if (len == 0) return getEmptyString(); + if (len == 0) { + return getEmptyString(); + } - const size_t bytes = utf8_length_from_utf16(in, len); + const ssize_t bytes = utf32_to_utf8_length(in, len); + if (bytes < 0) { + return getEmptyString(); + } SharedBuffer* buf = SharedBuffer::alloc(bytes+1); LOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - char* str = (char*)buf->data(); - - utf16_to_utf8(in, len, str, bytes+1); - - return str; + if (!buf) { + return getEmptyString(); } - return getEmptyString(); -} + char* str = (char*) buf->data(); + utf32_to_utf8(in, len, str); -static char* allocFromUTF32(const char32_t* in, size_t len) -{ - return allocFromUTF16OrUTF32<char32_t, size_t>(in, len); + return str; } // --------------------------------------------------------------------------- @@ -510,17 +413,17 @@ void String8::toUpper(size_t start, size_t length) size_t String8::getUtf32Length() const { - return utf32_length(mString, length()); + return utf8_to_utf32_length(mString, length()); } int32_t String8::getUtf32At(size_t index, size_t *next_index) const { - return utf32_at(mString, length(), index, next_index); + return utf32_from_utf8_at(mString, length(), index, next_index); } -size_t String8::getUtf32(char32_t* dst, size_t dst_len) const +void String8::getUtf32(char32_t* dst) const { - return utf8_to_utf32(mString, length(), dst, dst_len); + utf8_to_utf32(mString, length(), dst); } TextOutput& operator<<(TextOutput& to, const String8& val) @@ -705,241 +608,3 @@ String8& String8::convertToResPath() } }; // namespace android - -// --------------------------------------------------------------------------- - -size_t strlen32(const char32_t *s) -{ - const char32_t *ss = s; - while ( *ss ) - ss++; - return ss-s; -} - -size_t strnlen32(const char32_t *s, size_t maxlen) -{ - const char32_t *ss = s; - while ((maxlen > 0) && *ss) { - ss++; - maxlen--; - } - return ss-s; -} - -size_t utf8_length(const char *src) -{ - const char *cur = src; - size_t ret = 0; - while (*cur != '\0') { - const char first_char = *cur++; - if ((first_char & 0x80) == 0) { // ASCII - ret += 1; - continue; - } - // (UTF-8's character must not be like 10xxxxxx, - // but 110xxxxx, 1110xxxx, ... or 1111110x) - if ((first_char & 0x40) == 0) { - return 0; - } - - int32_t mask, to_ignore_mask; - size_t num_to_read = 0; - char32_t utf32 = 0; - for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80; - num_to_read < 5 && (first_char & mask); - num_to_read++, to_ignore_mask |= mask, mask >>= 1) { - if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx - return 0; - } - // 0x3F == 00111111 - utf32 = (utf32 << 6) + (*cur++ & 0x3F); - } - // "first_char" must be (110xxxxx - 11110xxx) - if (num_to_read == 5) { - return 0; - } - to_ignore_mask |= mask; - utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1)); - if (utf32 > android::kUnicodeMaxCodepoint) { - return 0; - } - - ret += num_to_read; - } - return ret; -} - -size_t utf32_length(const char *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return 0; - } - size_t ret = 0; - const char* cur; - const char* end; - size_t num_to_skip; - for (cur = src, end = src + src_len, num_to_skip = 1; - cur < end; - cur += num_to_skip, ret++) { - const char first_char = *cur; - num_to_skip = 1; - if ((first_char & 0x80) == 0) { // ASCII - continue; - } - int32_t mask; - - for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) { - } - } - return ret; -} - -size_t utf8_length_from_utf32(const char32_t *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return 0; - } - size_t ret = 0; - const char32_t *end = src + src_len; - while (src < end) { - ret += android::utf32_to_utf8_bytes(*src++); - } - return ret; -} - -size_t utf8_length_from_utf16(const char16_t *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return 0; - } - size_t ret = 0; - const char16_t* const end = src + src_len; - while (src < end) { - if ((*src & 0xFC00) == 0xD800 && (src + 1) < end - && (*++src & 0xFC00) == 0xDC00) { - // surrogate pairs are always 4 bytes. - ret += 4; - src++; - } else { - ret += android::utf32_to_utf8_bytes((char32_t) *src++); - } - } - return ret; -} - -static int32_t utf32_at_internal(const char* cur, size_t *num_read) -{ - const char first_char = *cur; - if ((first_char & 0x80) == 0) { // ASCII - *num_read = 1; - return *cur; - } - cur++; - char32_t mask, to_ignore_mask; - size_t num_to_read = 0; - char32_t utf32 = first_char; - for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80; - (first_char & mask); - num_to_read++, to_ignore_mask |= mask, mask >>= 1) { - // 0x3F == 00111111 - utf32 = (utf32 << 6) + (*cur++ & 0x3F); - } - to_ignore_mask |= mask; - utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1))); - - *num_read = num_to_read; - return static_cast<int32_t>(utf32); -} - -int32_t utf32_at(const char *src, size_t src_len, - size_t index, size_t *next_index) -{ - if (index >= src_len) { - return -1; - } - size_t dummy_index; - if (next_index == NULL) { - next_index = &dummy_index; - } - size_t num_read; - int32_t ret = utf32_at_internal(src + index, &num_read); - if (ret >= 0) { - *next_index = index + num_read; - } - - return ret; -} - -size_t utf8_to_utf32(const char* src, size_t src_len, - char32_t* dst, size_t dst_len) -{ - if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) { - return 0; - } - - const char* cur = src; - const char* end = src + src_len; - char32_t* cur_utf32 = dst; - const char32_t* end_utf32 = dst + dst_len; - while (cur_utf32 < end_utf32 && cur < end) { - size_t num_read; - *cur_utf32++ = - static_cast<char32_t>(utf32_at_internal(cur, &num_read)); - cur += num_read; - } - if (cur_utf32 < end_utf32) { - *cur_utf32 = 0; - } - return static_cast<size_t>(cur_utf32 - dst); -} - -size_t utf32_to_utf8(const char32_t* src, size_t src_len, - char* dst, size_t dst_len) -{ - if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) { - return 0; - } - const char32_t *cur_utf32 = src; - const char32_t *end_utf32 = src + src_len; - char *cur = dst; - const char *end = dst + dst_len; - while (cur_utf32 < end_utf32 && cur < end) { - size_t len = android::utf32_to_utf8_bytes(*cur_utf32); - android::utf32_to_utf8((uint8_t *)cur, *cur_utf32++, len); - cur += len; - } - if (cur < end) { - *cur = '\0'; - } - return cur - dst; -} - -size_t utf16_to_utf8(const char16_t* src, size_t src_len, - char* dst, size_t dst_len) -{ - if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) { - return 0; - } - const char16_t* cur_utf16 = src; - const char16_t* const end_utf16 = src + src_len; - char *cur = dst; - const char* const end = dst + dst_len; - while (cur_utf16 < end_utf16 && cur < end) { - char32_t utf32; - // surrogate pairs - if ((*cur_utf16 & 0xFC00) == 0xD800 && (cur_utf16 + 1) < end_utf16) { - utf32 = (*cur_utf16++ - 0xD800) << 10; - utf32 |= *cur_utf16++ - 0xDC00; - utf32 += 0x10000; - } else { - utf32 = (char32_t) *cur_utf16++; - } - size_t len = android::utf32_to_utf8_bytes(utf32); - android::utf32_to_utf8((uint8_t*)cur, utf32, len); - cur += len; - } - if (cur < end) { - *cur = '\0'; - } - return cur - dst; -} diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp new file mode 100644 index 0000000..78c61b4 --- /dev/null +++ b/libs/utils/Unicode.cpp @@ -0,0 +1,575 @@ +/* + * Copyright (C) 2005 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. + */ + +#include <utils/Unicode.h> + +#include <stddef.h> + +#ifdef HAVE_WINSOCK +# undef nhtol +# undef htonl +# undef nhtos +# undef htons + +# ifdef HAVE_LITTLE_ENDIAN +# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) +# define htonl(x) ntohl(x) +# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) +# define htons(x) ntohs(x) +# else +# define ntohl(x) (x) +# define htonl(x) (x) +# define ntohs(x) (x) +# define htons(x) (x) +# endif +#else +# include <netinet/in.h> +#endif + +extern "C" { + +static const char32_t kByteMask = 0x000000BF; +static const char32_t kByteMark = 0x00000080; + +// Surrogates aren't valid for UTF-32 characters, so define some +// constants that will let us screen them out. +static const char32_t kUnicodeSurrogateHighStart = 0x0000D800; +static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; +static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00; +static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; +static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; +static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; +static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF; + +// Mask used to set appropriate bits in first byte of UTF-8 sequence, +// indexed by number of bytes in the sequence. +// 0xxxxxxx +// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000 +// 110yyyyx 10xxxxxx +// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0 +// 1110yyyy 10yxxxxx 10xxxxxx +// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0 +// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx +// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0 +static const char32_t kFirstByteMark[] = { + 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 +}; + +// -------------------------------------------------------------------------- +// UTF-32 +// -------------------------------------------------------------------------- + +/** + * Return number of UTF-8 bytes required for the character. If the character + * is invalid, return size of 0. + */ +static inline size_t utf32_codepoint_utf8_length(char32_t srcChar) +{ + // Figure out how many bytes the result will require. + if (srcChar < 0x00000080) { + return 1; + } else if (srcChar < 0x00000800) { + return 2; + } else if (srcChar < 0x00010000) { + if ((srcChar < kUnicodeSurrogateStart) || (srcChar > kUnicodeSurrogateEnd)) { + return 3; + } else { + // Surrogates are invalid UTF-32 characters. + return 0; + } + } + // Max code point for Unicode is 0x0010FFFF. + else if (srcChar <= kUnicodeMaxCodepoint) { + return 4; + } else { + // Invalid UTF-32 character. + return 0; + } +} + +// Write out the source character to <dstP>. + +static inline void utf32_codepoint_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes) +{ + dstP += bytes; + switch (bytes) + { /* note: everything falls through. */ + case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]); + } +} + +size_t strlen32(const char32_t *s) +{ + const char32_t *ss = s; + while ( *ss ) + ss++; + return ss-s; +} + +size_t strnlen32(const char32_t *s, size_t maxlen) +{ + const char32_t *ss = s; + while ((maxlen > 0) && *ss) { + ss++; + maxlen--; + } + return ss-s; +} + +static inline int32_t utf32_at_internal(const char* cur, size_t *num_read) +{ + const char first_char = *cur; + if ((first_char & 0x80) == 0) { // ASCII + *num_read = 1; + return *cur; + } + cur++; + char32_t mask, to_ignore_mask; + size_t num_to_read = 0; + char32_t utf32 = first_char; + for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80; + (first_char & mask); + num_to_read++, to_ignore_mask |= mask, mask >>= 1) { + // 0x3F == 00111111 + utf32 = (utf32 << 6) + (*cur++ & 0x3F); + } + to_ignore_mask |= mask; + utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1))); + + *num_read = num_to_read; + return static_cast<int32_t>(utf32); +} + +int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index) +{ + if (index >= src_len) { + return -1; + } + size_t dummy_index; + if (next_index == NULL) { + next_index = &dummy_index; + } + size_t num_read; + int32_t ret = utf32_at_internal(src + index, &num_read); + if (ret >= 0) { + *next_index = index + num_read; + } + + return ret; +} + +ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return -1; + } + + size_t ret = 0; + const char32_t *end = src + src_len; + while (src < end) { + ret += utf32_codepoint_utf8_length(*src++); + } + return ret; +} + +void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst) +{ + if (src == NULL || src_len == 0 || dst == NULL) { + return; + } + + const char32_t *cur_utf32 = src; + const char32_t *end_utf32 = src + src_len; + char *cur = dst; + while (cur_utf32 < end_utf32) { + size_t len = utf32_codepoint_utf8_length(*cur_utf32); + utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len); + cur += len; + } + *cur = '\0'; +} + +// -------------------------------------------------------------------------- +// UTF-16 +// -------------------------------------------------------------------------- + +int strcmp16(const char16_t *s1, const char16_t *s2) +{ + char16_t ch; + int d = 0; + + while ( 1 ) { + d = (int)(ch = *s1++) - (int)*s2++; + if ( d || !ch ) + break; + } + + return d; +} + +int strncmp16(const char16_t *s1, const char16_t *s2, size_t n) +{ + char16_t ch; + int d = 0; + + while ( n-- ) { + d = (int)(ch = *s1++) - (int)*s2++; + if ( d || !ch ) + break; + } + + return d; +} + +char16_t *strcpy16(char16_t *dst, const char16_t *src) +{ + char16_t *q = dst; + const char16_t *p = src; + char16_t ch; + + do { + *q++ = ch = *p++; + } while ( ch ); + + return dst; +} + +size_t strlen16(const char16_t *s) +{ + const char16_t *ss = s; + while ( *ss ) + ss++; + return ss-s; +} + + +char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n) +{ + char16_t *q = dst; + const char16_t *p = src; + char ch; + + while (n) { + n--; + *q++ = ch = *p++; + if ( !ch ) + break; + } + + *q = 0; + + return dst; +} + +size_t strnlen16(const char16_t *s, size_t maxlen) +{ + const char16_t *ss = s; + + /* Important: the maxlen test must precede the reference through ss; + since the byte beyond the maximum may segfault */ + while ((maxlen > 0) && *ss) { + ss++; + maxlen--; + } + return ss-s; +} + +int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2) +{ + const char16_t* e1 = s1+n1; + const char16_t* e2 = s2+n2; + + while (s1 < e1 && s2 < e2) { + const int d = (int)*s1++ - (int)*s2++; + if (d) { + return d; + } + } + + return n1 < n2 + ? (0 - (int)*s2) + : (n1 > n2 + ? ((int)*s1 - 0) + : 0); +} + +int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2) +{ + const char16_t* e1 = s1H+n1; + const char16_t* e2 = s2N+n2; + + while (s1H < e1 && s2N < e2) { + const char16_t c2 = ntohs(*s2N); + const int d = (int)*s1H++ - (int)c2; + s2N++; + if (d) { + return d; + } + } + + return n1 < n2 + ? (0 - (int)ntohs(*s2N)) + : (n1 > n2 + ? ((int)*s1H - 0) + : 0); +} + +void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst) +{ + if (src == NULL || src_len == 0 || dst == NULL) { + return; + } + + const char16_t* cur_utf16 = src; + const char16_t* const end_utf16 = src + src_len; + char *cur = dst; + while (cur_utf16 < end_utf16) { + char32_t utf32; + // surrogate pairs + if ((*cur_utf16 & 0xFC00) == 0xD800) { + utf32 = (*cur_utf16++ - 0xD800) << 10; + utf32 |= *cur_utf16++ - 0xDC00; + utf32 += 0x10000; + } else { + utf32 = (char32_t) *cur_utf16++; + } + const size_t len = utf32_codepoint_utf8_length(utf32); + utf32_codepoint_to_utf8((uint8_t*)cur, utf32, len); + cur += len; + } + *cur = '\0'; +} + +// -------------------------------------------------------------------------- +// UTF-8 +// -------------------------------------------------------------------------- + +ssize_t utf8_length(const char *src) +{ + const char *cur = src; + size_t ret = 0; + while (*cur != '\0') { + const char first_char = *cur++; + if ((first_char & 0x80) == 0) { // ASCII + ret += 1; + continue; + } + // (UTF-8's character must not be like 10xxxxxx, + // but 110xxxxx, 1110xxxx, ... or 1111110x) + if ((first_char & 0x40) == 0) { + return -1; + } + + int32_t mask, to_ignore_mask; + size_t num_to_read = 0; + char32_t utf32 = 0; + for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80; + num_to_read < 5 && (first_char & mask); + num_to_read++, to_ignore_mask |= mask, mask >>= 1) { + if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx + return -1; + } + // 0x3F == 00111111 + utf32 = (utf32 << 6) + (*cur++ & 0x3F); + } + // "first_char" must be (110xxxxx - 11110xxx) + if (num_to_read == 5) { + return -1; + } + to_ignore_mask |= mask; + utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1)); + if (utf32 > kUnicodeMaxCodepoint) { + return -1; + } + + ret += num_to_read; + } + return ret; +} + +ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return -1; + } + + size_t ret = 0; + const char16_t* const end = src + src_len; + while (src < end) { + if ((*src & 0xFC00) == 0xD800 && (src + 1) < end + && (*++src & 0xFC00) == 0xDC00) { + // surrogate pairs are always 4 bytes. + ret += 4; + src++; + } else { + ret += utf32_codepoint_utf8_length((char32_t) *src++); + } + } + return ret; +} + +/** + * Returns 1-4 based on the number of leading bits. + * + * 1111 -> 4 + * 1110 -> 3 + * 110x -> 2 + * 10xx -> 1 + * 0xxx -> 1 + */ +static inline size_t utf8_codepoint_len(uint8_t ch) +{ + return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1; +} + +static inline void utf8_shift_and_mask(uint32_t* codePoint, const uint8_t byte) +{ + *codePoint <<= 6; + *codePoint |= 0x3F & byte; +} + +size_t utf8_to_utf32_length(const char *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return 0; + } + size_t ret = 0; + const char* cur; + const char* end; + size_t num_to_skip; + for (cur = src, end = src + src_len, num_to_skip = 1; + cur < end; + cur += num_to_skip, ret++) { + const char first_char = *cur; + num_to_skip = 1; + if ((first_char & 0x80) == 0) { // ASCII + continue; + } + int32_t mask; + + for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) { + } + } + return ret; +} + +void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst) +{ + if (src == NULL || src_len == 0 || dst == NULL) { + return; + } + + const char* cur = src; + const char* const end = src + src_len; + char32_t* cur_utf32 = dst; + while (cur < end) { + size_t num_read; + *cur_utf32++ = static_cast<char32_t>(utf32_at_internal(cur, &num_read)); + cur += num_read; + } + *cur_utf32 = 0; +} + +static inline uint32_t utf8_to_utf32_codepoint(const uint8_t *src, size_t length) +{ + uint32_t unicode; + + switch (length) + { + case 1: + return src[0]; + case 2: + unicode = src[0] & 0x1f; + utf8_shift_and_mask(&unicode, src[1]); + return unicode; + case 3: + unicode = src[0] & 0x0f; + utf8_shift_and_mask(&unicode, src[1]); + utf8_shift_and_mask(&unicode, src[2]); + return unicode; + case 4: + unicode = src[0] & 0x07; + utf8_shift_and_mask(&unicode, src[1]); + utf8_shift_and_mask(&unicode, src[2]); + utf8_shift_and_mask(&unicode, src[3]); + return unicode; + default: + return 0xffff; + } + + //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result); +} + +ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len) +{ + const uint8_t* const u8end = u8str + u8len; + const uint8_t* u8cur = u8str; + + /* Validate that the UTF-8 is the correct len */ + size_t u16measuredLen = 0; + while (u8cur < u8end) { + u16measuredLen++; + int u8charLen = utf8_codepoint_len(*u8cur); + uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8charLen); + if (codepoint > 0xFFFF) u16measuredLen++; // this will be a surrogate pair in utf16 + u8cur += u8charLen; + } + + /** + * Make sure that we ended where we thought we would and the output UTF-16 + * will be exactly how long we were told it would be. + */ + if (u8cur != u8end) { + return -1; + } + + return u16measuredLen; +} + +/** + * Convert a UTF-8 string to UTF-16. The destination UTF-16 buffer must have + * space for NULL at the end. + */ +void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) +{ + const uint8_t* const u8end = u8str + u8len; + const uint8_t* u8cur = u8str; + char16_t* u16cur = u16str; + + while (u8cur < u8end) { + size_t u8len = utf8_codepoint_len(*u8cur); + uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len); + + // Convert the UTF32 codepoint to one or more UTF16 codepoints + if (codepoint <= 0xFFFF) { + // Single UTF16 character + *u16cur++ = (char16_t) codepoint; + } else { + // Multiple UTF16 characters with surrogates + codepoint = codepoint - 0x10000; + *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800); + *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); + } + + u8cur += u8len; + } + *u16cur = 0; +} + +} diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 00077ee..72d4876 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -8,7 +8,8 @@ ifneq ($(TARGET_SIMULATOR),true) test_src_files := \ ObbFile_test.cpp \ Looper_test.cpp \ - String8_test.cpp + String8_test.cpp \ + Unicode_test.cpp shared_libraries := \ libz \ diff --git a/libs/utils/tests/Unicode_test.cpp b/libs/utils/tests/Unicode_test.cpp new file mode 100644 index 0000000..18c130c --- /dev/null +++ b/libs/utils/tests/Unicode_test.cpp @@ -0,0 +1,115 @@ +/* + * 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. + */ + +#define LOG_TAG "Unicode_test" +#include <utils/Log.h> +#include <utils/Unicode.h> + +#include <gtest/gtest.h> + +namespace android { + +class UnicodeTest : public testing::Test { +protected: + virtual void SetUp() { + } + + virtual void TearDown() { + } +}; + +TEST_F(UnicodeTest, UTF8toUTF16ZeroLength) { + ssize_t measured; + + const uint8_t str[] = { }; + + measured = utf8_to_utf16_length(str, 0); + EXPECT_EQ(0, measured) + << "Zero length input should return zero length output."; +} + +TEST_F(UnicodeTest, UTF8toUTF16ASCIILength) { + ssize_t measured; + + // U+0030 or ASCII '0' + const uint8_t str[] = { 0x30 }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(1, measured) + << "ASCII glyphs should have a length of 1 char16_t"; +} + +TEST_F(UnicodeTest, UTF8toUTF16Plane1Length) { + ssize_t measured; + + // U+2323 SMILE + const uint8_t str[] = { 0xE2, 0x8C, 0xA3 }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(1, measured) + << "Plane 1 glyphs should have a length of 1 char16_t"; +} + +TEST_F(UnicodeTest, UTF8toUTF16SurrogateLength) { + ssize_t measured; + + // U+10000 + const uint8_t str[] = { 0xF0, 0x90, 0x80, 0x80 }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(2, measured) + << "Surrogate pairs should have a length of 2 char16_t"; +} + +TEST_F(UnicodeTest, UTF8toUTF16TruncatedUTF8) { + ssize_t measured; + + // Truncated U+2323 SMILE + // U+2323 SMILE + const uint8_t str[] = { 0xE2, 0x8C }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(-1, measured) + << "Truncated UTF-8 should return -1 to indicate invalid"; +} + +TEST_F(UnicodeTest, UTF8toUTF16Normal) { + const uint8_t str[] = { + 0x30, // U+0030, 1 UTF-16 character + 0xC4, 0x80, // U+0100, 1 UTF-16 character + 0xE2, 0x8C, 0xA3, // U+2323, 1 UTF-16 character + 0xF0, 0x90, 0x80, 0x80, // U+10000, 2 UTF-16 character + }; + + char16_t output[1 + 1 + 1 + 2 + 1]; // Room for NULL + + utf8_to_utf16(str, sizeof(str), output); + + EXPECT_EQ(0x0030, output[0]) + << "should be U+0030"; + EXPECT_EQ(0x0100, output[1]) + << "should be U+0100"; + EXPECT_EQ(0x2323, output[2]) + << "should be U+2323"; + EXPECT_EQ(0xD800, output[3]) + << "should be first half of surrogate U+10000"; + EXPECT_EQ(0xDC00, output[4]) + << "should be second half of surrogate U+10000"; + EXPECT_EQ(NULL, output[5]) + << "should be NULL terminated"; +} + +} diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index 80c97a0..0e689e4 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -77,11 +77,13 @@ public class MediaFile { private static final int LAST_IMAGE_FILE_TYPE = FILE_TYPE_WBMP; // Playlist file types - public static final int FILE_TYPE_M3U = 41; - public static final int FILE_TYPE_PLS = 42; - public static final int FILE_TYPE_WPL = 43; + public static final int FILE_TYPE_M3U = 41; + public static final int FILE_TYPE_PLS = 42; + public static final int FILE_TYPE_WPL = 43; + public static final int FILE_TYPE_HTTPLIVE = 44; + private static final int FIRST_PLAYLIST_FILE_TYPE = FILE_TYPE_M3U; - private static final int LAST_PLAYLIST_FILE_TYPE = FILE_TYPE_WPL; + private static final int LAST_PLAYLIST_FILE_TYPE = FILE_TYPE_HTTPLIVE; // Drm file types public static final int FILE_TYPE_FL = 51; @@ -194,6 +196,9 @@ public class MediaFile { addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST); addFileType("PLS", FILE_TYPE_PLS, "audio/x-scpls", MtpConstants.FORMAT_PLS_PLAYLIST); addFileType("WPL", FILE_TYPE_WPL, "application/vnd.ms-wpl", MtpConstants.FORMAT_WPL_PLAYLIST); + addFileType("M3U8", FILE_TYPE_HTTPLIVE, "application/vnd.apple.mpegurl"); + addFileType("M3U8", FILE_TYPE_HTTPLIVE, "audio/mpegurl"); + addFileType("M3U8", FILE_TYPE_HTTPLIVE, "audio/x-mpegurl"); addFileType("FL", FILE_TYPE_FL, "application/x-android-drm-fl"); diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 4fe2c43..69b872b 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -434,24 +434,23 @@ public class MediaScanner } } - mMimeType = null; + mMimeType = mimeType; mFileType = 0; mFileSize = fileSize; // try mimeType first, if it is specified if (mimeType != null) { mFileType = MediaFile.getFileTypeForMimeType(mimeType); - if (mFileType != 0) { - mMimeType = mimeType; - } } // if mimeType was not specified, compute file type based on file extension. - if (mMimeType == null) { + if (mFileType == 0) { MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path); if (mediaFileType != null) { mFileType = mediaFileType.fileType; - mMimeType = mediaFileType.mimeType; + if (mMimeType == null) { + mMimeType = mediaFileType.mimeType; + } } } @@ -1194,18 +1193,37 @@ public class MediaScanner } mMtpObjectHandle = objectHandle; + initialize(volumeName); try { - initialize(volumeName); - // MTP will create a file entry for us so we don't want to do it in prescan - prescan(path, false); + if (MediaFile.isPlayListFileType(fileType)) { + // build file cache so we can look up tracks in the playlist + prescan(null, true); + + String key = path; + if (mMediaStoragePath != null && key.startsWith(mMediaStoragePath)) { + // MediaProvider uses external variant of path for _data, so we need to match + // against that path instead. + key = mExternalStoragePath + key.substring(mMediaStoragePath.length()); + } + if (mCaseInsensitivePaths) { + key = path.toLowerCase(); + } + FileCacheEntry entry = mFileCache.get(key); + if (entry != null) { + processPlayList(entry); + } + } else { + // MTP will create a file entry for us so we don't want to do it in prescan + prescan(path, false); - File file = new File(path); + File file = new File(path); - // lastModified is in milliseconds on Files. - long lastModifiedSeconds = file.lastModified() / 1000; + // lastModified is in milliseconds on Files. + long lastModifiedSeconds = file.lastModified() / 1000; - // always scan the file, so we can return the content://media Uri for existing files - mClient.doScanFile(path, mediaFileType.mimeType, lastModifiedSeconds, file.length(), true); + // always scan the file, so we can return the content://media Uri for existing files + mClient.doScanFile(path, mediaFileType.mimeType, lastModifiedSeconds, file.length(), true); + } } catch (RemoteException e) { Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e); } finally { @@ -1285,8 +1303,7 @@ public class MediaScanner } } - // if the match is not for an audio file, bail out - if (bestMatch == null || ! mAudioUri.equals(bestMatch.mTableUri)) { + if (bestMatch == null) { return false; } @@ -1431,63 +1448,67 @@ public class MediaScanner } } - private void processPlayLists() throws RemoteException { - Iterator<FileCacheEntry> iterator = mPlayLists.iterator(); - while (iterator.hasNext()) { - FileCacheEntry entry = iterator.next(); - String path = entry.mPath; + private void processPlayList(FileCacheEntry entry) throws RemoteException { + String path = entry.mPath; + ContentValues values = new ContentValues(); + int lastSlash = path.lastIndexOf('/'); + if (lastSlash < 0) throw new IllegalArgumentException("bad path " + path); + Uri uri, membersUri; + long rowId = entry.mRowId; + if (rowId == 0) { + // Create a new playlist + + int lastDot = path.lastIndexOf('.'); + String name = (lastDot < 0 ? path.substring(lastSlash + 1) : path.substring(lastSlash + 1, lastDot)); + values.put(MediaStore.Audio.Playlists.NAME, name); + values.put(MediaStore.Audio.Playlists.DATA, path); + values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, entry.mLastModified); + uri = mMediaProvider.insert(mPlaylistsUri, values); + rowId = ContentUris.parseId(uri); + membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY); + } else { + uri = ContentUris.withAppendedId(mPlaylistsUri, rowId); - // only process playlist files if they are new or have been modified since the last scan - if (entry.mLastModifiedChanged) { - ContentValues values = new ContentValues(); - int lastSlash = path.lastIndexOf('/'); - if (lastSlash < 0) throw new IllegalArgumentException("bad path " + path); - Uri uri, membersUri; - long rowId = entry.mRowId; - if (rowId == 0) { - // Create a new playlist - - int lastDot = path.lastIndexOf('.'); - String name = (lastDot < 0 ? path.substring(lastSlash + 1) : path.substring(lastSlash + 1, lastDot)); - values.put(MediaStore.Audio.Playlists.NAME, name); - values.put(MediaStore.Audio.Playlists.DATA, path); - values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, entry.mLastModified); - uri = mMediaProvider.insert(mPlaylistsUri, values); - rowId = ContentUris.parseId(uri); - membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY); - } else { - uri = ContentUris.withAppendedId(mPlaylistsUri, rowId); + // update lastModified value of existing playlist + values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, entry.mLastModified); + mMediaProvider.update(uri, values, null, null); - // update lastModified value of existing playlist - values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, entry.mLastModified); - mMediaProvider.update(uri, values, null, null); + // delete members of existing playlist + membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY); + mMediaProvider.delete(membersUri, null, null); + } - // delete members of existing playlist - membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY); - mMediaProvider.delete(membersUri, null, null); - } + String playListDirectory = path.substring(0, lastSlash + 1); + MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path); + int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType); - String playListDirectory = path.substring(0, lastSlash + 1); - MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path); - int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType); + if (fileType == MediaFile.FILE_TYPE_M3U) { + processM3uPlayList(path, playListDirectory, membersUri, values); + } else if (fileType == MediaFile.FILE_TYPE_PLS) { + processPlsPlayList(path, playListDirectory, membersUri, values); + } else if (fileType == MediaFile.FILE_TYPE_WPL) { + processWplPlayList(path, playListDirectory, membersUri); + } - if (fileType == MediaFile.FILE_TYPE_M3U) - processM3uPlayList(path, playListDirectory, membersUri, values); - else if (fileType == MediaFile.FILE_TYPE_PLS) - processPlsPlayList(path, playListDirectory, membersUri, values); - else if (fileType == MediaFile.FILE_TYPE_WPL) - processWplPlayList(path, playListDirectory, membersUri); + Cursor cursor = mMediaProvider.query(membersUri, PLAYLIST_MEMBERS_PROJECTION, null, + null, null); + try { + if (cursor == null || cursor.getCount() == 0) { + Log.d(TAG, "playlist is empty - deleting"); + mMediaProvider.delete(uri, null, null); + } + } finally { + if (cursor != null) cursor.close(); + } + } - Cursor cursor = mMediaProvider.query(membersUri, PLAYLIST_MEMBERS_PROJECTION, null, - null, null); - try { - if (cursor == null || cursor.getCount() == 0) { - Log.d(TAG, "playlist is empty - deleting"); - mMediaProvider.delete(uri, null, null); - } - } finally { - if (cursor != null) cursor.close(); - } + private void processPlayLists() throws RemoteException { + Iterator<FileCacheEntry> iterator = mPlayLists.iterator(); + while (iterator.hasNext()) { + FileCacheEntry entry = iterator.next(); + // only process playlist files if they are new or have been modified since the last scan + if (entry.mLastModifiedChanged) { + processPlayList(entry); } } } diff --git a/media/java/android/media/MtpConstants.java b/media/java/android/media/MtpConstants.java index a7d33ce..b20cbd1 100644 --- a/media/java/android/media/MtpConstants.java +++ b/media/java/android/media/MtpConstants.java @@ -21,6 +21,30 @@ package android.media; */ public final class MtpConstants { +// MTP Data Types + public static final int TYPE_UNDEFINED = 0x0000; + public static final int TYPE_INT8 = 0x0001; + public static final int TYPE_UINT8 = 0x0002; + public static final int TYPE_INT16 = 0x0003; + public static final int TYPE_UINT16 = 0x0004; + public static final int TYPE_INT32 = 0x0005; + public static final int TYPE_UINT32 = 0x0006; + public static final int TYPE_INT64 = 0x0007; + public static final int TYPE_UINT64 = 0x0008; + public static final int TYPE_INT128 = 0x0009; + public static final int TYPE_UINT128 = 0x000A; + public static final int TYPE_AINT8 = 0x4001; + public static final int TYPE_AUINT8 = 0x4002; + public static final int TYPE_AINT16 = 0x4003; + public static final int TYPE_AUINT16 = 0x4004; + public static final int TYPE_AINT32 = 0x4005; + public static final int TYPE_AUINT32 = 0x4006; + public static final int TYPE_AINT64 = 0x4007; + public static final int TYPE_AUINT64 = 0x4008; + public static final int TYPE_AINT128 = 0x4009; + public static final int TYPE_AUINT128 = 0x400A; + public static final int TYPE_STR = 0xFFFF; + // MTP Response Codes public static final int RESPONSE_UNDEFINED = 0x2000; public static final int RESPONSE_OK = 0x2001; diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java index 57ab3a1..42d068f 100644 --- a/media/java/android/media/MtpDatabase.java +++ b/media/java/android/media/MtpDatabase.java @@ -428,6 +428,26 @@ public class MtpDatabase { } } + private String queryAudio(int id, String column) { + Cursor c = null; + try { + c = mMediaProvider.query(Audio.Media.getContentUri(mVolumeName), + new String [] { Files.FileColumns._ID, column }, + ID_WHERE, new String[] { Integer.toString(id) }, null); + if (c != null && c.moveToNext()) { + return c.getString(1); + } else { + return ""; + } + } catch (Exception e) { + return null; + } finally { + if (c != null) { + c.close(); + } + } + } + private String queryGenre(int id) { Cursor c = null; try { @@ -450,7 +470,7 @@ public class MtpDatabase { } } - private boolean queryInt(int id, String column, long[] outValue) { + private Long queryLong(int id, String column) { Cursor c = null; try { // for now we are only reading properties from the "objects" table @@ -458,17 +478,15 @@ public class MtpDatabase { new String [] { Files.FileColumns._ID, column }, ID_WHERE, new String[] { Integer.toString(id) }, null); if (c != null && c.moveToNext()) { - outValue[0] = c.getLong(1); - return true; + return new Long(c.getLong(1)); } - return false; } catch (Exception e) { - return false; } finally { if (c != null) { c.close(); } } + return null; } private String nameFromPath(String path) { @@ -485,197 +503,286 @@ public class MtpDatabase { return path.substring(start, end); } - private int renameFile(int handle, String newName) { - Cursor c = null; - - // first compute current path - String path = null; - String[] whereArgs = new String[] { Integer.toString(handle) }; - try { - c = mMediaProvider.query(mObjectsUri, PATH_PROJECTION, ID_WHERE, whereArgs, null); - if (c != null && c.moveToNext()) { - path = externalToMediaPath(c.getString(1)); - } - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in getObjectFilePath", e); - return MtpConstants.RESPONSE_GENERAL_ERROR; - } finally { - if (c != null) { - c.close(); - } - } - if (path == null) { - return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE; - } - - // now rename the file. make sure this succeeds before updating database - File oldFile = new File(path); - int lastSlash = path.lastIndexOf('/'); - if (lastSlash <= 1) { - return MtpConstants.RESPONSE_GENERAL_ERROR; - } - String newPath = path.substring(0, lastSlash + 1) + newName; - File newFile = new File(newPath); - boolean success = oldFile.renameTo(newFile); - Log.d(TAG, "renaming "+ path + " to " + newPath + (success ? " succeeded" : " failed")); - if (!success) { - return MtpConstants.RESPONSE_GENERAL_ERROR; - } - - // finally update database - ContentValues values = new ContentValues(); - values.put(Files.FileColumns.DATA, newPath); - int updated = 0; - try { - // note - we are relying on a special case in MediaProvider.update() to update - // the paths for all children in the case where this is a directory. - updated = mMediaProvider.update(mObjectsUri, values, ID_WHERE, whereArgs); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in mMediaProvider.update", e); + private MtpPropertyList getObjectPropertyList(int handle, int format, int property, + int groupCode, int depth) { + // FIXME - implement group support + // For now we only support a single property at a time + if (groupCode != 0) { + return new MtpPropertyList(0, MtpConstants.RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED); } - if (updated == 0) { - Log.e(TAG, "Unable to update path for " + path + " to " + newPath); - // this shouldn't happen, but if it does we need to rename the file to its original name - newFile.renameTo(oldFile); - return MtpConstants.RESPONSE_GENERAL_ERROR; + if (depth > 1) { + return new MtpPropertyList(0, MtpConstants.RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED); } - return MtpConstants.RESPONSE_OK; - } - - private int getObjectProperty(int handle, int property, - long[] outIntValue, char[] outStringValue) { - Log.d(TAG, "getObjectProperty: " + property); String column = null; - boolean isString = false; + int type = MtpConstants.TYPE_UNDEFINED; - switch (property) { + switch (property) { case MtpConstants.PROPERTY_STORAGE_ID: - outIntValue[0] = mStorageID; - return MtpConstants.RESPONSE_OK; - case MtpConstants.PROPERTY_OBJECT_FORMAT: + // no query needed until we support multiple storage units + // for now it is always mStorageID + type = MtpConstants.TYPE_UINT32; + break; + case MtpConstants.PROPERTY_OBJECT_FORMAT: column = Files.FileColumns.FORMAT; + type = MtpConstants.TYPE_UINT16; break; case MtpConstants.PROPERTY_PROTECTION_STATUS: // protection status is always 0 - outIntValue[0] = 0; - return MtpConstants.RESPONSE_OK; + type = MtpConstants.TYPE_UINT16; + break; case MtpConstants.PROPERTY_OBJECT_SIZE: column = Files.FileColumns.SIZE; + type = MtpConstants.TYPE_UINT64; break; case MtpConstants.PROPERTY_OBJECT_FILE_NAME: - // special case - need to extract file name from full path - String value = queryString(handle, Files.FileColumns.DATA); - if (value != null) { - value = nameFromPath(value); - value.getChars(0, value.length(), outStringValue, 0); - outStringValue[value.length()] = 0; - return MtpConstants.RESPONSE_OK; - } else { - return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE; - } + column = Files.FileColumns.DATA; + type = MtpConstants.TYPE_STR; + break; case MtpConstants.PROPERTY_NAME: - // first try title - String name = queryString(handle, MediaColumns.TITLE); - // then try name - if (name == null) { - name = queryString(handle, Audio.PlaylistsColumns.NAME); - } - // if title and name fail, extract name from full path - if (name == null) { - name = queryString(handle, Files.FileColumns.DATA); - if (name != null) { - name = nameFromPath(name); - } - } - if (name != null) { - name.getChars(0, name.length(), outStringValue, 0); - outStringValue[name.length()] = 0; - return MtpConstants.RESPONSE_OK; - } else { - return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE; - } + column = MediaColumns.TITLE; + type = MtpConstants.TYPE_STR; + break; case MtpConstants.PROPERTY_DATE_MODIFIED: column = Files.FileColumns.DATE_MODIFIED; + type = MtpConstants.TYPE_STR; break; case MtpConstants.PROPERTY_DATE_ADDED: column = Files.FileColumns.DATE_ADDED; + type = MtpConstants.TYPE_STR; break; case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE: column = Audio.AudioColumns.YEAR; + type = MtpConstants.TYPE_STR; break; case MtpConstants.PROPERTY_PARENT_OBJECT: column = Files.FileColumns.PARENT; + type = MtpConstants.TYPE_UINT32; break; case MtpConstants.PROPERTY_PERSISTENT_UID: // PUID is concatenation of storageID and object handle - long puid = mStorageID; - puid <<= 32; - puid += handle; - outIntValue[0] = puid; - return MtpConstants.RESPONSE_OK; + type = MtpConstants.TYPE_UINT128; + break; case MtpConstants.PROPERTY_DURATION: column = Audio.AudioColumns.DURATION; + type = MtpConstants.TYPE_UINT32; break; case MtpConstants.PROPERTY_TRACK: - if (queryInt(handle, Audio.AudioColumns.TRACK, outIntValue)) { - // track is stored in lower 3 decimal digits - outIntValue[0] %= 1000; - return MtpConstants.RESPONSE_OK; - } else { - return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE; - } + column = Audio.AudioColumns.TRACK; + type = MtpConstants.TYPE_UINT16; + break; case MtpConstants.PROPERTY_DISPLAY_NAME: column = MediaColumns.DISPLAY_NAME; - isString = true; + type = MtpConstants.TYPE_STR; break; case MtpConstants.PROPERTY_ARTIST: - column = Audio.AudioColumns.ARTIST; - isString = true; + type = MtpConstants.TYPE_STR; break; case MtpConstants.PROPERTY_ALBUM_NAME: - column = Audio.AudioColumns.ALBUM; - isString = true; + type = MtpConstants.TYPE_STR; break; case MtpConstants.PROPERTY_ALBUM_ARTIST: column = Audio.AudioColumns.ALBUM_ARTIST; - isString = true; + type = MtpConstants.TYPE_STR; break; case MtpConstants.PROPERTY_GENRE: - String genre = queryGenre(handle); - if (genre != null) { - genre.getChars(0, genre.length(), outStringValue, 0); - outStringValue[genre.length()] = 0; - return MtpConstants.RESPONSE_OK; - } else { - return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE; - } + // genre requires a special query + type = MtpConstants.TYPE_STR; + break; case MtpConstants.PROPERTY_COMPOSER: column = Audio.AudioColumns.COMPOSER; - isString = true; + type = MtpConstants.TYPE_STR; break; case MtpConstants.PROPERTY_DESCRIPTION: column = Images.ImageColumns.DESCRIPTION; - isString = true; + type = MtpConstants.TYPE_STR; break; default: - return MtpConstants.RESPONSE_OBJECT_PROP_NOT_SUPPORTED; + return new MtpPropertyList(0, MtpConstants.RESPONSE_OBJECT_PROP_NOT_SUPPORTED); } - if (isString) { - String value = queryString(handle, column); - if (value != null) { - value.getChars(0, value.length(), outStringValue, 0); - outStringValue[value.length()] = 0; - return MtpConstants.RESPONSE_OK; + Cursor c = null; + try { + if (column != null) { + c = mMediaProvider.query(mObjectsUri, + new String [] { Files.FileColumns._ID, column }, + // depth 0: single record, depth 1: immediate children + (depth == 0 ? ID_WHERE : PARENT_WHERE), + new String[] { Integer.toString(handle) }, null); + if (c == null) { + return new MtpPropertyList(0, MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); + } + } else if (depth == 1) { + c = mMediaProvider.query(mObjectsUri, + new String [] { Files.FileColumns._ID }, + PARENT_WHERE, new String[] { Integer.toString(handle) }, null); + if (c == null) { + return new MtpPropertyList(0, MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); + } } - } else { - if (queryInt(handle, column, outIntValue)) { - return MtpConstants.RESPONSE_OK; + + int count = (c == null ? 1 : c.getCount()); + MtpPropertyList result = new MtpPropertyList(count, MtpConstants.RESPONSE_OK); + + for (int index = 0; index < count; index++) { + if (c != null) { + c.moveToNext(); + } + if (depth == 1) { + handle = (int)c.getLong(0); + } + + switch (property) { + // handle special cases here + case MtpConstants.PROPERTY_STORAGE_ID: + result.setProperty(index, handle, property, MtpConstants.TYPE_UINT32, + mStorageID); + break; + case MtpConstants.PROPERTY_PROTECTION_STATUS: + // protection status is always 0 + result.setProperty(index, handle, property, MtpConstants.TYPE_UINT16, 0); + break; + case MtpConstants.PROPERTY_OBJECT_FILE_NAME: + // special case - need to extract file name from full path + String value = c.getString(1); + if (value != null) { + result.setProperty(index, handle, property, nameFromPath(value)); + } else { + result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); + } + break; + case MtpConstants.PROPERTY_NAME: + // first try title + String name = c.getString(1); + // then try name + if (name == null) { + name = queryString(handle, Audio.PlaylistsColumns.NAME); + } + // if title and name fail, extract name from full path + if (name == null) { + name = queryString(handle, Files.FileColumns.DATA); + if (name != null) { + name = nameFromPath(name); + } + } + if (name != null) { + result.setProperty(index, handle, property, name); + } else { + result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); + } + break; + case MtpConstants.PROPERTY_DATE_MODIFIED: + case MtpConstants.PROPERTY_DATE_ADDED: + // convert from seconds to DateTime + result.setProperty(index, handle, property, format_date_time(c.getInt(1))); + break; + case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE: + // release date is stored internally as just the year + int year = c.getInt(1); + String dateTime = Integer.toString(year) + "0101T000000"; + result.setProperty(index, handle, property, dateTime); + break; + case MtpConstants.PROPERTY_PERSISTENT_UID: + // PUID is concatenation of storageID and object handle + long puid = mStorageID; + puid <<= 32; + puid += handle; + result.setProperty(index, handle, property, MtpConstants.TYPE_UINT128, puid); + break; + case MtpConstants.PROPERTY_TRACK: + result.setProperty(index, handle, property, MtpConstants.TYPE_UINT16, + c.getInt(1) % 1000); + break; + case MtpConstants.PROPERTY_ARTIST: + result.setProperty(index, handle, property, queryAudio(handle, Audio.AudioColumns.ARTIST)); + break; + case MtpConstants.PROPERTY_ALBUM_NAME: + result.setProperty(index, handle, property, queryAudio(handle, Audio.AudioColumns.ALBUM)); + break; + case MtpConstants.PROPERTY_GENRE: + String genre = queryGenre(handle); + if (genre != null) { + result.setProperty(index, handle, property, genre); + } else { + result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); + } + break; + default: + if (type == MtpConstants.TYPE_STR) { + result.setProperty(index, handle, property, c.getString(1)); + } else { + result.setProperty(index, handle, property, type, c.getLong(1)); + } + } + } + + return result; + } catch (RemoteException e) { + return new MtpPropertyList(0, MtpConstants.RESPONSE_GENERAL_ERROR); + } finally { + if (c != null) { + c.close(); } } - // query failed if we get here - return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE; + // impossible to get here, so no return statement + } + + private int renameFile(int handle, String newName) { + Cursor c = null; + + // first compute current path + String path = null; + String[] whereArgs = new String[] { Integer.toString(handle) }; + try { + c = mMediaProvider.query(mObjectsUri, PATH_PROJECTION, ID_WHERE, whereArgs, null); + if (c != null && c.moveToNext()) { + path = externalToMediaPath(c.getString(1)); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getObjectFilePath", e); + return MtpConstants.RESPONSE_GENERAL_ERROR; + } finally { + if (c != null) { + c.close(); + } + } + if (path == null) { + return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE; + } + + // now rename the file. make sure this succeeds before updating database + File oldFile = new File(path); + int lastSlash = path.lastIndexOf('/'); + if (lastSlash <= 1) { + return MtpConstants.RESPONSE_GENERAL_ERROR; + } + String newPath = path.substring(0, lastSlash + 1) + newName; + File newFile = new File(newPath); + boolean success = oldFile.renameTo(newFile); + Log.d(TAG, "renaming "+ path + " to " + newPath + (success ? " succeeded" : " failed")); + if (!success) { + return MtpConstants.RESPONSE_GENERAL_ERROR; + } + + // finally update database + ContentValues values = new ContentValues(); + values.put(Files.FileColumns.DATA, newPath); + int updated = 0; + try { + // note - we are relying on a special case in MediaProvider.update() to update + // the paths for all children in the case where this is a directory. + updated = mMediaProvider.update(mObjectsUri, values, ID_WHERE, whereArgs); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in mMediaProvider.update", e); + } + if (updated == 0) { + Log.e(TAG, "Unable to update path for " + path + " to " + newPath); + // this shouldn't happen, but if it does we need to rename the file to its original name + newFile.renameTo(oldFile); + return MtpConstants.RESPONSE_GENERAL_ERROR; + } + + return MtpConstants.RESPONSE_OK; } private int setObjectProperty(int handle, int property, @@ -913,4 +1020,5 @@ public class MtpDatabase { private native final void native_setup(); private native final void native_finalize(); + private native String format_date_time(long seconds); } diff --git a/media/java/android/media/MtpPropertyList.java b/media/java/android/media/MtpPropertyList.java new file mode 100644 index 0000000..f598981 --- /dev/null +++ b/media/java/android/media/MtpPropertyList.java @@ -0,0 +1,76 @@ +/* + * 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.media; + +/** + * Encapsulates the ObjectPropList dataset used by the GetObjectPropList command. + * The fields of this class are read by JNI code in android_media_MtpDatabase.cpp + * + * {@hide} + */ + +public class MtpPropertyList { + + // number of results returned + public final int mCount; + // result code for GetObjectPropList + public int mResult; + // list of object handles (first field in quadruplet) + public final int[] mObjectHandles; + // list of object propery codes (second field in quadruplet) + public final int[] mPropertyCodes; + // list of data type codes (third field in quadruplet) + public final int[] mDataTypes; + // list of long int property values (fourth field in quadruplet, when value is integer type) + public long[] mLongValues; + // list of long int property values (fourth field in quadruplet, when value is string type) + public String[] mStringValues; + + // constructor only called from MtpDatabase + public MtpPropertyList(int count, int result) { + mCount = count; + mResult = result; + mObjectHandles = new int[count]; + mPropertyCodes = new int[count]; + mDataTypes = new int[count]; + // mLongValues and mStringValues are created lazily since both might not be necessary + } + + public void setProperty(int index, int handle, int property, int type, long value) { + if (mLongValues == null) { + mLongValues = new long[mCount]; + } + mObjectHandles[index] = handle; + mPropertyCodes[index] = property; + mDataTypes[index] = type; + mLongValues[index] = value; + } + + public void setProperty(int index, int handle, int property, String value) { + if (mStringValues == null) { + mStringValues = new String[mCount]; + } + mObjectHandles[index] = handle; + mPropertyCodes[index] = property; + mDataTypes[index] = MtpConstants.TYPE_STR; + mStringValues[index] = value; + } + + public void setResult(int result) { + mResult = result; + } +} diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp index 87cb82e..5377af6 100644 --- a/media/jni/android_media_MtpDatabase.cpp +++ b/media/jni/android_media_MtpDatabase.cpp @@ -46,10 +46,10 @@ static jmethodID method_getSupportedPlaybackFormats; static jmethodID method_getSupportedCaptureFormats; static jmethodID method_getSupportedObjectProperties; static jmethodID method_getSupportedDeviceProperties; -static jmethodID method_getObjectProperty; static jmethodID method_setObjectProperty; static jmethodID method_getDeviceProperty; static jmethodID method_setDeviceProperty; +static jmethodID method_getObjectPropertyList; static jmethodID method_getObjectInfo; static jmethodID method_getObjectFilePath; static jmethodID method_deleteFile; @@ -60,6 +60,16 @@ static jmethodID method_sessionEnded; static jfieldID field_context; +// MtpPropertyList fields +static jfieldID field_mCount; +static jfieldID field_mResult; +static jfieldID field_mObjectHandles; +static jfieldID field_mPropertyCodes; +static jfieldID field_mDataTypes; +static jfieldID field_mLongValues; +static jfieldID field_mStringValues; + + MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) { return (MtpDatabase *)env->GetIntField(database, field_context); } @@ -122,6 +132,12 @@ public: virtual MtpResponseCode resetDeviceProperty(MtpDeviceProperty property); + virtual MtpResponseCode getObjectPropertyList(MtpObjectHandle handle, + MtpObjectFormat format, + MtpObjectProperty property, + int groupCode, int depth, + MtpDataPacket& packet); + virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle, MtpDataPacket& packet); @@ -336,84 +352,111 @@ MtpDevicePropertyList* MyMtpDatabase::getSupportedDeviceProperties() { MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, MtpDataPacket& packet) { - int type; - - if (!getObjectPropertyInfo(property, type)) - return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED; - JNIEnv* env = AndroidRuntime::getJNIEnv(); - jint result = env->CallIntMethod(mDatabase, method_getObjectProperty, - (jint)handle, (jint)property, mLongBuffer, mStringBuffer); - if (result != MTP_RESPONSE_OK) { - checkAndClearExceptionFromCallback(env, __FUNCTION__); - return result; - } - - jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0); - jlong longValue = longValues[0]; - env->ReleaseLongArrayElements(mLongBuffer, longValues, 0); - - // special case date properties, which are strings to MTP - // but stored internally as a uint64 - if (property == MTP_PROPERTY_DATE_MODIFIED || property == MTP_PROPERTY_DATE_ADDED) { - char date[20]; - formatDateTime(longValue, date, sizeof(date)); - packet.putString(date); - return MTP_RESPONSE_OK; - } - // release date is stored internally as just the year - if (property == MTP_PROPERTY_ORIGINAL_RELEASE_DATE) { - char date[20]; - snprintf(date, sizeof(date), "%04lld0101T000000", longValue); - packet.putString(date); - return MTP_RESPONSE_OK; - } + jobject list = env->CallObjectMethod(mDatabase, method_getObjectPropertyList, + (jint)handle, 0, (jint)property, 0, 0); + MtpResponseCode result = env->GetIntField(list, field_mResult); + int count = env->GetIntField(list, field_mCount); + if (result == MTP_RESPONSE_OK && count != 1) + result = MTP_RESPONSE_GENERAL_ERROR; + + if (result == MTP_RESPONSE_OK) { + jintArray objectHandlesArray = (jintArray)env->GetObjectField(list, field_mObjectHandles); + jintArray propertyCodesArray = (jintArray)env->GetObjectField(list, field_mPropertyCodes); + jintArray dataTypesArray = (jintArray)env->GetObjectField(list, field_mDataTypes); + jlongArray longValuesArray = (jlongArray)env->GetObjectField(list, field_mLongValues); + jobjectArray stringValuesArray = (jobjectArray)env->GetObjectField(list, field_mStringValues); + + jint* objectHandles = env->GetIntArrayElements(objectHandlesArray, 0); + jint* propertyCodes = env->GetIntArrayElements(propertyCodesArray, 0); + jint* dataTypes = env->GetIntArrayElements(dataTypesArray, 0); + jlong* longValues = (longValuesArray ? env->GetLongArrayElements(longValuesArray, 0) : NULL); + + int type = dataTypes[0]; + jlong longValue = (longValues ? longValues[0] : 0); + + // special case date properties, which are strings to MTP + // but stored internally as a uint64 + if (property == MTP_PROPERTY_DATE_MODIFIED || property == MTP_PROPERTY_DATE_ADDED) { + char date[20]; + formatDateTime(longValue, date, sizeof(date)); + packet.putString(date); + goto out; + } + // release date is stored internally as just the year + if (property == MTP_PROPERTY_ORIGINAL_RELEASE_DATE) { + char date[20]; + snprintf(date, sizeof(date), "%04lld0101T000000", longValue); + packet.putString(date); + goto out; + } - switch (type) { - case MTP_TYPE_INT8: - packet.putInt8(longValue); - break; - case MTP_TYPE_UINT8: - packet.putUInt8(longValue); - break; - case MTP_TYPE_INT16: - packet.putInt16(longValue); - break; - case MTP_TYPE_UINT16: - packet.putUInt16(longValue); - break; - case MTP_TYPE_INT32: - packet.putInt32(longValue); - break; - case MTP_TYPE_UINT32: - packet.putUInt32(longValue); - break; - case MTP_TYPE_INT64: - packet.putInt64(longValue); - break; - case MTP_TYPE_UINT64: - packet.putUInt64(longValue); - break; - case MTP_TYPE_INT128: - packet.putInt128(longValue); - break; - case MTP_TYPE_UINT128: - packet.putInt128(longValue); - break; - case MTP_TYPE_STR: - { - jchar* str = env->GetCharArrayElements(mStringBuffer, 0); - packet.putString(str); - env->ReleaseCharArrayElements(mStringBuffer, str, 0); - break; - } - default: - LOGE("unsupported type in getObjectPropertyValue\n"); - return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT; + switch (type) { + case MTP_TYPE_INT8: + packet.putInt8(longValue); + break; + case MTP_TYPE_UINT8: + packet.putUInt8(longValue); + break; + case MTP_TYPE_INT16: + packet.putInt16(longValue); + break; + case MTP_TYPE_UINT16: + packet.putUInt16(longValue); + break; + case MTP_TYPE_INT32: + packet.putInt32(longValue); + break; + case MTP_TYPE_UINT32: + packet.putUInt32(longValue); + break; + case MTP_TYPE_INT64: + packet.putInt64(longValue); + break; + case MTP_TYPE_UINT64: + packet.putUInt64(longValue); + break; + case MTP_TYPE_INT128: + packet.putInt128(longValue); + break; + case MTP_TYPE_UINT128: + packet.putInt128(longValue); + break; + case MTP_TYPE_STR: + { + jstring stringValue = (jstring)env->GetObjectArrayElement(stringValuesArray, 0); + if (stringValue) { + const char* str = env->GetStringUTFChars(stringValue, NULL); + packet.putString(str); + env->ReleaseStringUTFChars(stringValue, str); + } else { + packet.putEmptyString(); + } + break; + } + default: + LOGE("unsupported type in getObjectPropertyValue\n"); + result = MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT; + } +out: + env->ReleaseIntArrayElements(objectHandlesArray, objectHandles, 0); + env->ReleaseIntArrayElements(propertyCodesArray, propertyCodes, 0); + env->ReleaseIntArrayElements(dataTypesArray, dataTypes, 0); + if (longValues) + env->ReleaseLongArrayElements(longValuesArray, longValues, 0); + + env->DeleteLocalRef(objectHandlesArray); + env->DeleteLocalRef(propertyCodesArray); + env->DeleteLocalRef(dataTypesArray); + if (longValuesArray) + env->DeleteLocalRef(longValuesArray); + if (stringValuesArray) + env->DeleteLocalRef(stringValuesArray); } + env->DeleteLocalRef(list); checkAndClearExceptionFromCallback(env, __FUNCTION__); - return MTP_RESPONSE_OK; + return result; } MtpResponseCode MyMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle, @@ -601,6 +644,106 @@ MtpResponseCode MyMtpDatabase::resetDeviceProperty(MtpDeviceProperty property) { return -1; } +MtpResponseCode MyMtpDatabase::getObjectPropertyList(MtpObjectHandle handle, + MtpObjectFormat format, + MtpObjectProperty property, + int groupCode, int depth, + MtpDataPacket& packet) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jobject list = env->CallObjectMethod(mDatabase, method_getObjectPropertyList, + (jint)handle, (jint)format, (jint)property, (jint)groupCode, (jint)depth); + int count = env->GetIntField(list, field_mCount); + MtpResponseCode result = env->GetIntField(list, field_mResult); + + packet.putUInt32(count); + + if (count > 0) { + jintArray objectHandlesArray = (jintArray)env->GetObjectField(list, field_mObjectHandles); + jintArray propertyCodesArray = (jintArray)env->GetObjectField(list, field_mPropertyCodes); + jintArray dataTypesArray = (jintArray)env->GetObjectField(list, field_mDataTypes); + jlongArray longValuesArray = (jlongArray)env->GetObjectField(list, field_mLongValues); + jobjectArray stringValuesArray = (jobjectArray)env->GetObjectField(list, field_mStringValues); + + jint* objectHandles = env->GetIntArrayElements(objectHandlesArray, 0); + jint* propertyCodes = env->GetIntArrayElements(propertyCodesArray, 0); + jint* dataTypes = env->GetIntArrayElements(dataTypesArray, 0); + jlong* longValues = (longValuesArray ? env->GetLongArrayElements(longValuesArray, 0) : NULL); + + for (int i = 0; i < count; i++) { + packet.putUInt32(objectHandles[i]); + packet.putUInt16(propertyCodes[i]); + int type = dataTypes[i]; + packet.putUInt16(type); + + switch (type) { + case MTP_TYPE_INT8: + packet.putInt8(longValues[i]); + break; + case MTP_TYPE_UINT8: + packet.putUInt8(longValues[i]); + break; + case MTP_TYPE_INT16: + packet.putInt16(longValues[i]); + break; + case MTP_TYPE_UINT16: + packet.putUInt16(longValues[i]); + break; + case MTP_TYPE_INT32: + packet.putInt32(longValues[i]); + break; + case MTP_TYPE_UINT32: + packet.putUInt32(longValues[i]); + break; + case MTP_TYPE_INT64: + packet.putInt64(longValues[i]); + break; + case MTP_TYPE_UINT64: + packet.putUInt64(longValues[i]); + break; + case MTP_TYPE_INT128: + packet.putInt128(longValues[i]); + break; + case MTP_TYPE_UINT128: + packet.putUInt128(longValues[i]); + break; + case MTP_TYPE_STR: { + jstring value = (jstring)env->GetObjectArrayElement(stringValuesArray, i); + const char *valueStr = env->GetStringUTFChars(value, NULL); + if (valueStr) { + packet.putString(valueStr); + env->ReleaseStringUTFChars(value, valueStr); + } else { + packet.putEmptyString(); + } + env->DeleteLocalRef(value); + break; + } + default: + LOGE("bad or unsupported data type in MyMtpDatabase::getObjectPropertyList"); + break; + } + } + + env->ReleaseIntArrayElements(objectHandlesArray, objectHandles, 0); + env->ReleaseIntArrayElements(propertyCodesArray, propertyCodes, 0); + env->ReleaseIntArrayElements(dataTypesArray, dataTypes, 0); + if (longValues) + env->ReleaseLongArrayElements(longValuesArray, longValues, 0); + + env->DeleteLocalRef(objectHandlesArray); + env->DeleteLocalRef(propertyCodesArray); + env->DeleteLocalRef(dataTypesArray); + if (longValuesArray) + env->DeleteLocalRef(longValuesArray); + if (stringValuesArray) + env->DeleteLocalRef(stringValuesArray); + } + + env->DeleteLocalRef(list); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return result; +} + MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle, MtpDataPacket& packet) { char date[20]; @@ -894,11 +1037,25 @@ android_media_MtpDatabase_finalize(JNIEnv *env, jobject thiz) #endif } +static jstring +android_media_MtpDatabase_format_date_time(JNIEnv *env, jobject thiz, jlong seconds) +{ +#ifdef HAVE_ANDROID_OS + char date[20]; + formatDateTime(seconds, date, sizeof(date)); + return env->NewStringUTF(date); +#else + return NULL; +#endif +} + // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { {"native_setup", "()V", (void *)android_media_MtpDatabase_setup}, {"native_finalize", "()V", (void *)android_media_MtpDatabase_finalize}, + {"format_date_time", "(J)Ljava/lang/String;", + (void *)android_media_MtpDatabase_format_date_time}, }; static const char* const kClassPathName = "android/media/MtpDatabase"; @@ -954,11 +1111,6 @@ int register_android_media_MtpDatabase(JNIEnv *env) LOGE("Can't find getSupportedDeviceProperties"); return -1; } - method_getObjectProperty = env->GetMethodID(clazz, "getObjectProperty", "(II[J[C)I"); - if (method_getObjectProperty == NULL) { - LOGE("Can't find getObjectProperty"); - return -1; - } method_setObjectProperty = env->GetMethodID(clazz, "setObjectProperty", "(IIJLjava/lang/String;)I"); if (method_setObjectProperty == NULL) { LOGE("Can't find setObjectProperty"); @@ -974,6 +1126,12 @@ int register_android_media_MtpDatabase(JNIEnv *env) LOGE("Can't find setDeviceProperty"); return -1; } + method_getObjectPropertyList = env->GetMethodID(clazz, "getObjectPropertyList", + "(IIIII)Landroid/media/MtpPropertyList;"); + if (method_getObjectPropertyList == NULL) { + LOGE("Can't find getObjectPropertyList"); + return -1; + } method_getObjectInfo = env->GetMethodID(clazz, "getObjectInfo", "(I[I[C[J)Z"); if (method_getObjectInfo == NULL) { LOGE("Can't find getObjectInfo"); @@ -1016,6 +1174,48 @@ int register_android_media_MtpDatabase(JNIEnv *env) return -1; } + // now set up fields for MtpPropertyList class + clazz = env->FindClass("android/media/MtpPropertyList"); + if (clazz == NULL) { + LOGE("Can't find android/media/MtpPropertyList"); + return -1; + } + field_mCount = env->GetFieldID(clazz, "mCount", "I"); + if (field_mCount == NULL) { + LOGE("Can't find MtpPropertyList.mCount"); + return -1; + } + field_mResult = env->GetFieldID(clazz, "mResult", "I"); + if (field_mResult == NULL) { + LOGE("Can't find MtpPropertyList.mResult"); + return -1; + } + field_mObjectHandles = env->GetFieldID(clazz, "mObjectHandles", "[I"); + if (field_mObjectHandles == NULL) { + LOGE("Can't find MtpPropertyList.mObjectHandles"); + return -1; + } + field_mPropertyCodes = env->GetFieldID(clazz, "mPropertyCodes", "[I"); + if (field_mPropertyCodes == NULL) { + LOGE("Can't find MtpPropertyList.mPropertyCodes"); + return -1; + } + field_mDataTypes = env->GetFieldID(clazz, "mDataTypes", "[I"); + if (field_mDataTypes == NULL) { + LOGE("Can't find MtpPropertyList.mDataTypes"); + return -1; + } + field_mLongValues = env->GetFieldID(clazz, "mLongValues", "[J"); + if (field_mLongValues == NULL) { + LOGE("Can't find MtpPropertyList.mLongValues"); + return -1; + } + field_mStringValues = env->GetFieldID(clazz, "mStringValues", "[Ljava/lang/String;"); + if (field_mStringValues == NULL) { + LOGE("Can't find MtpPropertyList.mStringValues"); + return -1; + } + return AndroidRuntime::registerNativeMethods(env, "android/media/MtpDatabase", gMethods, NELEM(gMethods)); } diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp index 1a46715..c287c0a 100644 --- a/media/libmedia/IMediaPlayer.cpp +++ b/media/libmedia/IMediaPlayer.cpp @@ -29,7 +29,6 @@ namespace android { enum { DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, SET_VIDEO_SURFACE, - SET_VIDEO_ISURFACE, PREPARE_ASYNC, START, STOP, @@ -65,15 +64,6 @@ public: remote()->transact(DISCONNECT, data, &reply); } - status_t setVideoISurface(const sp<ISurface>& surface) - { - Parcel data, reply; - data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); - data.writeStrongBinder(surface->asBinder()); - remote()->transact(SET_VIDEO_ISURFACE, data, &reply); - return reply.readInt32(); - } - status_t setVideoSurface(const sp<Surface>& surface) { Parcel data, reply; @@ -245,12 +235,6 @@ status_t BnMediaPlayer::onTransact( disconnect(); return NO_ERROR; } break; - case SET_VIDEO_ISURFACE: { - CHECK_INTERFACE(IMediaPlayer, data, reply); - sp<ISurface> surface = interface_cast<ISurface>(data.readStrongBinder()); - reply->writeInt32(setVideoISurface(surface)); - return NO_ERROR; - } break; case SET_VIDEO_SURFACE: { CHECK_INTERFACE(IMediaPlayer, data, reply); sp<Surface> surface = Surface::readFromParcel(data); diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index f975217..9ce6738 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -31,48 +31,9 @@ enum { FILL_BUFFER, EMPTY_BUFFER, GET_EXTENSION_INDEX, - CREATE_RENDERER, OBSERVER_ON_MSG, - RENDERER_RENDER, }; -sp<IOMXRenderer> IOMX::createRenderer( - const sp<Surface> &surface, - const char *componentName, - OMX_COLOR_FORMATTYPE colorFormat, - size_t encodedWidth, size_t encodedHeight, - size_t displayWidth, size_t displayHeight) { - return createRenderer( - surface->getISurface(), - componentName, colorFormat, encodedWidth, encodedHeight, - displayWidth, displayHeight); -} - -sp<IOMXRenderer> IOMX::createRendererFromJavaSurface( - JNIEnv *env, jobject javaSurface, - const char *componentName, - OMX_COLOR_FORMATTYPE colorFormat, - size_t encodedWidth, size_t encodedHeight, - size_t displayWidth, size_t displayHeight) { - jclass surfaceClass = env->FindClass("android/view/Surface"); - if (surfaceClass == NULL) { - LOGE("Can't find android/view/Surface"); - return NULL; - } - - jfieldID surfaceID = env->GetFieldID(surfaceClass, ANDROID_VIEW_SURFACE_JNI_ID, "I"); - if (surfaceID == NULL) { - LOGE("Can't find Surface.mSurface"); - return NULL; - } - - sp<Surface> surface = (Surface *)env->GetIntField(javaSurface, surfaceID); - - return createRenderer( - surface, componentName, colorFormat, encodedWidth, - encodedHeight, displayWidth, displayHeight); -} - class BpOMX : public BpInterface<IOMX> { public: BpOMX(const sp<IBinder> &impl) @@ -395,28 +356,6 @@ public: return err; } - - virtual sp<IOMXRenderer> createRenderer( - const sp<ISurface> &surface, - const char *componentName, - OMX_COLOR_FORMATTYPE colorFormat, - size_t encodedWidth, size_t encodedHeight, - size_t displayWidth, size_t displayHeight) { - Parcel data, reply; - data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); - - data.writeStrongBinder(surface->asBinder()); - data.writeCString(componentName); - data.writeInt32(colorFormat); - data.writeInt32(encodedWidth); - data.writeInt32(encodedHeight); - data.writeInt32(displayWidth); - data.writeInt32(displayHeight); - - remote()->transact(CREATE_RENDERER, data, &reply); - - return interface_cast<IOMXRenderer>(reply.readStrongBinder()); - } }; IMPLEMENT_META_INTERFACE(OMX, "android.hardware.IOMX"); @@ -767,33 +706,6 @@ status_t BnOMX::onTransact( return OK; } - case CREATE_RENDERER: - { - CHECK_INTERFACE(IOMX, data, reply); - - sp<ISurface> isurface = - interface_cast<ISurface>(data.readStrongBinder()); - - const char *componentName = data.readCString(); - - OMX_COLOR_FORMATTYPE colorFormat = - static_cast<OMX_COLOR_FORMATTYPE>(data.readInt32()); - - size_t encodedWidth = (size_t)data.readInt32(); - size_t encodedHeight = (size_t)data.readInt32(); - size_t displayWidth = (size_t)data.readInt32(); - size_t displayHeight = (size_t)data.readInt32(); - - sp<IOMXRenderer> renderer = - createRenderer(isurface, componentName, colorFormat, - encodedWidth, encodedHeight, - displayWidth, displayHeight); - - reply->writeStrongBinder(renderer->asBinder()); - - return OK; - } - default: return BBinder::onTransact(code, data, reply, flags); } @@ -839,44 +751,4 @@ status_t BnOMXObserver::onTransact( } } -//////////////////////////////////////////////////////////////////////////////// - -class BpOMXRenderer : public BpInterface<IOMXRenderer> { -public: - BpOMXRenderer(const sp<IBinder> &impl) - : BpInterface<IOMXRenderer>(impl) { - } - - virtual void render(IOMX::buffer_id buffer) { - Parcel data, reply; - data.writeInterfaceToken(IOMXRenderer::getInterfaceDescriptor()); - data.writeIntPtr((intptr_t)buffer); - - // NOTE: Do NOT make this a ONE_WAY call, it must be synchronous - // so that the caller knows when to recycle the buffer. - remote()->transact(RENDERER_RENDER, data, &reply); - } -}; - -IMPLEMENT_META_INTERFACE(OMXRenderer, "android.hardware.IOMXRenderer"); - -status_t BnOMXRenderer::onTransact( - uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { - switch (code) { - case RENDERER_RENDER: - { - CHECK_INTERFACE(IOMXRenderer, data, reply); - - IOMX::buffer_id buffer = (void*)data.readIntPtr(); - - render(buffer); - - return NO_ERROR; - } - - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - } // namespace android diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 34e41a1..54b292c 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -198,13 +198,6 @@ status_t MediaPlayer::setVideoSurface(const sp<Surface>& surface) Mutex::Autolock _l(mLock); if (mPlayer == 0) return NO_INIT; - status_t err = mPlayer->setVideoISurface( - surface == NULL ? NULL : surface->getISurface()); - - if (err != OK) { - return err; - } - return mPlayer->setVideoSurface(surface); } diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index 55846be..3341ff7 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -38,14 +38,6 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_STATIC_LIBRARIES := \ libstagefright_rtsp -ifneq ($(BUILD_WITHOUT_PV),true) -LOCAL_SHARED_LIBRARIES += \ - libopencore_player \ - libopencore_author -else -LOCAL_CFLAGS += -DNO_OPENCORE -endif - ifneq ($(TARGET_SIMULATOR),true) LOCAL_SHARED_LIBRARIES += libdl endif diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index bb86e05..00e510b 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -56,7 +56,6 @@ #include "MetadataRetrieverClient.h" #include "MidiFile.h" -#include <media/PVPlayer.h> #include "TestPlayerStub.h" #include "StagefrightPlayer.h" @@ -196,11 +195,6 @@ extmap FILE_EXTS [] = { {".rtttl", SONIVOX_PLAYER}, {".rtx", SONIVOX_PLAYER}, {".ota", SONIVOX_PLAYER}, -#ifndef NO_OPENCORE - {".wma", PV_PLAYER}, - {".wmv", PV_PLAYER}, - {".asf", PV_PLAYER}, -#endif }; // TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround @@ -691,14 +685,6 @@ player_type getPlayerType(int fd, int64_t offset, int64_t length) if (ident == 0x5367674f) // 'OggS' return STAGEFRIGHT_PLAYER; -#ifndef NO_OPENCORE - if (ident == 0x75b22630) { - // The magic number for .asf files, i.e. wmv and wma content. - // These are not currently supported through stagefright. - return PV_PLAYER; - } -#endif - // Some kind of MIDI? EAS_DATA_HANDLE easdata; if (EAS_Init(&easdata) == EAS_SUCCESS) { @@ -737,16 +723,6 @@ player_type getPlayerType(const char* url) } } - if (!strncasecmp(url, "rtsp://", 7)) { - char value[PROPERTY_VALUE_MAX]; - if (property_get("media.stagefright.enable-rtsp", value, NULL) - && (strcmp(value, "1") && strcasecmp(value, "true"))) { - // For now, we're going to use PV for rtsp-based playback - // by default until we can clear up a few more issues. - return PV_PLAYER; - } - } - return getDefaultPlayerType(); } @@ -755,12 +731,6 @@ static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie, { sp<MediaPlayerBase> p; switch (playerType) { -#ifndef NO_OPENCORE - case PV_PLAYER: - LOGV(" create PVPlayer"); - p = new PVPlayer(); - break; -#endif case SONIVOX_PLAYER: LOGV(" create MidiFile"); p = new MidiFile(); @@ -773,6 +743,9 @@ static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie, LOGV("Create Test Player stub"); p = new TestPlayerStub(); break; + default: + LOGE("Unknown player type: %d", playerType); + return NULL; } if (p != NULL) { if (p->initCheck() == NO_ERROR) { @@ -891,14 +864,6 @@ status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64 return mStatus; } -status_t MediaPlayerService::Client::setVideoISurface(const sp<ISurface>& surface) -{ - LOGV("[%d] setVideoISurface(%p)", mConnId, surface.get()); - sp<MediaPlayerBase> p = getPlayer(); - if (p == 0) return UNKNOWN_ERROR; - return p->setVideoISurface(surface); -} - status_t MediaPlayerService::Client::setVideoSurface(const sp<Surface>& surface) { LOGV("[%d] setVideoSurface(%p)", mConnId, surface.get()); diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index e197cde..184324c 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -206,7 +206,6 @@ private: // IMediaPlayer interface virtual void disconnect(); - virtual status_t setVideoISurface(const sp<ISurface>& surface); virtual status_t setVideoSurface(const sp<Surface>& surface); virtual status_t prepareAsync(); virtual status_t start(); diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index be6a8be..1a1780c 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -31,10 +31,6 @@ #include <binder/MemoryHeapBase.h> #include <binder/MemoryBase.h> -#ifndef NO_OPENCORE -#include <media/PVMediaRecorder.h> -#endif - #include <utils/String16.h> #include <media/AudioTrack.h> @@ -304,22 +300,7 @@ MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service, { LOGV("Client constructor"); mPid = pid; - - char value[PROPERTY_VALUE_MAX]; - if (!property_get("media.stagefright.enable-record", value, NULL) - || !strcmp(value, "1") || !strcasecmp(value, "true")) { - mRecorder = new StagefrightRecorder; - } else -#ifndef NO_OPENCORE - { - mRecorder = new PVMediaRecorder(); - } -#else - { - mRecorder = NULL; - } -#endif - + mRecorder = new StagefrightRecorder; mMediaPlayerService = service; } diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp index 39fce81..b069345 100644 --- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp +++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp @@ -35,7 +35,6 @@ #include <binder/IServiceManager.h> #include <media/MediaMetadataRetrieverInterface.h> #include <media/MediaPlayerInterface.h> -#include <media/PVMetadataRetriever.h> #include <private/media/VideoFrame.h> #include "MidiMetadataRetriever.h" #include "MetadataRetrieverClient.h" @@ -107,12 +106,6 @@ static sp<MediaMetadataRetrieverBase> createRetriever(player_type playerType) p = new StagefrightMetadataRetriever; break; } -#ifndef NO_OPENCORE - case PV_PLAYER: - LOGV("create pv metadata retriever"); - p = new PVMetadataRetriever(); - break; -#endif case SONIVOX_PLAYER: LOGV("create midi metadata retriever"); p = new MidiMetadataRetriever(); diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h index 06e4b70..aa8f3f0 100644 --- a/media/libmediaplayerservice/MidiFile.h +++ b/media/libmediaplayerservice/MidiFile.h @@ -35,7 +35,6 @@ public: const char* path, const KeyedVector<String8, String8> *headers); virtual status_t setDataSource(int fd, int64_t offset, int64_t length); - virtual status_t setVideoISurface(const sp<ISurface>& surface) { return UNKNOWN_ERROR; } virtual status_t setVideoSurface(const sp<Surface>& surface) { return UNKNOWN_ERROR; } virtual status_t prepare(); virtual status_t prepareAsync(); diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp index e0957f6..58ef99b 100644 --- a/media/libmediaplayerservice/StagefrightPlayer.cpp +++ b/media/libmediaplayerservice/StagefrightPlayer.cpp @@ -44,13 +44,6 @@ status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length return mPlayer->setDataSource(dup(fd), offset, length); } -status_t StagefrightPlayer::setVideoISurface(const sp<ISurface> &surface) { - LOGV("setVideoISurface"); - - mPlayer->setISurface(surface); - return OK; -} - status_t StagefrightPlayer::setVideoSurface(const sp<Surface> &surface) { LOGV("setVideoSurface"); diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h index 3899447..c4a2588 100644 --- a/media/libmediaplayerservice/StagefrightPlayer.h +++ b/media/libmediaplayerservice/StagefrightPlayer.h @@ -35,7 +35,6 @@ public: const char *url, const KeyedVector<String8, String8> *headers); virtual status_t setDataSource(int fd, int64_t offset, int64_t length); - virtual status_t setVideoISurface(const sp<ISurface> &surface); virtual status_t setVideoSurface(const sp<Surface> &surface); virtual status_t prepare(); virtual status_t prepareAsync(); diff --git a/media/libmediaplayerservice/TestPlayerStub.h b/media/libmediaplayerservice/TestPlayerStub.h index 5eaf592..6abd8e3 100644 --- a/media/libmediaplayerservice/TestPlayerStub.h +++ b/media/libmediaplayerservice/TestPlayerStub.h @@ -75,9 +75,6 @@ class TestPlayerStub : public MediaPlayerInterface { // All the methods below wrap the mPlayer instance. - virtual status_t setVideoISurface(const android::sp<android::ISurface>& s) { - return mPlayer->setVideoISurface(s); - } virtual status_t setVideoSurface(const android::sp<android::Surface>& s) { return mPlayer->setVideoSurface(s); } diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 1df0bed..8fe1d4d 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -68,7 +68,8 @@ LOCAL_SHARED_LIBRARIES := \ libsurfaceflinger_client \ libstagefright_yuv \ libcamera_client \ - libdrmframework + libdrmframework \ + libcrypto LOCAL_STATIC_LIBRARIES := \ libstagefright_aacdec \ diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index f084e28..ff9f255 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -49,6 +49,8 @@ #include <media/stagefright/foundation/ALooper.h> +#define USE_SURFACE_ALLOC 1 + namespace android { static int64_t kLowWaterMarkUs = 2000000ll; // 2secs @@ -77,39 +79,16 @@ private: AwesomeEvent &operator=(const AwesomeEvent &); }; -struct AwesomeRemoteRenderer : public AwesomeRenderer { - AwesomeRemoteRenderer(const sp<IOMXRenderer> &target) - : mTarget(target) { - } - - virtual void render(MediaBuffer *buffer) { - void *id; - if (buffer->meta_data()->findPointer(kKeyBufferID, &id)) { - mTarget->render((IOMX::buffer_id)id); - } - } - -private: - sp<IOMXRenderer> mTarget; - - AwesomeRemoteRenderer(const AwesomeRemoteRenderer &); - AwesomeRemoteRenderer &operator=(const AwesomeRemoteRenderer &); -}; - struct AwesomeLocalRenderer : public AwesomeRenderer { AwesomeLocalRenderer( - bool previewOnly, - const char *componentName, OMX_COLOR_FORMATTYPE colorFormat, - const sp<ISurface> &isurface, const sp<Surface> &surface, size_t displayWidth, size_t displayHeight, size_t decodedWidth, size_t decodedHeight) - : mTarget(NULL), - mLibHandle(NULL) { - init(previewOnly, componentName, - colorFormat, isurface, surface, displayWidth, - displayHeight, decodedWidth, decodedHeight); + : mTarget(NULL) { + init(colorFormat, surface, + displayWidth, displayHeight, + decodedWidth, decodedHeight); } virtual void render(MediaBuffer *buffer) { @@ -125,22 +104,13 @@ protected: virtual ~AwesomeLocalRenderer() { delete mTarget; mTarget = NULL; - - if (mLibHandle) { - dlclose(mLibHandle); - mLibHandle = NULL; - } } private: - VideoRenderer *mTarget; - void *mLibHandle; + SoftwareRenderer *mTarget; void init( - bool previewOnly, - const char *componentName, OMX_COLOR_FORMATTYPE colorFormat, - const sp<ISurface> &isurface, const sp<Surface> &surface, size_t displayWidth, size_t displayHeight, size_t decodedWidth, size_t decodedHeight); @@ -150,48 +120,13 @@ private: }; void AwesomeLocalRenderer::init( - bool previewOnly, - const char *componentName, OMX_COLOR_FORMATTYPE colorFormat, - const sp<ISurface> &isurface, const sp<Surface> &surface, size_t displayWidth, size_t displayHeight, size_t decodedWidth, size_t decodedHeight) { - if (!previewOnly) { - // We will stick to the vanilla software-color-converting renderer - // for "previewOnly" mode, to avoid unneccessarily switching overlays - // more often than necessary. - - mLibHandle = dlopen("libstagefrighthw.so", RTLD_NOW); - - if (mLibHandle) { - typedef VideoRenderer *(*CreateRendererFunc)( - const sp<ISurface> &surface, - const char *componentName, - OMX_COLOR_FORMATTYPE colorFormat, - size_t displayWidth, size_t displayHeight, - size_t decodedWidth, size_t decodedHeight); - - CreateRendererFunc func = - (CreateRendererFunc)dlsym( - mLibHandle, - "_Z14createRendererRKN7android2spINS_8ISurfaceEEEPKc20" - "OMX_COLOR_FORMATTYPEjjjj"); - - if (func) { - mTarget = - (*func)(isurface, componentName, colorFormat, - displayWidth, displayHeight, - decodedWidth, decodedHeight); - } - } - } - - if (mTarget == NULL) { - mTarget = new SoftwareRenderer( - colorFormat, surface, displayWidth, displayHeight, - decodedWidth, decodedHeight); - } + mTarget = new SoftwareRenderer( + colorFormat, surface, displayWidth, displayHeight, + decodedWidth, decodedHeight); } struct AwesomeNativeWindowRenderer : public AwesomeRenderer { @@ -294,6 +229,16 @@ status_t AwesomePlayer::setDataSource_l( mUri = uri; + if (!strncmp("http://", uri, 7)) { + // Hack to support http live. + + size_t len = strlen(uri); + if (!strcasecmp(&uri[len - 5], ".m3u8")) { + mUri = "httplive://"; + mUri.append(&uri[7]); + } + } + if (headers) { mUriHeaders = *headers; } @@ -855,54 +800,41 @@ void AwesomePlayer::notifyVideoSize_l() { } void AwesomePlayer::initRenderer_l() { - if (mSurface != NULL || mISurface != NULL) { - sp<MetaData> meta = mVideoSource->getFormat(); + if (mSurface == NULL) { + return; + } - int32_t format; - const char *component; - int32_t decodedWidth, decodedHeight; - CHECK(meta->findInt32(kKeyColorFormat, &format)); - CHECK(meta->findCString(kKeyDecoderComponent, &component)); - CHECK(meta->findInt32(kKeyWidth, &decodedWidth)); - CHECK(meta->findInt32(kKeyHeight, &decodedHeight)); + sp<MetaData> meta = mVideoSource->getFormat(); - mVideoRenderer.clear(); + int32_t format; + const char *component; + int32_t decodedWidth, decodedHeight; + CHECK(meta->findInt32(kKeyColorFormat, &format)); + CHECK(meta->findCString(kKeyDecoderComponent, &component)); + CHECK(meta->findInt32(kKeyWidth, &decodedWidth)); + CHECK(meta->findInt32(kKeyHeight, &decodedHeight)); - // Must ensure that mVideoRenderer's destructor is actually executed - // before creating a new one. - IPCThreadState::self()->flushCommands(); + mVideoRenderer.clear(); - if (mSurface != NULL) { - if (strncmp(component, "OMX.", 4) == 0) { - // Hardware decoders avoid the CPU color conversion by decoding - // directly to ANativeBuffers, so we must use a renderer that - // just pushes those buffers to the ANativeWindow. - mVideoRenderer = new AwesomeNativeWindowRenderer(mSurface); - } else { - // Other decoders are instantiated locally and as a consequence - // allocate their buffers in local address space. This renderer - // then performs a color conversion and copy to get the data - // into the ANativeBuffer. - mVideoRenderer = new AwesomeLocalRenderer( - false, // previewOnly - component, - (OMX_COLOR_FORMATTYPE)format, - mISurface, - mSurface, - mVideoWidth, mVideoHeight, - decodedWidth, decodedHeight); - } - } else { - // Our OMX codecs allocate buffers on the media_server side - // therefore they require a remote IOMXRenderer that knows how - // to display them. - mVideoRenderer = new AwesomeRemoteRenderer( - mClient.interface()->createRenderer( - mISurface, component, - (OMX_COLOR_FORMATTYPE)format, - decodedWidth, decodedHeight, - mVideoWidth, mVideoHeight)); - } + // Must ensure that mVideoRenderer's destructor is actually executed + // before creating a new one. + IPCThreadState::self()->flushCommands(); + + if (USE_SURFACE_ALLOC && strncmp(component, "OMX.", 4) == 0) { + // Hardware decoders avoid the CPU color conversion by decoding + // directly to ANativeBuffers, so we must use a renderer that + // just pushes those buffers to the ANativeWindow. + mVideoRenderer = new AwesomeNativeWindowRenderer(mSurface); + } else { + // Other decoders are instantiated locally and as a consequence + // allocate their buffers in local address space. This renderer + // then performs a color conversion and copy to get the data + // into the ANativeBuffer. + mVideoRenderer = new AwesomeLocalRenderer( + (OMX_COLOR_FORMATTYPE)format, + mSurface, + mVideoWidth, mVideoHeight, + decodedWidth, decodedHeight); } } @@ -946,12 +878,6 @@ bool AwesomePlayer::isPlaying() const { return (mFlags & PLAYING) || (mFlags & CACHE_UNDERRUN); } -void AwesomePlayer::setISurface(const sp<ISurface> &isurface) { - Mutex::Autolock autoLock(mLock); - - mISurface = isurface; -} - void AwesomePlayer::setSurface(const sp<Surface> &surface) { Mutex::Autolock autoLock(mLock); @@ -1143,7 +1069,7 @@ status_t AwesomePlayer::initVideoDecoder(uint32_t flags) { mClient.interface(), mVideoTrack->getFormat(), false, // createEncoder mVideoTrack, - NULL, flags, mSurface); + NULL, flags, USE_SURFACE_ALLOC ? mSurface : NULL); if (mVideoSource != NULL) { int64_t durationUs; @@ -1885,13 +1811,10 @@ status_t AwesomePlayer::resume() { mFlags = state->mFlags & (AUTO_LOOPING | LOOPING | AT_EOS); - if (state->mLastVideoFrame && (mSurface != NULL || mISurface != NULL)) { + if (state->mLastVideoFrame && mSurface != NULL) { mVideoRenderer = new AwesomeLocalRenderer( - true, // previewOnly - "", (OMX_COLOR_FORMATTYPE)state->mColorFormat, - mISurface, mSurface, state->mVideoWidth, state->mVideoHeight, diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index b8450fb..d9ff723 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -495,26 +495,6 @@ status_t CameraSource::init( mIsMetaDataStoredInVideoBuffers = true; } - /* - * mCamera->startRecording() signals camera hal to make - * available the video buffers (for instance, allocation - * of the video buffers may be triggered when camera hal's - * startRecording() method is called). Making available these - * video buffers earlier (before calling start()) is critical, - * if one wants to configure omx video encoders to use these - * buffers for passing video frame data during video recording - * without the need to memcpy the video frame data stored - * in these buffers. Eliminating memcpy for video frame data - * is crucial in performance for HD quality video recording - * applications. - * - * Based on OMX IL spec, configuring the omx video encoders - * must occur in loaded state. When start() is called, omx - * video encoders are already in idle state, which is too - * late. Thus, we must call mCamera->startRecording() earlier. - */ - startCameraRecording(); - IPCThreadState::self()->restoreCallingIdentity(token); int64_t glitchDurationUs = (1000000LL / mVideoFrameRate); @@ -565,8 +545,11 @@ status_t CameraSource::start(MetaData *meta) { mStartTimeUs = startTimeUs; } + // Call setListener first before calling startCameraRecording() + // to avoid recording frames being dropped. int64_t token = IPCThreadState::self()->clearCallingIdentity(); mCamera->setListener(new CameraSourceListener(this)); + startCameraRecording(); IPCThreadState::self()->restoreCallingIdentity(token); mStarted = true; diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp index 7aac447..133f225 100644 --- a/media/libstagefright/NuHTTPDataSource.cpp +++ b/media/libstagefright/NuHTTPDataSource.cpp @@ -5,6 +5,7 @@ #include "include/NuHTTPDataSource.h" #include <cutils/properties.h> +#include <media/stagefright/foundation/ALooper.h> #include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaErrors.h> @@ -68,6 +69,9 @@ NuHTTPDataSource::NuHTTPDataSource() mOffset(0), mContentLength(0), mContentLengthValid(false), + mNumBandwidthHistoryItems(0), + mTotalTransferTimeUs(0), + mTotalTransferBytes(0), mDecryptHandle(NULL), mDrmManagerClient(NULL) { } @@ -189,6 +193,20 @@ status_t NuHTTPDataSource::connect( return ERROR_IO; } + { + string value; + if (mHTTP.find_header_value("Transfer-Encoding", &value)) { + // We don't currently support any transfer encodings. + + mState = DISCONNECTED; + mHTTP.disconnect(); + + LOGE("We don't support '%s' transfer encoding.", value.c_str()); + + return ERROR_UNSUPPORTED; + } + } + applyTimeoutResponse(); if (offset == 0) { @@ -254,6 +272,8 @@ ssize_t NuHTTPDataSource::readAt(off_t offset, void *data, size_t size) { size_t numBytesRead = 0; while (numBytesRead < size) { + int64_t startTimeUs = ALooper::GetNowUs(); + ssize_t n = mHTTP.receive((uint8_t *)data + numBytesRead, size - numBytesRead); @@ -261,6 +281,9 @@ ssize_t NuHTTPDataSource::readAt(off_t offset, void *data, size_t size) { return n; } + int64_t delayUs = ALooper::GetNowUs() - startTimeUs; + addBandwidthMeasurement_l(n, delayUs); + numBytesRead += (size_t)n; if (n == 0) { @@ -345,6 +368,36 @@ void NuHTTPDataSource::applyTimeoutResponse() { } } +bool NuHTTPDataSource::estimateBandwidth(int32_t *bandwidth_bps) { + Mutex::Autolock autoLock(mLock); + + if (mNumBandwidthHistoryItems < 10) { + return false; + } + + *bandwidth_bps = ((double)mTotalTransferBytes * 8E6 / mTotalTransferTimeUs); + + return true; +} + +void NuHTTPDataSource::addBandwidthMeasurement_l( + size_t numBytes, int64_t delayUs) { + BandwidthEntry entry; + entry.mDelayUs = delayUs; + entry.mNumBytes = numBytes; + mTotalTransferTimeUs += delayUs; + mTotalTransferBytes += numBytes; + + mBandwidthHistory.push_back(entry); + if (++mNumBandwidthHistoryItems > 100) { + BandwidthEntry *entry = &*mBandwidthHistory.begin(); + mTotalTransferTimeUs -= entry->mDelayUs; + mTotalTransferBytes -= entry->mNumBytes; + mBandwidthHistory.erase(mBandwidthHistory.begin()); + --mNumBandwidthHistoryItems; + } +} + DecryptHandle* NuHTTPDataSource::DrmInitialization(DrmManagerClient* client) { if (client == NULL) { return NULL; diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 8edcd12..5ed4d84 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -54,6 +54,7 @@ #include <OMX_Component.h> #include "include/ThreadedSource.h" +#include "include/avc_utils.h" namespace android { @@ -264,39 +265,6 @@ static const char *GetCodec(const CodecInfo *info, size_t numInfos, return NULL; } -enum { - kAVCProfileBaseline = 0x42, - kAVCProfileMain = 0x4d, - kAVCProfileExtended = 0x58, - kAVCProfileHigh = 0x64, - kAVCProfileHigh10 = 0x6e, - kAVCProfileHigh422 = 0x7a, - kAVCProfileHigh444 = 0xf4, - kAVCProfileCAVLC444Intra = 0x2c -}; - -static const char *AVCProfileToString(uint8_t profile) { - switch (profile) { - case kAVCProfileBaseline: - return "Baseline"; - case kAVCProfileMain: - return "Main"; - case kAVCProfileExtended: - return "Extended"; - case kAVCProfileHigh: - return "High"; - case kAVCProfileHigh10: - return "High 10"; - case kAVCProfileHigh422: - return "High 422"; - case kAVCProfileHigh444: - return "High 444"; - case kAVCProfileCAVLC444Intra: - return "CAVLC 444 Intra"; - default: return "Unknown"; - } -} - template<class T> static void InitOMXParams(T *params) { params->nSize = sizeof(T); diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp index 478e40c..2fe5e18 100644 --- a/media/libstagefright/avc_utils.cpp +++ b/media/libstagefright/avc_utils.cpp @@ -218,6 +218,28 @@ static sp<ABuffer> FindNAL( return NULL; } +const char *AVCProfileToString(uint8_t profile) { + switch (profile) { + case kAVCProfileBaseline: + return "Baseline"; + case kAVCProfileMain: + return "Main"; + case kAVCProfileExtended: + return "Extended"; + case kAVCProfileHigh: + return "High"; + case kAVCProfileHigh10: + return "High 10"; + case kAVCProfileHigh422: + return "High 422"; + case kAVCProfileHigh444: + return "High 444"; + case kAVCProfileCAVLC444Intra: + return "CAVLC 444 Intra"; + default: return "Unknown"; + } +} + sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit) { const uint8_t *data = accessUnit->data(); size_t size = accessUnit->size(); @@ -244,6 +266,10 @@ sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit) { *out++ = 0x01; // configurationVersion memcpy(out, seqParamSet->data() + 1, 3); // profile/level... + + uint8_t profile = out[0]; + uint8_t level = out[2]; + out += 3; *out++ = (0x3f << 2) | 1; // lengthSize == 2 bytes *out++ = 0xe0 | 1; @@ -271,7 +297,8 @@ sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit) { meta->setInt32(kKeyWidth, width); meta->setInt32(kKeyHeight, height); - LOGI("found AVC codec config (%d x %d)", width, height); + LOGI("found AVC codec config (%d x %d, %s-profile level %d.%d)", + width, height, AVCProfileToString(profile), level / 10, level % 10); return meta; } diff --git a/media/libstagefright/codecs/aacenc/AACEncoder.cpp b/media/libstagefright/codecs/aacenc/AACEncoder.cpp index df9f107..9524884 100644 --- a/media/libstagefright/codecs/aacenc/AACEncoder.cpp +++ b/media/libstagefright/codecs/aacenc/AACEncoder.cpp @@ -36,6 +36,7 @@ AACEncoder::AACEncoder(const sp<MediaSource> &source, const sp<MetaData> &meta) mStarted(false), mBufferGroup(NULL), mInputBuffer(NULL), + mInputFrame(NULL), mEncoderHandle(NULL), mApiHandle(NULL), mMemOperator(NULL) { @@ -45,6 +46,7 @@ status_t AACEncoder::initCheck() { CHECK(mApiHandle == NULL && mEncoderHandle == NULL); CHECK(mMeta->findInt32(kKeySampleRate, &mSampleRate)); CHECK(mMeta->findInt32(kKeyChannelCount, &mChannels)); + CHECK(mChannels <= 2 && mChannels >= 1); CHECK(mMeta->findInt32(kKeyBitRate, &mBitRate)); mApiHandle = new VO_AUDIO_CODECAPI; @@ -145,6 +147,10 @@ status_t AACEncoder::start(MetaData *params) { mNumInputSamples = 0; mAnchorTimeUs = 0; mFrameCount = 0; + + mInputFrame = new int16_t[mChannels * kNumSamplesPerFrame]; + CHECK(mInputFrame != NULL); + mSource->start(params); mStarted = true; @@ -176,6 +182,10 @@ status_t AACEncoder::stop() { mApiHandle = NULL; mStarted = false; + if (mInputFrame) { + delete[] mInputFrame; + mInputFrame = NULL; + } return OK; } @@ -222,7 +232,8 @@ status_t AACEncoder::read( buffer->meta_data()->setInt32(kKeyIsCodecConfig, false); } - while (mNumInputSamples < kNumSamplesPerFrame) { + const int32_t nSamples = mChannels * kNumSamplesPerFrame; + while (mNumInputSamples < nSamples) { if (mInputBuffer == NULL) { if (mSource->read(&mInputBuffer, options) != OK) { if (mNumInputSamples == 0) { @@ -231,7 +242,7 @@ status_t AACEncoder::read( } memset(&mInputFrame[mNumInputSamples], 0, - sizeof(int16_t) * (kNumSamplesPerFrame - mNumInputSamples)); + sizeof(int16_t) * (nSamples - mNumInputSamples)); mNumInputSamples = 0; break; } @@ -250,8 +261,7 @@ status_t AACEncoder::read( } else { readFromSource = false; } - size_t copy = - (kNumSamplesPerFrame - mNumInputSamples) * sizeof(int16_t); + size_t copy = (nSamples - mNumInputSamples) * sizeof(int16_t); if (copy > mInputBuffer->range_length()) { copy = mInputBuffer->range_length(); @@ -271,8 +281,8 @@ status_t AACEncoder::read( mInputBuffer = NULL; } mNumInputSamples += copy / sizeof(int16_t); - if (mNumInputSamples >= kNumSamplesPerFrame) { - mNumInputSamples %= kNumSamplesPerFrame; + if (mNumInputSamples >= nSamples) { + mNumInputSamples %= nSamples; break; } } @@ -280,7 +290,7 @@ status_t AACEncoder::read( VO_CODECBUFFER inputData; memset(&inputData, 0, sizeof(inputData)); inputData.Buffer = (unsigned char*) mInputFrame; - inputData.Length = kNumSamplesPerFrame * sizeof(int16_t); + inputData.Length = nSamples * sizeof(int16_t); CHECK(VO_ERR_NONE == mApiHandle->SetInputData(mEncoderHandle,&inputData)); VO_CODECBUFFER outputData; @@ -289,15 +299,21 @@ status_t AACEncoder::read( memset(&outputInfo, 0, sizeof(outputInfo)); VO_U32 ret = VO_ERR_NONE; - outputData.Buffer = outPtr; - outputData.Length = buffer->size(); - ret = mApiHandle->GetOutputData(mEncoderHandle, &outputData, &outputInfo); - CHECK(ret == VO_ERR_NONE || ret == VO_ERR_INPUT_BUFFER_SMALL); - CHECK(outputData.Length != 0); - buffer->set_range(0, outputData.Length); + size_t nOutputBytes = 0; + do { + outputData.Buffer = outPtr; + outputData.Length = buffer->size() - nOutputBytes; + ret = mApiHandle->GetOutputData(mEncoderHandle, &outputData, &outputInfo); + if (ret == VO_ERR_NONE) { + outPtr += outputData.Length; + nOutputBytes += outputData.Length; + } + } while (ret != VO_ERR_INPUT_BUFFER_SMALL); + buffer->set_range(0, nOutputBytes); int64_t mediaTimeUs = ((mFrameCount - 1) * 1000000LL * kNumSamplesPerFrame) / mSampleRate; + buffer->meta_data()->setInt64(kKeyTime, mAnchorTimeUs + mediaTimeUs); if (readFromSource && wallClockTimeUs != -1) { buffer->meta_data()->setInt64(kKeyDriftTime, mediaTimeUs - wallClockTimeUs); diff --git a/media/libstagefright/httplive/Android.mk b/media/libstagefright/httplive/Android.mk index cc7dd4f..3aabf5f 100644 --- a/media/libstagefright/httplive/Android.mk +++ b/media/libstagefright/httplive/Android.mk @@ -9,7 +9,8 @@ LOCAL_SRC_FILES:= \ LOCAL_C_INCLUDES:= \ $(JNI_H_INCLUDE) \ $(TOP)/frameworks/base/include/media/stagefright/openmax \ - $(TOP)/frameworks/base/media/libstagefright + $(TOP)/frameworks/base/media/libstagefright \ + $(TOP)/external/openssl/include LOCAL_MODULE:= libstagefright_httplive diff --git a/media/libstagefright/httplive/LiveSource.cpp b/media/libstagefright/httplive/LiveSource.cpp index 4124571..f9d27eb 100644 --- a/media/libstagefright/httplive/LiveSource.cpp +++ b/media/libstagefright/httplive/LiveSource.cpp @@ -22,9 +22,14 @@ #include "include/M3UParser.h" #include "include/NuHTTPDataSource.h" +#include <cutils/properties.h> +#include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/FileSource.h> -#include <media/stagefright/MediaDebug.h> + +#include <ctype.h> +#include <openssl/aes.h> namespace android { @@ -38,7 +43,9 @@ LiveSource::LiveSource(const char *url) mSourceSize(0), mOffsetBias(0), mSignalDiscontinuity(false), - mPrevBandwidthIndex(-1) { + mPrevBandwidthIndex(-1), + mAESKey((AES_KEY *)malloc(sizeof(AES_KEY))), + mStreamEncrypted(false) { if (switchToNext()) { mInitCheck = OK; @@ -47,6 +54,8 @@ LiveSource::LiveSource(const char *url) } LiveSource::~LiveSource() { + free(mAESKey); + mAESKey = NULL; } status_t LiveSource::initCheck() const { @@ -68,7 +77,77 @@ static double uniformRand() { return (double)rand() / RAND_MAX; } -bool LiveSource::loadPlaylist(bool fetchMaster) { +size_t LiveSource::getBandwidthIndex() { + if (mBandwidthItems.size() == 0) { + return 0; + } + +#if 1 + int32_t bandwidthBps; + if (mSource != NULL && mSource->estimateBandwidth(&bandwidthBps)) { + LOGI("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f); + } else { + LOGI("no bandwidth estimate."); + return 0; // Pick the lowest bandwidth stream by default. + } + + char value[PROPERTY_VALUE_MAX]; + if (property_get("media.httplive.max-bw", value, NULL)) { + char *end; + long maxBw = strtoul(value, &end, 10); + if (end > value && *end == '\0') { + if (maxBw > 0 && bandwidthBps > maxBw) { + LOGV("bandwidth capped to %ld bps", maxBw); + bandwidthBps = maxBw; + } + } + } + + // Consider only 80% of the available bandwidth usable. + bandwidthBps = (bandwidthBps * 8) / 10; + + // Pick the highest bandwidth stream below or equal to estimated bandwidth. + + size_t index = mBandwidthItems.size() - 1; + while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth + > (size_t)bandwidthBps) { + --index; + } +#elif 0 + // Change bandwidth at random() + size_t index = uniformRand() * mBandwidthItems.size(); +#elif 0 + // There's a 50% chance to stay on the current bandwidth and + // a 50% chance to switch to the next higher bandwidth (wrapping around + // to lowest) + const size_t kMinIndex = 0; + + size_t index; + if (mPrevBandwidthIndex < 0) { + index = kMinIndex; + } else if (uniformRand() < 0.5) { + index = (size_t)mPrevBandwidthIndex; + } else { + index = mPrevBandwidthIndex + 1; + if (index == mBandwidthItems.size()) { + index = kMinIndex; + } + } +#elif 0 + // Pick the highest bandwidth stream below or equal to 1.2 Mbit/sec + + size_t index = mBandwidthItems.size() - 1; + while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth > 1200000) { + --index; + } +#else + size_t index = mBandwidthItems.size() - 1; // Highest bandwidth stream +#endif + + return index; +} + +bool LiveSource::loadPlaylist(bool fetchMaster, size_t bandwidthIndex) { mSignalDiscontinuity = false; mPlaylist.clear(); @@ -112,49 +191,35 @@ bool LiveSource::loadPlaylist(bool fetchMaster) { mBandwidthItems.sort(SortByBandwidth); +#if 1 // XXX + if (mBandwidthItems.size() > 1) { + // Remove the lowest bandwidth stream, this is sometimes + // an AAC program stream, which we don't support at this point. + mBandwidthItems.removeItemsAt(0); + } +#endif + for (size_t i = 0; i < mBandwidthItems.size(); ++i) { const BandwidthItem &item = mBandwidthItems.itemAt(i); LOGV("item #%d: %s", i, item.mURI.c_str()); } + + bandwidthIndex = getBandwidthIndex(); } } if (mBandwidthItems.size() > 0) { -#if 0 - // Change bandwidth at random() - size_t index = uniformRand() * mBandwidthItems.size(); -#elif 0 - // There's a 50% chance to stay on the current bandwidth and - // a 50% chance to switch to the next higher bandwidth (wrapping around - // to lowest) - size_t index; - if (uniformRand() < 0.5) { - index = mPrevBandwidthIndex < 0 ? 0 : (size_t)mPrevBandwidthIndex; - } else { - if (mPrevBandwidthIndex < 0) { - index = 0; - } else { - index = mPrevBandwidthIndex + 1; - if (index == mBandwidthItems.size()) { - index = 0; - } - } - } -#else - // Stay on the lowest bandwidth available. - size_t index = mBandwidthItems.size() - 1; // Highest bandwidth stream -#endif + mURL = mBandwidthItems.editItemAt(bandwidthIndex).mURI; - mURL = mBandwidthItems.editItemAt(index).mURI; - - if (mPrevBandwidthIndex >= 0 && (size_t)mPrevBandwidthIndex != index) { + if (mPrevBandwidthIndex >= 0 + && (size_t)mPrevBandwidthIndex != bandwidthIndex) { // If we switched streams because of bandwidth changes, // we'll signal this discontinuity by inserting a // special transport stream packet into the stream. mSignalDiscontinuity = true; } - mPrevBandwidthIndex = index; + mPrevBandwidthIndex = bandwidthIndex; } else { mURL = mMasterURL; } @@ -199,18 +264,33 @@ bool LiveSource::switchToNext() { mOffsetBias += mSourceSize; mSourceSize = 0; + size_t bandwidthIndex = getBandwidthIndex(); + if (mLastFetchTimeUs < 0 || getNowUs() >= mLastFetchTimeUs + 15000000ll - || mPlaylistIndex == mPlaylist->size()) { + || mPlaylistIndex == mPlaylist->size() + || (ssize_t)bandwidthIndex != mPrevBandwidthIndex) { int32_t nextSequenceNumber = mPlaylistIndex + mFirstItemSequenceNumber; - if (!loadPlaylist(mLastFetchTimeUs < 0)) { + if (!loadPlaylist(mLastFetchTimeUs < 0, bandwidthIndex)) { LOGE("failed to reload playlist"); return false; } if (mLastFetchTimeUs < 0) { - mPlaylistIndex = 0; + if (isSeekable()) { + mPlaylistIndex = 0; + } else { + // This is live streamed content, the first seqnum in the + // various bandwidth' streams may be slightly off, so don't + // start at the very first entry. + // With a segment duration of 6-10secs, this really only + // delays playback up to 30secs compared to real time. + mPlaylistIndex = 3; + if (mPlaylistIndex >= mPlaylist->size()) { + mPlaylistIndex = mPlaylist->size() - 1; + } + } } else { if (nextSequenceNumber < mFirstItemSequenceNumber || nextSequenceNumber @@ -227,6 +307,10 @@ bool LiveSource::switchToNext() { mLastFetchTimeUs = getNowUs(); } + if (!setupCipher()) { + return false; + } + AString uri; sp<AMessage> itemMeta; CHECK(mPlaylist->itemAt(mPlaylistIndex, &uri, &itemMeta)); @@ -243,6 +327,121 @@ bool LiveSource::switchToNext() { } mPlaylistIndex++; + + return true; +} + +bool LiveSource::setupCipher() { + sp<AMessage> itemMeta; + bool found = false; + AString method; + + for (ssize_t i = mPlaylistIndex; i >= 0; --i) { + AString uri; + CHECK(mPlaylist->itemAt(i, &uri, &itemMeta)); + + if (itemMeta->findString("cipher-method", &method)) { + found = true; + break; + } + } + + if (!found) { + method = "NONE"; + } + + mStreamEncrypted = false; + + if (method == "AES-128") { + AString keyURI; + if (!itemMeta->findString("cipher-uri", &keyURI)) { + LOGE("Missing key uri"); + return false; + } + + if (keyURI.size() >= 2 + && keyURI.c_str()[0] == '"' + && keyURI.c_str()[keyURI.size() - 1] == '"') { + // Remove surrounding quotes. + AString tmp(keyURI, 1, keyURI.size() - 2); + keyURI = tmp; + } + + ssize_t index = mAESKeyForURI.indexOfKey(keyURI); + + sp<ABuffer> key; + if (index >= 0) { + key = mAESKeyForURI.valueAt(index); + } else { + key = new ABuffer(16); + + sp<NuHTTPDataSource> keySource = new NuHTTPDataSource; + status_t err = keySource->connect(keyURI.c_str()); + + if (err == OK) { + size_t offset = 0; + while (offset < 16) { + ssize_t n = keySource->readAt( + offset, key->data() + offset, 16 - offset); + if (n <= 0) { + err = ERROR_IO; + break; + } + + offset += n; + } + } + + if (err != OK) { + LOGE("failed to fetch cipher key from '%s'.", keyURI.c_str()); + return false; + } + + mAESKeyForURI.add(keyURI, key); + } + + if (AES_set_decrypt_key(key->data(), 128, (AES_KEY *)mAESKey) != 0) { + LOGE("failed to set AES decryption key."); + return false; + } + + AString iv; + if (itemMeta->findString("cipher-iv", &iv)) { + if ((!iv.startsWith("0x") && !iv.startsWith("0X")) + || iv.size() != 16 * 2 + 2) { + LOGE("malformed cipher IV '%s'.", iv.c_str()); + return false; + } + + memset(mAESIVec, 0, sizeof(mAESIVec)); + for (size_t i = 0; i < 16; ++i) { + char c1 = tolower(iv.c_str()[2 + 2 * i]); + char c2 = tolower(iv.c_str()[3 + 2 * i]); + if (!isxdigit(c1) || !isxdigit(c2)) { + LOGE("malformed cipher IV '%s'.", iv.c_str()); + return false; + } + uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10; + uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10; + + mAESIVec[i] = nibble1 << 4 | nibble2; + } + } else { + size_t seqNum = mPlaylistIndex + mFirstItemSequenceNumber; + + memset(mAESIVec, 0, sizeof(mAESIVec)); + mAESIVec[15] = seqNum & 0xff; + mAESIVec[14] = (seqNum >> 8) & 0xff; + mAESIVec[13] = (seqNum >> 16) & 0xff; + mAESIVec[12] = (seqNum >> 24) & 0xff; + } + + mStreamEncrypted = true; + } else if (!(method == "NONE")) { + LOGE("Unsupported cipher method '%s'", method.c_str()); + return false; + } + return true; } @@ -279,6 +478,7 @@ ssize_t LiveSource::readAt(off_t offset, void *data, size_t size) { return avail; } + bool done = false; size_t numRead = 0; while (numRead < size) { ssize_t n = mSource->readAt( @@ -289,7 +489,44 @@ ssize_t LiveSource::readAt(off_t offset, void *data, size_t size) { break; } + if (mStreamEncrypted) { + size_t nmod = n % 16; + CHECK(nmod == 0); + + sp<ABuffer> tmp = new ABuffer(n); + + AES_cbc_encrypt((const unsigned char *)data + numRead, + tmp->data(), + n, + (const AES_KEY *)mAESKey, + mAESIVec, + AES_DECRYPT); + + if (mSourceSize == (off_t)(offset + numRead - delta + n)) { + // check for padding at the end of the file. + + size_t pad = tmp->data()[n - 1]; + CHECK_GT(pad, 0u); + CHECK_LE(pad, 16u); + CHECK_GE((size_t)n, pad); + for (size_t i = 0; i < pad; ++i) { + CHECK_EQ((unsigned)tmp->data()[n - 1 - i], pad); + } + + n -= pad; + mSourceSize -= pad; + + done = true; + } + + memcpy((uint8_t *)data + numRead, tmp->data(), n); + } + numRead += n; + + if (done) { + break; + } } return numRead; @@ -359,19 +596,17 @@ bool LiveSource::seekTo(int64_t seekTimeUs) { return false; } - size_t newPlaylistIndex = mFirstItemSequenceNumber + index; - - if (newPlaylistIndex == mPlaylistIndex) { + if (index == mPlaylistIndex) { return false; } - mPlaylistIndex = newPlaylistIndex; + mPlaylistIndex = index; + + LOGV("seeking to index %lld", index); switchToNext(); mOffsetBias = 0; - LOGV("seeking to index %lld", index); - return true; } diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index 90f3d6d..b166cc3 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -158,6 +158,11 @@ status_t M3UParser::parse(const void *_data, size_t size) { return ERROR_MALFORMED; } err = parseMetaData(line, &mMeta, "media-sequence"); + } else if (line.startsWith("#EXT-X-KEY")) { + if (mIsVariantPlaylist) { + return ERROR_MALFORMED; + } + err = parseCipherInfo(line, &itemMeta); } else if (line.startsWith("#EXT-X-ENDLIST")) { mIsComplete = true; } else if (line.startsWith("#EXTINF")) { @@ -292,6 +297,57 @@ status_t M3UParser::parseStreamInf( } // static +status_t M3UParser::parseCipherInfo( + const AString &line, sp<AMessage> *meta) { + ssize_t colonPos = line.find(":"); + + if (colonPos < 0) { + return ERROR_MALFORMED; + } + + size_t offset = colonPos + 1; + + while (offset < line.size()) { + ssize_t end = line.find(",", offset); + if (end < 0) { + end = line.size(); + } + + AString attr(line, offset, end - offset); + attr.trim(); + + offset = end + 1; + + ssize_t equalPos = attr.find("="); + if (equalPos < 0) { + continue; + } + + AString key(attr, 0, equalPos); + key.trim(); + + AString val(attr, equalPos + 1, attr.size() - equalPos - 1); + val.trim(); + + LOGV("key=%s value=%s", key.c_str(), val.c_str()); + + key.tolower(); + + if (key == "method" || key == "uri" || key == "iv") { + if (meta->get() == NULL) { + *meta = new AMessage; + } + + key.insert(AString("cipher-"), 0); + + (*meta)->setString(key.c_str(), val.c_str(), val.size()); + } + } + + return OK; +} + +// static status_t M3UParser::ParseInt32(const char *s, int32_t *x) { char *end; long lval = strtol(s, &end, 10); diff --git a/media/libstagefright/include/AACEncoder.h b/media/libstagefright/include/AACEncoder.h index ecc533f..3d5fc60 100644 --- a/media/libstagefright/include/AACEncoder.h +++ b/media/libstagefright/include/AACEncoder.h @@ -60,7 +60,7 @@ class AACEncoder: public MediaSource { kNumSamplesPerFrame = 1024, }; - int16_t mInputFrame[kNumSamplesPerFrame]; + int16_t *mInputFrame; uint8_t mAudioSpecificConfigData[2]; // auido specific data void *mEncoderHandle; diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 302a1ba..4e63b7a 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -79,7 +79,6 @@ struct AwesomePlayer { bool isPlaying() const; - void setISurface(const sp<ISurface> &isurface); void setSurface(const sp<Surface> &surface); void setAudioSink(const sp<MediaPlayerBase::AudioSink> &audioSink); status_t setLooping(bool shouldLoop); @@ -130,7 +129,6 @@ private: bool mQueueStarted; wp<MediaPlayerBase> mListener; - sp<ISurface> mISurface; sp<Surface> mSurface; sp<MediaPlayerBase::AudioSink> mAudioSink; diff --git a/media/libstagefright/include/LiveSource.h b/media/libstagefright/include/LiveSource.h index 55dd45e..7ba1f44 100644 --- a/media/libstagefright/include/LiveSource.h +++ b/media/libstagefright/include/LiveSource.h @@ -21,6 +21,7 @@ #include <media/stagefright/foundation/ABase.h> #include <media/stagefright/foundation/AString.h> #include <media/stagefright/DataSource.h> +#include <utils/KeyedVector.h> #include <utils/Vector.h> namespace android { @@ -72,14 +73,23 @@ private: bool mSignalDiscontinuity; ssize_t mPrevBandwidthIndex; + void *mAESKey; + unsigned char mAESIVec[16]; + bool mStreamEncrypted; + + KeyedVector<AString, sp<ABuffer> > mAESKeyForURI; + status_t fetchM3U(const char *url, sp<ABuffer> *buffer); static int SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b); bool switchToNext(); - bool loadPlaylist(bool fetchMaster); + bool loadPlaylist(bool fetchMaster, size_t bandwidthIndex); void determineSeekability(); + size_t getBandwidthIndex(); + bool setupCipher(); + DISALLOW_EVIL_CONSTRUCTORS(LiveSource); }; diff --git a/media/libstagefright/include/M3UParser.h b/media/libstagefright/include/M3UParser.h index bd9eebe..531d184 100644 --- a/media/libstagefright/include/M3UParser.h +++ b/media/libstagefright/include/M3UParser.h @@ -66,6 +66,9 @@ private: static status_t parseStreamInf( const AString &line, sp<AMessage> *meta); + static status_t parseCipherInfo( + const AString &line, sp<AMessage> *meta); + static status_t ParseInt32(const char *s, int32_t *x); DISALLOW_EVIL_CONSTRUCTORS(M3UParser); diff --git a/media/libstagefright/include/NuHTTPDataSource.h b/media/libstagefright/include/NuHTTPDataSource.h index 93b7a76..c707fdc 100644 --- a/media/libstagefright/include/NuHTTPDataSource.h +++ b/media/libstagefright/include/NuHTTPDataSource.h @@ -3,6 +3,7 @@ #define NU_HTTP_DATA_SOURCE_H_ #include <media/stagefright/DataSource.h> +#include <utils/List.h> #include <utils/String8.h> #include <utils/threads.h> @@ -26,6 +27,10 @@ struct NuHTTPDataSource : public DataSource { virtual status_t getSize(off_t *size); virtual uint32_t flags(); + // Returns true if bandwidth could successfully be estimated, + // false otherwise. + bool estimateBandwidth(int32_t *bandwidth_bps); + virtual DecryptHandle* DrmInitialization(DrmManagerClient *client); virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client); @@ -39,6 +44,11 @@ private: CONNECTED }; + struct BandwidthEntry { + int64_t mDelayUs; + size_t mNumBytes; + }; + Mutex mLock; State mState; @@ -54,6 +64,11 @@ private: off_t mContentLength; bool mContentLengthValid; + List<BandwidthEntry> mBandwidthHistory; + size_t mNumBandwidthHistoryItems; + int64_t mTotalTransferTimeUs; + size_t mTotalTransferBytes; + DecryptHandle *mDecryptHandle; DrmManagerClient *mDrmManagerClient; @@ -66,6 +81,7 @@ private: off_t offset); void applyTimeoutResponse(); + void addBandwidthMeasurement_l(size_t numBytes, int64_t delayUs); static void MakeFullHeaders( const KeyedVector<String8, String8> *overrides, diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h index 5a6c96f9..5fed98a 100644 --- a/media/libstagefright/include/OMX.h +++ b/media/libstagefright/include/OMX.h @@ -97,13 +97,6 @@ public: const char *parameter_name, OMX_INDEXTYPE *index); - virtual sp<IOMXRenderer> createRenderer( - const sp<ISurface> &surface, - const char *componentName, - OMX_COLOR_FORMATTYPE colorFormat, - size_t encodedWidth, size_t encodedHeight, - size_t displayWidth, size_t displayHeight); - virtual void binderDied(const wp<IBinder> &the_late_who); OMX_ERRORTYPE OnEvent( diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h index 8d58056..198bfd6 100644 --- a/media/libstagefright/include/SoftwareRenderer.h +++ b/media/libstagefright/include/SoftwareRenderer.h @@ -19,15 +19,13 @@ #define SOFTWARE_RENDERER_H_ #include <media/stagefright/ColorConverter.h> -#include <media/stagefright/VideoRenderer.h> #include <utils/RefBase.h> namespace android { class Surface; -class MemoryHeapBase; -class SoftwareRenderer : public VideoRenderer { +class SoftwareRenderer { public: SoftwareRenderer( OMX_COLOR_FORMATTYPE colorFormat, @@ -35,9 +33,9 @@ public: size_t displayWidth, size_t displayHeight, size_t decodedWidth, size_t decodedHeight); - virtual ~SoftwareRenderer(); + ~SoftwareRenderer(); - virtual void render( + void render( const void *data, size_t size, void *platformPrivate); private: diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h index 62cfc36..3aeb07f 100644 --- a/media/libstagefright/include/avc_utils.h +++ b/media/libstagefright/include/avc_utils.h @@ -24,6 +24,17 @@ namespace android { struct ABitReader; +enum { + kAVCProfileBaseline = 0x42, + kAVCProfileMain = 0x4d, + kAVCProfileExtended = 0x58, + kAVCProfileHigh = 0x64, + kAVCProfileHigh10 = 0x6e, + kAVCProfileHigh422 = 0x7a, + kAVCProfileHigh444 = 0xf4, + kAVCProfileCAVLC444Intra = 0x2c +}; + void FindAVCDimensions( const sp<ABuffer> &seqParamSet, int32_t *width, int32_t *height); @@ -39,6 +50,8 @@ sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit); bool IsIDR(const sp<ABuffer> &accessUnit); +const char *AVCProfileToString(uint8_t profile); + } // namespace android #endif // AVC_UTILS_H_ diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp index d16476d..a40c0a3 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.cpp +++ b/media/libstagefright/matroska/MatroskaExtractor.cpp @@ -22,13 +22,15 @@ #include "mkvparser.hpp" +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/MediaBuffer.h> -#include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MediaSource.h> #include <media/stagefright/MetaData.h> +#include <media/stagefright/Utils.h> #include <utils/String8.h> namespace android { @@ -81,46 +83,6 @@ private: //////////////////////////////////////////////////////////////////////////////// -#include <ctype.h> -static void hexdump(const void *_data, size_t size) { - const uint8_t *data = (const uint8_t *)_data; - size_t offset = 0; - while (offset < size) { - printf("0x%04x ", offset); - - size_t n = size - offset; - if (n > 16) { - n = 16; - } - - for (size_t i = 0; i < 16; ++i) { - if (i == 8) { - printf(" "); - } - - if (offset + i < size) { - printf("%02x ", data[offset + i]); - } else { - printf(" "); - } - } - - printf(" "); - - for (size_t i = 0; i < n; ++i) { - if (isprint(data[offset + i])) { - printf("%c", data[offset + i]); - } else { - printf("."); - } - } - - printf("\n"); - - offset += 16; - } -} - struct BlockIterator { BlockIterator(mkvparser::Segment *segment, unsigned long trackNum); @@ -167,6 +129,7 @@ private: size_t mTrackIndex; Type mType; BlockIterator mBlockIter; + size_t mNALSizeLen; // for type AVC status_t advance(); @@ -180,13 +143,26 @@ MatroskaSource::MatroskaSource( mTrackIndex(index), mType(OTHER), mBlockIter(mExtractor->mSegment, - mExtractor->mTracks.itemAt(index).mTrackNum) { + mExtractor->mTracks.itemAt(index).mTrackNum), + mNALSizeLen(0) { + sp<MetaData> meta = mExtractor->mTracks.itemAt(index).mMeta; + const char *mime; - CHECK(mExtractor->mTracks.itemAt(index).mMeta-> - findCString(kKeyMIMEType, &mime)); + CHECK(meta->findCString(kKeyMIMEType, &mime)); if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { mType = AVC; + + uint32_t dummy; + const uint8_t *avcc; + size_t avccSize; + CHECK(meta->findData( + kKeyAVCC, &dummy, (const void **)&avcc, &avccSize)); + + CHECK_GE(avccSize, 5u); + + mNALSizeLen = 1 + (avcc[4] & 3); + LOGV("mNALSizeLen = %d", mNALSizeLen); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { mType = AAC; } @@ -276,6 +252,10 @@ int64_t BlockIterator::blockTimeUs() const { //////////////////////////////////////////////////////////////////////////////// +static unsigned U24_AT(const uint8_t *ptr) { + return ptr[0] << 16 | ptr[1] << 8 | ptr[2]; +} + status_t MatroskaSource::read( MediaBuffer **out, const ReadOptions *options) { *out = NULL; @@ -286,6 +266,7 @@ status_t MatroskaSource::read( mBlockIter.seek(seekTimeUs); } +again: if (mBlockIter.eos()) { return ERROR_END_OF_STREAM; } @@ -294,38 +275,70 @@ status_t MatroskaSource::read( size_t size = block->GetSize(); int64_t timeUs = mBlockIter.blockTimeUs(); - MediaBuffer *buffer = new MediaBuffer(size + 2); + // In the case of AVC content, each NAL unit is prefixed by + // mNALSizeLen bytes of length. We want to prefix the data with + // a four-byte 0x00000001 startcode instead of the length prefix. + // mNALSizeLen ranges from 1 through 4 bytes, so add an extra + // 3 bytes of padding to the buffer start. + static const size_t kPadding = 3; + + MediaBuffer *buffer = new MediaBuffer(size + kPadding); buffer->meta_data()->setInt64(kKeyTime, timeUs); buffer->meta_data()->setInt32(kKeyIsSyncFrame, block->IsKey()); long res = block->Read( - mExtractor->mReader, (unsigned char *)buffer->data() + 2); + mExtractor->mReader, (unsigned char *)buffer->data() + kPadding); if (res != 0) { return ERROR_END_OF_STREAM; } - buffer->set_range(2, size); + buffer->set_range(kPadding, size); if (mType == AVC) { - CHECK(size >= 2); + CHECK_GE(size, mNALSizeLen); uint8_t *data = (uint8_t *)buffer->data(); - unsigned NALsize = data[2] << 8 | data[3]; - CHECK_EQ(size, NALsize + 2); + size_t NALsize; + switch (mNALSizeLen) { + case 1: NALsize = data[kPadding]; break; + case 2: NALsize = U16_AT(&data[kPadding]); break; + case 3: NALsize = U24_AT(&data[kPadding]); break; + case 4: NALsize = U32_AT(&data[kPadding]); break; + default: + TRESPASS(); + } - memcpy(data, "\x00\x00\x00\x01", 4); - buffer->set_range(0, size + 2); + CHECK_GE(size, NALsize + mNALSizeLen); + if (size > NALsize + mNALSizeLen) { + LOGW("discarding %d bytes of data.", size - NALsize - mNALSizeLen); + } + + // actual data starts at &data[kPadding + mNALSizeLen] + + memcpy(&data[mNALSizeLen - 1], "\x00\x00\x00\x01", 4); + buffer->set_range(mNALSizeLen - 1, NALsize + 4); } else if (mType == AAC) { // There's strange junk at the beginning... - const uint8_t *data = (const uint8_t *)buffer->data() + 2; + const uint8_t *data = (const uint8_t *)buffer->data() + kPadding; + + // hexdump(data, size); + size_t offset = 0; while (offset < size && data[offset] != 0x21) { ++offset; } - buffer->set_range(2 + offset, size - offset); + + if (size == offset) { + buffer->release(); + + mBlockIter.advance(); + goto again; + } + + buffer->set_range(kPadding + offset, size - offset); } *out = buffer; diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index c88c6c1..f06a1bb 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -274,6 +274,8 @@ ATSParser::Stream::Stream( mQueue(streamType == 0x1b ? ElementaryStreamQueue::H264 : ElementaryStreamQueue::AAC) { mBuffer->setRange(0, 0); + + LOGV("new stream PID 0x%02x, type 0x%02x", elementaryPID, streamType); } ATSParser::Stream::~Stream() { @@ -307,7 +309,8 @@ void ATSParser::Stream::parse( } void ATSParser::Stream::signalDiscontinuity(bool isASeek) { - LOGV("Stream discontinuity"); + isASeek = false; // Always signal a "real" discontinuity + mPayloadStarted = false; mBuffer->setRange(0, 0); @@ -317,7 +320,9 @@ void ATSParser::Stream::signalDiscontinuity(bool isASeek) { // This is only a "minor" discontinuity, we stay within the same // bitstream. - mSource->clear(); + if (mSource != NULL) { + mSource->clear(); + } return; } diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index b0b9e66..f11b3c3 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -41,7 +41,10 @@ sp<MetaData> ElementaryStreamQueue::getFormat() { } void ElementaryStreamQueue::clear() { - mBuffer->setRange(0, 0); + if (mBuffer != NULL) { + mBuffer->setRange(0, 0); + } + mTimestamps.clear(); mFormat.clear(); } diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index f9f638f..4e9920b 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -24,14 +24,11 @@ #include <sys/resource.h> #include "../include/OMX.h" -#include "OMXRenderer.h" #include "../include/OMXNodeInstance.h" -#include "../include/SoftwareRenderer.h" #include <binder/IMemory.h> #include <media/stagefright/MediaDebug.h> -#include <media/stagefright/VideoRenderer.h> #include <utils/threads.h> #include "OMXMaster.h" @@ -442,110 +439,4 @@ void OMX::invalidateNodeID_l(node_id node) { mNodeIDToInstance.removeItem(node); } -//////////////////////////////////////////////////////////////////////////////// - -struct SharedVideoRenderer : public VideoRenderer { - SharedVideoRenderer(void *libHandle, VideoRenderer *obj) - : mLibHandle(libHandle), - mObj(obj) { - } - - virtual ~SharedVideoRenderer() { - delete mObj; - mObj = NULL; - - dlclose(mLibHandle); - mLibHandle = NULL; - } - - virtual void render( - const void *data, size_t size, void *platformPrivate) { - return mObj->render(data, size, platformPrivate); - } - -private: - void *mLibHandle; - VideoRenderer *mObj; - - SharedVideoRenderer(const SharedVideoRenderer &); - SharedVideoRenderer &operator=(const SharedVideoRenderer &); -}; - -sp<IOMXRenderer> OMX::createRenderer( - const sp<ISurface> &surface, - const char *componentName, - OMX_COLOR_FORMATTYPE colorFormat, - size_t encodedWidth, size_t encodedHeight, - size_t displayWidth, size_t displayHeight) { - Mutex::Autolock autoLock(mLock); - - VideoRenderer *impl = NULL; - - void *libHandle = dlopen("libstagefrighthw.so", RTLD_NOW); - - if (libHandle) { - typedef VideoRenderer *(*CreateRendererFunc)( - const sp<ISurface> &surface, - const char *componentName, - OMX_COLOR_FORMATTYPE colorFormat, - size_t displayWidth, size_t displayHeight, - size_t decodedWidth, size_t decodedHeight); - - CreateRendererFunc func = - (CreateRendererFunc)dlsym( - libHandle, - "_Z14createRendererRKN7android2spINS_8ISurfaceEEEPKc20" - "OMX_COLOR_FORMATTYPEjjjj"); - - if (func) { - impl = (*func)(surface, componentName, colorFormat, - displayWidth, displayHeight, encodedWidth, encodedHeight); - - if (impl) { - impl = new SharedVideoRenderer(libHandle, impl); - libHandle = NULL; - } - } - - if (libHandle) { - dlclose(libHandle); - libHandle = NULL; - } - } - - if (!impl) { -#if 0 - LOGW("Using software renderer."); - impl = new SoftwareRenderer( - colorFormat, - surface, - displayWidth, displayHeight, - encodedWidth, encodedHeight); -#else - CHECK(!"Should not be here."); - return NULL; -#endif - } - - return new OMXRenderer(impl); -} - -OMXRenderer::OMXRenderer(VideoRenderer *impl) - : mImpl(impl) { -} - -OMXRenderer::~OMXRenderer() { - delete mImpl; - mImpl = NULL; -} - -void OMXRenderer::render(IOMX::buffer_id buffer) { - OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer; - - mImpl->render( - header->pBuffer + header->nOffset, - header->nFilledLen, - header->pPlatformPrivate); -} - } // namespace android diff --git a/media/libstagefright/omx/OMXRenderer.h b/media/libstagefright/omx/OMXRenderer.h deleted file mode 100644 index 4d194ce..0000000 --- a/media/libstagefright/omx/OMXRenderer.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2009 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. - */ - -#ifndef OMX_RENDERER_H_ - -#define OMX_RENDERER_H_ - -#include <media/IOMX.h> - -namespace android { - -class VideoRenderer; - -class OMXRenderer : public BnOMXRenderer { -public: - // Assumes ownership of "impl". - OMXRenderer(VideoRenderer *impl); - virtual ~OMXRenderer(); - - virtual void render(IOMX::buffer_id buffer); - -private: - VideoRenderer *mImpl; - - OMXRenderer(const OMXRenderer &); - OMXRenderer &operator=(const OMXRenderer &); -}; - -} // namespace android - -#endif // OMX_RENDERER_H_ diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h index c8cb016..fafd221 100644 --- a/media/mtp/MtpDatabase.h +++ b/media/mtp/MtpDatabase.h @@ -75,6 +75,12 @@ public: virtual MtpResponseCode resetDeviceProperty(MtpDeviceProperty property) = 0; + virtual MtpResponseCode getObjectPropertyList(MtpObjectHandle handle, + MtpObjectFormat format, + MtpObjectProperty property, + int groupCode, int depth, + MtpDataPacket& packet) = 0; + virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle, MtpDataPacket& packet) = 0; diff --git a/media/mtp/MtpDebug.cpp b/media/mtp/MtpDebug.cpp index 3416807..1668ecf 100644 --- a/media/mtp/MtpDebug.cpp +++ b/media/mtp/MtpDebug.cpp @@ -56,6 +56,10 @@ static const CodeEntry sOperationCodes[] = { { "MTP_OPERATION_GET_OBJECT_PROP_DESC", 0x9802 }, { "MTP_OPERATION_GET_OBJECT_PROP_VALUE", 0x9803 }, { "MTP_OPERATION_SET_OBJECT_PROP_VALUE", 0x9804 }, + { "MTP_OPERATION_GET_OBJECT_PROP_LIST", 0x9805 }, + { "MTP_OPERATION_SET_OBJECT_PROP_LIST", 0x9806 }, + { "MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC", 0x9807 }, + { "MTP_OPERATION_SEND_OBJECT_PROP_LIST", 0x9808 }, { "MTP_OPERATION_GET_OBJECT_REFERENCES", 0x9810 }, { "MTP_OPERATION_SET_OBJECT_REFERENCES", 0x9811 }, { "MTP_OPERATION_SKIP", 0x9820 }, @@ -371,15 +375,21 @@ const char* MtpDebug::getOperationCodeName(MtpOperationCode code) { return getCodeName(code, sOperationCodes); } -const char* MtpDebug::getFormatCodeName(MtpOperationCode code) { +const char* MtpDebug::getFormatCodeName(MtpObjectFormat code) { + if (code == 0) + return "NONE"; return getCodeName(code, sFormatCodes); } const char* MtpDebug::getObjectPropCodeName(MtpPropertyCode code) { + if (code == 0) + return "NONE"; return getCodeName(code, sObjectPropCodes); } const char* MtpDebug::getDevicePropCodeName(MtpPropertyCode code) { + if (code == 0) + return "NONE"; return getCodeName(code, sDevicePropCodes); } diff --git a/media/mtp/MtpProperty.cpp b/media/mtp/MtpProperty.cpp index f7c12d6..86889c3 100644 --- a/media/mtp/MtpProperty.cpp +++ b/media/mtp/MtpProperty.cpp @@ -53,7 +53,7 @@ MtpProperty::MtpProperty(MtpPropertyCode propCode, mDefaultArrayValues(NULL), mCurrentArrayLength(0), mCurrentArrayValues(NULL), - mGroupCode(0), + mGroupCode(-1), // disable multiple properties in GetObjectPropList for now mFormFlag(kFormNone), mEnumLength(0), mEnumValues(NULL) diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index 5ba6be9..6cf70ec 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -72,6 +72,10 @@ static const MtpOperationCode kSupportedOperationCodes[] = { MTP_OPERATION_GET_OBJECT_PROP_DESC, MTP_OPERATION_GET_OBJECT_PROP_VALUE, MTP_OPERATION_SET_OBJECT_PROP_VALUE, + MTP_OPERATION_GET_OBJECT_PROP_LIST, +// MTP_OPERATION_SET_OBJECT_PROP_LIST, +// MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC, +// MTP_OPERATION_SEND_OBJECT_PROP_LIST, MTP_OPERATION_GET_OBJECT_REFERENCES, MTP_OPERATION_SET_OBJECT_REFERENCES, // MTP_OPERATION_SKIP, @@ -276,6 +280,9 @@ bool MtpServer::handleRequest() { case MTP_OPERATION_RESET_DEVICE_PROP_VALUE: response = doResetDevicePropValue(); break; + case MTP_OPERATION_GET_OBJECT_PROP_LIST: + response = doGetObjectPropList(); + break; case MTP_OPERATION_GET_OBJECT_INFO: response = doGetObjectInfo(); break; @@ -523,6 +530,20 @@ MtpResponseCode MtpServer::doResetDevicePropValue() { return mDatabase->resetDeviceProperty(property); } +MtpResponseCode MtpServer::doGetObjectPropList() { + + MtpObjectHandle handle = mRequest.getParameter(1); + MtpObjectFormat format = mRequest.getParameter(2); + MtpDeviceProperty property = mRequest.getParameter(3); + int groupCode = mRequest.getParameter(4); + int depth = mRequest.getParameter(4); + LOGD("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n", + handle, MtpDebug::getFormatCodeName(format), + MtpDebug::getObjectPropCodeName(property), groupCode, depth); + + return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData); +} + MtpResponseCode MtpServer::doGetObjectInfo() { MtpObjectHandle handle = mRequest.getParameter(1); return mDatabase->getObjectInfo(handle, mData); diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h index 68a6564..e65ddb0 100644 --- a/media/mtp/MtpServer.h +++ b/media/mtp/MtpServer.h @@ -93,6 +93,7 @@ private: MtpResponseCode doGetDevicePropValue(); MtpResponseCode doSetDevicePropValue(); MtpResponseCode doResetDevicePropValue(); + MtpResponseCode doGetObjectPropList(); MtpResponseCode doGetObjectInfo(); MtpResponseCode doGetObject(); MtpResponseCode doSendObjectInfo(); diff --git a/media/mtp/mtp.h b/media/mtp/mtp.h index b7afa66..8bc2e22 100644 --- a/media/mtp/mtp.h +++ b/media/mtp/mtp.h @@ -37,7 +37,7 @@ #define MTP_CONTAINER_PARAMETER_OFFSET 12 #define MTP_CONTAINER_HEADER_SIZE 12 -// MTP Types +// MTP Data Types #define MTP_TYPE_UNDEFINED 0x0000 // Undefined #define MTP_TYPE_INT8 0x0001 // Signed 8-bit integer #define MTP_TYPE_UINT8 0x0002 // Unsigned 8-bit integer @@ -383,6 +383,10 @@ #define MTP_OPERATION_GET_OBJECT_PROP_DESC 0x9802 #define MTP_OPERATION_GET_OBJECT_PROP_VALUE 0x9803 #define MTP_OPERATION_SET_OBJECT_PROP_VALUE 0x9804 +#define MTP_OPERATION_GET_OBJECT_PROP_LIST 0x9805 +#define MTP_OPERATION_SET_OBJECT_PROP_LIST 0x9806 +#define MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC 0x9807 +#define MTP_OPERATION_SEND_OBJECT_PROP_LIST 0x9808 #define MTP_OPERATION_GET_OBJECT_REFERENCES 0x9810 #define MTP_OPERATION_SET_OBJECT_REFERENCES 0x9811 #define MTP_OPERATION_SKIP 0x9820 diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java index 0132ae8..95b7386 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java @@ -64,7 +64,7 @@ public class MediaPlayerStressTest extends ActivityInstrumentationTestCase2<Medi mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); try { - assertTrue(MediaFrameworkTest.checkStreamingServer()); + //assertTrue(MediaFrameworkTest.checkStreamingServer()); for (int i = 0; i < NUMBER_OF_STRESS_LOOPS; i++) { MediaPlayer mp = new MediaPlayer(); mp.setDataSource(MediaNames.STREAM_H264_480_360_1411k); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerStateUnitTestTemplate.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerStateUnitTestTemplate.java index 6abfbb2..f8e8f98 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerStateUnitTestTemplate.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerStateUnitTestTemplate.java @@ -39,7 +39,7 @@ import com.android.mediaframeworktest.MediaNames; */ class MediaPlayerStateUnitTestTemplate extends AndroidTestCase { private static final String TEST_PATH = MediaNames.TEST_PATH_1; - private static final String TAG = "MediaPlayerSeekToStateUnitTest"; + private static final String TAG = "MediaPlayerStateUnitTestTemplate"; private static final int SEEK_TO_END = 135110; // Milliseconds. private static int WAIT_FOR_COMMAND_TO_COMPLETE = 1000; // Milliseconds. diff --git a/media/tests/players/invoke_mock_media_player.cpp b/media/tests/players/invoke_mock_media_player.cpp index 53308be..1e3731e 100644 --- a/media/tests/players/invoke_mock_media_player.cpp +++ b/media/tests/players/invoke_mock_media_player.cpp @@ -68,7 +68,6 @@ class Player: public MediaPlayerBase } virtual status_t setDataSource(int fd, int64_t offset, int64_t length) {return OK;} - virtual status_t setVideoISurface(const sp<ISurface>& surface) {return OK;} virtual status_t setVideoSurface(const sp<Surface>& surface) {return OK;} virtual status_t prepare() {return OK;} virtual status_t prepareAsync() {return OK;} diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_0.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_0.png Binary files differdeleted file mode 100644 index 35b765f..0000000 --- a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_0.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_10.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_10.png Binary files differdeleted file mode 100644 index 39eeb06..0000000 --- a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_10.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_100.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_100.png Binary files differdeleted file mode 100644 index b5787aa..0000000 --- a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_100.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_20.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_20.png Binary files differdeleted file mode 100644 index 3fc2852..0000000 --- a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_20.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_30.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_30.png Binary files differdeleted file mode 100644 index 6ba6da3..0000000 --- a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_30.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_40.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_40.png Binary files differdeleted file mode 100644 index f5ac131..0000000 --- a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_40.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_50.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_50.png Binary files differdeleted file mode 100644 index fffb701..0000000 --- a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_50.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_60.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_60.png Binary files differdeleted file mode 100644 index 7b4fa44..0000000 --- a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_60.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_70.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_70.png Binary files differdeleted file mode 100644 index 3c9eeb1..0000000 --- a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_70.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_80.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_80.png Binary files differdeleted file mode 100644 index 3945188..0000000 --- a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_80.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_90.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_90.png Binary files differdeleted file mode 100644 index 8a9241c..0000000 --- a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_90.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_batterymini_100.png b/packages/SystemUI/res/drawable-hdpi/sysbar_batterymini_100.png Binary files differdeleted file mode 100644 index 67591fe..0000000 --- a/packages/SystemUI/res/drawable-hdpi/sysbar_batterymini_100.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_batterymini_red.png b/packages/SystemUI/res/drawable-hdpi/sysbar_batterymini_red.png Binary files differdeleted file mode 100644 index 697044b..0000000 --- a/packages/SystemUI/res/drawable-hdpi/sysbar_batterymini_red.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signalmini_100.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signalmini_100.png Binary files differdeleted file mode 100644 index 8bec533..0000000 --- a/packages/SystemUI/res/drawable-hdpi/sysbar_signalmini_100.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_quicksettings.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_quicksettings.png Binary files differnew file mode 100644 index 0000000..4434b5c --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_quicksettings.png diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_0.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_0.png Binary files differdeleted file mode 100644 index ff75a51..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_0.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_10.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_10.png Binary files differdeleted file mode 100644 index 66ab4c6..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_10.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_100.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_100.png Binary files differdeleted file mode 100644 index 3b50500..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_100.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_20.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_20.png Binary files differdeleted file mode 100644 index 9119065..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_20.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_30.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_30.png Binary files differdeleted file mode 100644 index 296f19f..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_30.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_40.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_40.png Binary files differdeleted file mode 100644 index 9daab23..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_40.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_50.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_50.png Binary files differdeleted file mode 100644 index 62d24c4..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_50.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_60.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_60.png Binary files differdeleted file mode 100644 index eea927a..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_60.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_70.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_70.png Binary files differdeleted file mode 100644 index 6816088..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_70.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_80.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_80.png Binary files differdeleted file mode 100644 index b7dd9bb..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_80.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_90.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_90.png Binary files differdeleted file mode 100644 index 6e36f53..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_90.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_batterymini_100.png b/packages/SystemUI/res/drawable-mdpi/sysbar_batterymini_100.png Binary files differdeleted file mode 100644 index 8eb0f29..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_batterymini_100.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_batterymini_red.png b/packages/SystemUI/res/drawable-mdpi/sysbar_batterymini_red.png Binary files differdeleted file mode 100644 index adcc6b9..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_batterymini_red.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_0.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_0.png Binary files differdeleted file mode 100644 index 7469372..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_0.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_10.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_10.png Binary files differdeleted file mode 100644 index 6625d9a..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_10.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_100.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_100.png Binary files differdeleted file mode 100644 index b2e763b..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_100.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_20.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_20.png Binary files differdeleted file mode 100644 index fb66362..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_20.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_30.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_30.png Binary files differdeleted file mode 100644 index a87d94e..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_30.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_40.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_40.png Binary files differdeleted file mode 100644 index 8e229d5..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_40.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_50.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_50.png Binary files differdeleted file mode 100644 index fe989d4..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_50.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_60.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_60.png Binary files differdeleted file mode 100644 index aac57dc..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_60.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_70.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_70.png Binary files differdeleted file mode 100644 index 2281968..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_70.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_80.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_80.png Binary files differdeleted file mode 100644 index 7177ae1..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_80.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_90.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_90.png Binary files differdeleted file mode 100644 index 7f60480..0000000 --- a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_90.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable/sysbar_battery.xml b/packages/SystemUI/res/drawable/sysbar_battery.xml deleted file mode 100644 index 9551bf0..0000000 --- a/packages/SystemUI/res/drawable/sysbar_battery.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/res/drawable/stat_sys_battery.xml -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<level-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:maxLevel="01" android:drawable="@drawable/sysbar_battery_0" /> - <item android:maxLevel="10" android:drawable="@drawable/sysbar_battery_10" /> - <item android:maxLevel="20" android:drawable="@drawable/sysbar_battery_20" /> - <item android:maxLevel="30" android:drawable="@drawable/sysbar_battery_30" /> - <item android:maxLevel="40" android:drawable="@drawable/sysbar_battery_40" /> - <item android:maxLevel="50" android:drawable="@drawable/sysbar_battery_50" /> - <item android:maxLevel="60" android:drawable="@drawable/sysbar_battery_60" /> - <item android:maxLevel="70" android:drawable="@drawable/sysbar_battery_70" /> - <item android:maxLevel="80" android:drawable="@drawable/sysbar_battery_80" /> - <item android:maxLevel="90" android:drawable="@drawable/sysbar_battery_90" /> - <item android:maxLevel="101" android:drawable="@drawable/sysbar_battery_100" /> -</level-list> diff --git a/packages/SystemUI/res/drawable/sysbar_batterymini.xml b/packages/SystemUI/res/drawable/sysbar_batterymini.xml deleted file mode 100644 index c7300e6..0000000 --- a/packages/SystemUI/res/drawable/sysbar_batterymini.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/res/drawable/stat_sys_battery.xml -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<clip xmlns:android="http://schemas.android.com/apk/res/android" - android:clipOrientation="horizontal" - android:maxLevel="100" - android:gravity="left"> - <level-list> - <item android:maxLevel="1500" android:drawable="@drawable/sysbar_batterymini_red" /> - <item android:maxLevel="10000" android:drawable="@drawable/sysbar_batterymini_100" /> - </level-list> -</clip> diff --git a/packages/SystemUI/res/drawable/sysbar_signal.xml b/packages/SystemUI/res/drawable/sysbar_signal.xml deleted file mode 100644 index 9561c37..0000000 --- a/packages/SystemUI/res/drawable/sysbar_signal.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/res/drawable/stat_sys_battery.xml -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<level-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:maxLevel="01" android:drawable="@drawable/sysbar_signal_0" /> - <item android:maxLevel="10" android:drawable="@drawable/sysbar_signal_10" /> - <item android:maxLevel="20" android:drawable="@drawable/sysbar_signal_20" /> - <item android:maxLevel="30" android:drawable="@drawable/sysbar_signal_30" /> - <item android:maxLevel="40" android:drawable="@drawable/sysbar_signal_40" /> - <item android:maxLevel="50" android:drawable="@drawable/sysbar_signal_50" /> - <item android:maxLevel="60" android:drawable="@drawable/sysbar_signal_60" /> - <item android:maxLevel="70" android:drawable="@drawable/sysbar_signal_70" /> - <item android:maxLevel="80" android:drawable="@drawable/sysbar_signal_80" /> - <item android:maxLevel="90" android:drawable="@drawable/sysbar_signal_90" /> - <item android:maxLevel="101" android:drawable="@drawable/sysbar_signal_100" /> -</level-list> diff --git a/packages/SystemUI/res/drawable/sysbar_signalmini.xml b/packages/SystemUI/res/drawable/sysbar_signalmini.xml deleted file mode 100644 index 598bf10..0000000 --- a/packages/SystemUI/res/drawable/sysbar_signalmini.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/res/drawable/stat_sys_battery.xml -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<clip xmlns:android="http://schemas.android.com/apk/res/android" - android:clipOrientation="horizontal" - android:gravity="right" - android:maxLevel="10000" - android:drawable="@drawable/sysbar_signalmini_100" /> diff --git a/packages/SystemUI/res/drawable/sysbar_wifi.xml b/packages/SystemUI/res/drawable/sysbar_wifi.xml deleted file mode 100644 index 9561c37..0000000 --- a/packages/SystemUI/res/drawable/sysbar_wifi.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/res/drawable/stat_sys_battery.xml -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<level-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:maxLevel="01" android:drawable="@drawable/sysbar_signal_0" /> - <item android:maxLevel="10" android:drawable="@drawable/sysbar_signal_10" /> - <item android:maxLevel="20" android:drawable="@drawable/sysbar_signal_20" /> - <item android:maxLevel="30" android:drawable="@drawable/sysbar_signal_30" /> - <item android:maxLevel="40" android:drawable="@drawable/sysbar_signal_40" /> - <item android:maxLevel="50" android:drawable="@drawable/sysbar_signal_50" /> - <item android:maxLevel="60" android:drawable="@drawable/sysbar_signal_60" /> - <item android:maxLevel="70" android:drawable="@drawable/sysbar_signal_70" /> - <item android:maxLevel="80" android:drawable="@drawable/sysbar_signal_80" /> - <item android:maxLevel="90" android:drawable="@drawable/sysbar_signal_90" /> - <item android:maxLevel="101" android:drawable="@drawable/sysbar_signal_100" /> -</level-list> diff --git a/packages/SystemUI/res/drawable/sysbar_wifimini.xml b/packages/SystemUI/res/drawable/sysbar_wifimini.xml deleted file mode 100644 index ca6c9ed..0000000 --- a/packages/SystemUI/res/drawable/sysbar_wifimini.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/res/drawable/stat_sys_battery.xml -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<clip xmlns:android="http://schemas.android.com/apk/res/android" - android:clipOrientation="horizontal" - android:gravity="right" - android:maxLevel="100" - android:drawable="@drawable/sysbar_signalmini_100" /> diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml index 590132f..a6e5c89 100644 --- a/packages/SystemUI/res/layout-xlarge/status_bar.xml +++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml @@ -25,100 +25,74 @@ android:id="@+id/bar_contents" android:layout_width="match_parent" android:layout_height="match_parent" + android:animateLayoutChanges="true" > - <ImageView - android:id="@+id/notificationTrigger" + <FrameLayout + android:id="@+id/ticker" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_alignParentRight="true" - android:layout_marginLeft="6dip" - android:src="@drawable/ic_sysbar_noti_none" - android:background="@drawable/ic_sysbar_icon_bg" - android:gravity="center" + android:layout_toRightOf="@+id/systemInfo" + android:paddingLeft="6dip" + android:gravity="center_vertical" + android:animateLayoutChanges="true" /> <LinearLayout - android:id="@+id/notificationButtons" + android:id="@+id/notificationArea" android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_toLeftOf="@+id/notificationTrigger" - android:gravity="center_vertical" + android:layout_alignParentRight="true" android:orientation="horizontal" - android:visibility="gone" > - - <TextView android:id="@+id/clear_all_button" - style="?android:attr/textAppearance" + <com.android.systemui.statusbar.tablet.NotificationIconArea + android:id="@+id/notificationIcons" android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="right|center_vertical" - android:layout_marginTop="2dip" - android:layout_marginBottom="1dip" - android:layout_marginRight="10dip" - android:padding="6dip" - android:textSize="14sp" - android:text="@string/status_bar_clear_all_button" - /> - - <TextView android:id="@+id/do_not_disturb" - style="?android:attr/textAppearance" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="right|center_vertical" - android:layout_marginTop="2dip" - android:layout_marginBottom="1dip" - android:layout_marginRight="10dip" - android:padding="6dip" - android:textSize="14sp" - android:text="@string/status_bar_do_not_disturb_button" - /> - - </LinearLayout> + android:layout_height="match_parent" + android:gravity="center_vertical" + android:orientation="horizontal" + > + <view + class="com.android.systemui.statusbar.tablet.NotificationIconArea$IconLayout" + android:id="@+id/icons" + android:layout_width="wrap_content" + android:layout_height="@*android:dimen/status_bar_icon_size" + android:layout_marginLeft="8dip" + /> + </com.android.systemui.statusbar.tablet.NotificationIconArea> - <com.android.systemui.statusbar.tablet.NotificationIconArea - android:id="@+id/notificationIcons" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_toLeftOf="@+id/notificationTrigger" - android:gravity="center_vertical" - android:orientation="horizontal" - > - <com.android.systemui.statusbar.tablet.InputMethodButton - android:id="@+id/imeButton" + <LinearLayout + android:id="@+id/notificationTrigger" android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_marginLeft="8dip" - android:src="@drawable/ic_sysbar_ime_default" - android:background="@drawable/ic_sysbar_icon_bg" - android:visibility="invisible" - /> - <view - class="com.android.systemui.statusbar.tablet.NotificationIconArea$IconLayout" - android:id="@+id/icons" - android:layout_width="wrap_content" - android:layout_height="@*android:dimen/status_bar_icon_size" - android:layout_marginLeft="8dip" - /> - </com.android.systemui.statusbar.tablet.NotificationIconArea> - - - <FrameLayout - android:id="@+id/ticker" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_alignParentRight="true" - android:layout_toRightOf="@+id/systemInfo" - android:paddingLeft="6dip" - android:gravity="center_vertical" - android:animateLayoutChanges="true" - /> - - <include layout="@layout/status_bar_center" - android:layout_width="256dip" - android:layout_height="match_parent" - android:layout_centerInParent="true" - /> + > + <com.android.systemui.statusbar.policy.Clock + style="@*android:style/TextAppearance.StatusBar.Icon" + android:id="@+id/clock" + android:layout_width="64dip" + android:layout_height="48dip" + android:singleLine="true" + android:gravity="center" + android:textSize="16sp" + android:textStyle="bold" + android:padding="2dip" + /> + <ImageView + android:id="@+id/battery" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:layout_gravity="center_vertical" + /> + <ImageView + android:id="@+id/network" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:layout_gravity="center_vertical" + android:src="@drawable/ic_sysbar_wifi_mini" + /> + </LinearLayout> + </LinearLayout> <LinearLayout android:id="@+id/navigationArea" @@ -126,9 +100,10 @@ android:layout_height="match_parent" android:layout_alignParentLeft="true" android:orientation="horizontal" + android:animateLayoutChanges="true" > - <com.android.systemui.statusbar.KeyButtonView android:id="@+id/back" + <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back" android:layout_width="wrap_content" android:layout_height="match_parent" android:paddingLeft="15dip" @@ -137,7 +112,7 @@ android:background="@drawable/ic_sysbar_icon_bg" systemui:keyCode="4" /> - <com.android.systemui.statusbar.KeyButtonView android:id="@+id/home" + <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home" android:layout_width="wrap_content" android:layout_height="match_parent" android:paddingLeft="15dip" @@ -154,7 +129,7 @@ android:paddingLeft="15dip" android:paddingRight="15dip" /> - <com.android.systemui.statusbar.KeyButtonView android:id="@+id/menu" + <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu" android:layout_width="wrap_content" android:layout_height="match_parent" android:paddingLeft="15dip" @@ -185,6 +160,15 @@ android:gravity="center" /> </com.android.systemui.statusbar.tablet.ShirtPocket> + <com.android.systemui.statusbar.tablet.InputMethodButton + android:id="@+id/imeButton" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginLeft="8dip" + android:src="@drawable/ic_sysbar_ime_default" + android:background="@drawable/ic_sysbar_icon_bg" + android:visibility="invisible" + /> </LinearLayout> </RelativeLayout> diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_center.xml b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml deleted file mode 100644 index d4f0e50..0000000 --- a/packages/SystemUI/res/layout-xlarge/status_bar_center.xml +++ /dev/null @@ -1,72 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- 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. ---> - -<!-- Center of status bar: System info display, system info panel trigger --> -<RelativeLayout android:id="@+id/systemInfo" - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_centerInParent="true" - android:clickable="true" - > - <com.android.systemui.statusbar.Clock - style="@*android:style/TextAppearance.StatusBar.Icon" - android:id="@+id/clock" - android:layout_width="64dip" - android:layout_height="48dip" - android:layout_centerInParent="true" - android:singleLine="true" - android:gravity="center" - android:textSize="16sp" - android:textStyle="bold" - android:padding="2dip" - /> - <ImageView - android:id="@+id/battery" - android:layout_width="64dip" - android:layout_height="16dip" - android:layout_toLeftOf="@id/clock" - android:layout_centerInParent="true" - android:background="@drawable/sysbar_minimeter_bg" - /> - <ImageView - android:id="@+id/signal" - android:layout_width="64dip" - android:layout_height="16dip" - android:layout_toRightOf="@id/clock" - android:layout_centerInParent="true" - android:background="@drawable/sysbar_minimeter_bg" - /> - <ImageView - android:id="@+id/battery_icon" - android:layout_height="30dip" - android:layout_width="30dip" - android:layout_toLeftOf="@id/battery" - android:layout_centerInParent="true" - android:src="@drawable/ic_sysbar_battery_mini" - /> - <ImageView - android:id="@+id/signal_icon" - android:layout_height="30dip" - android:layout_width="30dip" - android:layout_toRightOf="@id/signal" - android:layout_centerInParent="true" - android:src="@drawable/ic_sysbar_wifi_mini" - /> -</RelativeLayout> - - diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notification_peek.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notification_peek.xml index a7c91f5..02f9a90 100644 --- a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notification_peek.xml +++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notification_peek.xml @@ -19,7 +19,7 @@ --> <!-- android:background="@drawable/status_bar_closed_default_background" --> -<com.android.systemui.statusbar.tablet.NotificationPanel +<com.android.systemui.statusbar.tablet.NotificationPeekPanel xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:layout_width="match_parent" @@ -40,4 +40,4 @@ android:descendantFocusability="afterDescendants" > </FrameLayout> -</com.android.systemui.statusbar.tablet.NotificationPanel> +</com.android.systemui.statusbar.tablet.NotificationPeekPanel> diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml index 8cbf3e3..884a473 100644 --- a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml +++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml @@ -28,11 +28,97 @@ android:animateLayoutChanges="true" > + <com.android.systemui.statusbar.policy.Clock + android:id="@+id/clock" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:layout_alignParentTop="true" + android:gravity="right" + /> + + <com.android.systemui.statusbar.policy.DateView + android:id="@+id/date" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:layout_below="@id/clock" + android:gravity="right" + /> + + <ImageView + android:id="@+id/settings_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/date" + android:layout_alignParentRight="true" + android:paddingRight="10dp" + android:src="@drawable/ic_sysbar_quicksettings" + android:baseline="17dp" + /> + + <ImageView + android:id="@+id/notification_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBaseline="@id/settings_button" + android:layout_alignParentRight="true" + android:paddingRight="10dp" + android:visibility="invisible" + android:src="@drawable/status_bar_veto" + android:baseline="17dp" + /> + + <ImageView + android:id="@+id/battery" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:layout_alignParentLeft="true" + android:layout_alignBaseline="@id/settings_button" + android:src="@drawable/ic_sysbar_battery_mini" + android:baseline="17dp" + /> + + <TextView + android:id="@+id/battery_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/battery" + android:layout_alignBaseline="@id/settings_button" + android:singleLine="true" + android:text="@string/system_panel_settings_button" + /> + + <ImageView + android:id="@+id/network" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:layout_toRightOf="@id/battery_text" + android:layout_alignBaseline="@id/settings_button" + android:src="@drawable/ic_sysbar_wifi_mini" + android:baseline="21dp" + /> + + <TextView + android:id="@+id/network_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/network" + android:layout_alignBaseline="@id/settings_button" + android:singleLine="true" + android:text="@string/system_panel_settings_button" + /> + + <FrameLayout + android:id="@+id/settings_frame" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:layout_below="@id/settings_button" + /> + <ScrollView android:id="@+id/notificationScroller" android:layout_height="wrap_content" android:layout_width="match_parent" - android:animateLayoutChanges="true" + android:layout_below="@id/settings_button" > <LinearLayout android:id="@+id/content" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLinearLayout.java b/packages/SystemUI/res/layout-xlarge/sysbar_panel_settings.xml index 8105352..c6ddfed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLinearLayout.java +++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_settings.xml @@ -1,5 +1,6 @@ -/* - * Copyright (C) 2008 The Android Open Source Project +<?xml version="1.0" encoding="utf-8"?> +<!-- + * 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. @@ -12,18 +13,12 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ +--> -package com.android.systemui.statusbar; - -import android.content.Context; -import android.util.AttributeSet; -import android.widget.LinearLayout; - - -public class NotificationLinearLayout extends LinearLayout { - public NotificationLinearLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } -} +<com.android.systemui.statusbar.tablet.SettingsPanel + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="200dip" + > +</com.android.systemui.statusbar.tablet.SettingsPanel> diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml index e6ec9f4..a1792fd 100644 --- a/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml +++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml @@ -95,7 +95,6 @@ android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:scaleType="centerCrop" - android:src="@drawable/sysbar_battery" /> <TextView android:id="@+id/battery_info" style="@style/TextAppearance.StatusBar.SystemPanel" @@ -106,7 +105,7 @@ /> </RelativeLayout> - <com.android.systemui.statusbar.Clock + <com.android.systemui.statusbar.policy.Clock style="@style/TextAppearance.StatusBar.SystemPanel" android:id="@+id/clock" android:layout_width="wrap_content" @@ -130,7 +129,6 @@ android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:scaleType="centerCrop" - android:src="@drawable/sysbar_signal" /> <TextView android:id="@+id/signal_info" @@ -200,7 +198,7 @@ > <!-- TODO: alarm --> <!-- TODO: sync --> - <com.android.systemui.statusbar.DateView + <com.android.systemui.statusbar.policy.DateView android:id="@+id/date" style="@style/TextAppearance.StatusBar.SystemPanel" android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml index 2f1b36e..c8f5772 100644 --- a/packages/SystemUI/res/layout/status_bar.xml +++ b/packages/SystemUI/res/layout/status_bar.xml @@ -19,7 +19,7 @@ --> <!-- android:background="@drawable/status_bar_closed_default_background" --> -<com.android.systemui.statusbar.StatusBarView +<com.android.systemui.statusbar.phone.PhoneStatusBarView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" android:background="@drawable/status_bar_background" @@ -33,7 +33,7 @@ android:layout_height="match_parent" android:orientation="horizontal"> - <com.android.systemui.statusbar.IconMerger android:id="@+id/notificationIcons" + <com.android.systemui.statusbar.phone.IconMerger android:id="@+id/notificationIcons" android:layout_width="0dip" android:layout_weight="1" android:layout_height="match_parent" @@ -50,7 +50,7 @@ android:gravity="center_vertical" android:orientation="horizontal"/> - <com.android.systemui.statusbar.Clock + <com.android.systemui.statusbar.policy.Clock android:textAppearance="@*android:style/TextAppearance.StatusBar.Icon" android:layout_width="wrap_content" android:layout_height="match_parent" @@ -82,7 +82,7 @@ android:layout_height="25dip" /> </ImageSwitcher> - <com.android.systemui.statusbar.TickerView android:id="@+id/tickerText" + <com.android.systemui.statusbar.phone.TickerView android:id="@+id/tickerText" android:layout_width="0dip" android:layout_weight="1" android:layout_height="wrap_content" @@ -100,10 +100,10 @@ android:layout_height="wrap_content" android:singleLine="true" /> - </com.android.systemui.statusbar.TickerView> + </com.android.systemui.statusbar.phone.TickerView> </LinearLayout> - <com.android.systemui.statusbar.DateView android:id="@+id/date" + <com.android.systemui.statusbar.policy.DateView android:id="@+id/date" android:textAppearance="@*android:style/TextAppearance.StatusBar.Icon" android:layout_width="wrap_content" android:layout_height="match_parent" @@ -113,4 +113,4 @@ android:paddingRight="6px" android:background="@drawable/status_bar_background" /> -</com.android.systemui.statusbar.StatusBarView> +</com.android.systemui.statusbar.phone.PhoneStatusBarView> diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index b5b1b50..3ad199e 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -18,7 +18,7 @@ */ --> -<com.android.systemui.statusbar.ExpandedView xmlns:android="http://schemas.android.com/apk/res/android" +<com.android.systemui.statusbar.phone.ExpandedView xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:focusable="true" android:descendantFocusability="afterDescendants" @@ -33,7 +33,7 @@ android:paddingRight="3dp" android:background="@drawable/shade_header_background" > - <com.android.systemui.statusbar.CarrierLabel + <com.android.systemui.statusbar.phone.CarrierLabel android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" @@ -72,7 +72,7 @@ android:layout_height="match_parent" android:fadingEdge="none" > - <com.android.systemui.statusbar.NotificationLinearLayout + <LinearLayout android:id="@+id/notificationLinearLayout" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -115,7 +115,7 @@ android:layout_height="wrap_content" android:orientation="vertical" /> - </com.android.systemui.statusbar.NotificationLinearLayout> + </LinearLayout> </ScrollView> <ImageView @@ -126,4 +126,4 @@ /> </FrameLayout> -</com.android.systemui.statusbar.ExpandedView> +</com.android.systemui.statusbar.phone.ExpandedView> diff --git a/packages/SystemUI/res/layout/status_bar_tracking.xml b/packages/SystemUI/res/layout/status_bar_tracking.xml index a2b40e6..a0ddab5 100644 --- a/packages/SystemUI/res/layout/status_bar_tracking.xml +++ b/packages/SystemUI/res/layout/status_bar_tracking.xml @@ -15,7 +15,7 @@ limitations under the License. --> -<com.android.systemui.statusbar.TrackingView +<com.android.systemui.statusbar.phone.TrackingView xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:visibility="gone" @@ -26,13 +26,13 @@ android:paddingRight="0px" > - <com.android.systemui.statusbar.TrackingPatternView + <com.android.systemui.statusbar.phone.TrackingPatternView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" /> - <com.android.systemui.statusbar.CloseDragHandle android:id="@+id/close" + <com.android.systemui.statusbar.phone.CloseDragHandle android:id="@+id/close" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" @@ -45,6 +45,6 @@ android:src="@drawable/shade_handlebar" /> - </com.android.systemui.statusbar.CloseDragHandle> + </com.android.systemui.statusbar.phone.CloseDragHandle> -</com.android.systemui.statusbar.TrackingView> +</com.android.systemui.statusbar.phone.TrackingView> diff --git a/packages/SystemUI/res/values-xlarge/config.xml b/packages/SystemUI/res/values-xlarge/config.xml index 6df883c..e6af4f5 100644 --- a/packages/SystemUI/res/values-xlarge/config.xml +++ b/packages/SystemUI/res/values-xlarge/config.xml @@ -24,7 +24,7 @@ <!-- Component to be used as the status bar service. Must implement the IStatusBar interface. This name is in the ComponentName flattened format (package/class) --> - <string name="config_statusBarComponent">com.android.systemui.statusbar.tablet.TabletStatusBarService</string> + <string name="config_statusBarComponent">com.android.systemui.statusbar.tablet.TabletStatusBar</string> </resources> diff --git a/packages/SystemUI/res/values-xlarge/strings.xml b/packages/SystemUI/res/values-xlarge/strings.xml index 3c59c92..e305681 100644 --- a/packages/SystemUI/res/values-xlarge/strings.xml +++ b/packages/SystemUI/res/values-xlarge/strings.xml @@ -23,13 +23,6 @@ <!-- System panel ("Quick Settings") --> - <!-- Text to display underneath the graphical battery meter. Should - include the word for "battery" and a place for the percentage charge - available. [CHAR LIMIT=20] --> - <string name="system_panel_battery_meter_format"> - Battery: <xliff:g id="number">%d</xliff:g><xliff:g id="percent">%%</xliff:g> - </string> - <!-- Text to display underneath the graphical signal strength meter when no connection is available. [CHAR LIMIT=20] --> <string name="system_panel_signal_meter_disconnected"> @@ -38,10 +31,9 @@ <!-- Text to display underneath the graphical signal strength meter when it is displaying information about a connected, named Wi-Fi network. - Should include the word for "Wi-Fi" and a placeholder for the - wireless network's SSID. [CHAR LIMIT=20] --> + [CHAR LIMIT=20] --> <string name="system_panel_signal_meter_wifi_ssid_format"> - Wi-Fi: <xliff:g id="ssid">%s</xliff:g> + <xliff:g id="ssid">%s</xliff:g> </string> <!-- Text to display underneath the graphical signal strength meter when diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 07cb52a..86beb14 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -33,7 +33,7 @@ <!-- Component to be used as the status bar service. Must implement the IStatusBar interface. This name is in the ComponentName flattened format (package/class) --> - <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.PhoneStatusBarService</string> + <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar</string> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index a65de37..e8c3c91 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -18,7 +18,7 @@ --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- Name of the status bar as seen in the applications info settings page. [CHAR LIMIT=12] --> - <string name="app_label">Status Bar</string> + <string name="app_label">System UI</string> <!-- The text for the button in the notification window-shade that clears all of the currently visible notifications. [CHAR LIMIT=10]--> @@ -74,6 +74,10 @@ <!-- Name of the button that links to the Settings app. [CHAR LIMIT=NONE] --> <string name="system_panel_settings_button">Settings</string> + <!-- Text to display next to the graphical battery meter. [CHAR LIMIT=3] --> + <string name="system_panel_battery_meter_format" translatable="false"> + <xliff:g id="number">%d</xliff:g><xliff:g id="percent">%%</xliff:g> + </string> <!-- Recent Tasks dialog: title [CHAR LIMIT=30] --> <string name="recent_tasks_title">Recent</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 5aefb02..86ffb4d 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -39,4 +39,17 @@ <item name="android:textStyle">normal</item> <item name="android:textColor">#FFFFFFFF</item> </style> + + <style name="Animation" /> + + <style name="Animation.ShirtPocketPanel"> + <item name="android:windowEnterAnimation">@*android:anim/grow_fade_in_from_bottom</item> + <item name="android:windowExitAnimation">@*android:anim/shrink_fade_out_from_bottom</item> + </style> + + <style name="Animation.RecentPanel"> + <item name="android:windowEnterAnimation">@*android:anim/grow_fade_in_from_bottom</item> + <item name="android:windowExitAnimation">@*android:anim/shrink_fade_out_from_bottom</item> + </style> + </resources> diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java index faea3fc..ff2a4ed 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java @@ -29,6 +29,8 @@ import java.util.List; import android.app.Activity; import android.app.ActivityManager; +import android.app.ActivityManagerNative; +import android.app.IActivityManager; import android.app.IThumbnailReceiver; import android.app.ActivityManager.RunningTaskInfo; import android.content.ActivityNotFoundException; @@ -189,8 +191,15 @@ public class RecentApplicationsActivity extends Activity { public void onCardSelected(int n) { if (n < mActivityDescriptions.size()) { ActivityDescription item = mActivityDescriptions.get(n); - // prepare a launch intent and send it - if (item.intent != null) { + if (item.id >= 0) { + // This is an active task; it should just go to the foreground. + IActivityManager am = ActivityManagerNative.getDefault(); + try { + am.moveTaskToFront(item.id); + } catch (RemoteException e) { + } + } else if (item.intent != null) { + // prepare a launch intent and send it item.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY); try { if (DBG) Log.v(TAG, "Starting intent " + item.intent); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java index 776b59c..731f6cd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java @@ -34,7 +34,6 @@ import android.view.WindowManagerImpl; import java.util.ArrayList; -import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; @@ -43,8 +42,8 @@ import com.android.internal.statusbar.StatusBarNotification; import com.android.systemui.SystemUI; import com.android.systemui.R; -public abstract class StatusBarService extends SystemUI implements CommandQueue.Callbacks { - static final String TAG = "StatusBarService"; +public abstract class StatusBar extends SystemUI implements CommandQueue.Callbacks { + static final String TAG = "StatusBar"; private static final boolean SPEW = false; protected CommandQueue mCommandQueue; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 8419e56..9a61be6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -84,7 +84,7 @@ public class StatusBarIconView extends AnimatedImageView { if (!iconEquals) { Drawable drawable = getIcon(icon); if (drawable == null) { - Slog.w(StatusBarService.TAG, "No icon for slot " + mSlot); + Slog.w(StatusBar.TAG, "No icon for slot " + mSlot); return false; } setImageDrawable(drawable); @@ -130,7 +130,7 @@ public class StatusBarIconView extends AnimatedImageView { try { r = context.getPackageManager().getResourcesForApplication(icon.iconPackage); } catch (PackageManager.NameNotFoundException ex) { - Slog.e(StatusBarService.TAG, "Icon package not found: " + icon.iconPackage); + Slog.e(StatusBar.TAG, "Icon package not found: " + icon.iconPackage); return null; } } else { @@ -144,7 +144,7 @@ public class StatusBarIconView extends AnimatedImageView { try { return r.getDrawable(icon.iconId); } catch (RuntimeException e) { - Slog.w(StatusBarService.TAG, "Icon not found in " + Slog.w(StatusBar.TAG, "Icon not found in " + (icon.iconPackage != null ? icon.iconId : "<system>") + ": " + Integer.toHexString(icon.iconId)); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CarrierLabel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CarrierLabel.java index 31b78b6..d8441f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CarrierLabel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CarrierLabel.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.statusbar.phone; import android.content.BroadcastReceiver; import android.content.Context; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CloseDragHandle.java index 0f6723e..ba64282 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CloseDragHandle.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.statusbar.phone; import android.content.Context; import android.util.AttributeSet; @@ -23,7 +23,7 @@ import android.widget.LinearLayout; public class CloseDragHandle extends LinearLayout { - PhoneStatusBarService mService; + PhoneStatusBar mService; public CloseDragHandle(Context context, AttributeSet attrs) { super(context, attrs); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandedView.java index a2d4b95..92b8976 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandedView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.statusbar.phone; import android.content.Context; import android.util.AttributeSet; @@ -27,7 +27,7 @@ import android.util.Slog; public class ExpandedView extends LinearLayout { - PhoneStatusBarService mService; + PhoneStatusBar mService; int mPrevHeight = -1; public ExpandedView(Context context, AttributeSet attrs) { @@ -50,10 +50,10 @@ public class ExpandedView extends LinearLayout { super.onLayout(changed, left, top, right, bottom); int height = bottom - top; if (height != mPrevHeight) { - //Slog.d(StatusBarService.TAG, "height changed old=" + mPrevHeight + //Slog.d(StatusBar.TAG, "height changed old=" + mPrevHeight // + " new=" + height); mPrevHeight = height; - mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE); + mService.updateExpandedViewPos(PhoneStatusBar.EXPANDED_LEAVE_ALONE); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/IconMerger.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java index e87d003..e1d17a8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/IconMerger.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.statusbar.phone; import android.content.Context; import android.os.Handler; @@ -26,7 +26,7 @@ import android.widget.LinearLayout; import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.R; - +import com.android.systemui.statusbar.StatusBarIconView; public class IconMerger extends LinearLayout { private static final String TAG = "IconMerger"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index d3d2285..2dad81c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.statusbar.phone; import android.app.Service; import android.app.ActivityManagerNative; @@ -66,19 +66,19 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Set; -import com.android.internal.statusbar.IStatusBar; -import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; import com.android.internal.statusbar.StatusBarNotification; import com.android.systemui.R; -import com.android.systemui.statusbar.policy.StatusBarPolicy; +import com.android.systemui.statusbar.NotificationData; +import com.android.systemui.statusbar.StatusBar; +import com.android.systemui.statusbar.StatusBarIconView; +import com.android.systemui.statusbar.policy.DateView; - -public class PhoneStatusBarService extends StatusBarService { - static final String TAG = "PhoneStatusBarService"; +public class PhoneStatusBar extends StatusBar { + static final String TAG = "PhoneStatusBar"; static final boolean SPEW = false; public static final String ACTION_STATUSBAR_START @@ -95,12 +95,12 @@ public class PhoneStatusBarService extends StatusBarService { // will likely move to a resource or other tunable param at some point private static final int INTRUDER_ALERT_DECAY_MS = 10000; - StatusBarPolicy mIconPolicy; + PhoneStatusBarPolicy mIconPolicy; int mIconSize; Display mDisplay; - StatusBarView mStatusBarView; + PhoneStatusBarView mStatusBarView; int mPixelFormat; H mHandler = new H(); Object mQueueLock = new Object(); @@ -202,7 +202,7 @@ public class PhoneStatusBarService extends StatusBarService { addIntruderView(); // Lastly, call to the icon policy to install/update all the icons. - mIconPolicy = new StatusBarPolicy(mContext); + mIconPolicy = new PhoneStatusBarPolicy(mContext); } // ================================================================================ @@ -223,7 +223,8 @@ public class PhoneStatusBarService extends StatusBarService { mIntruderAlertView.setVisibility(View.GONE); mIntruderAlertView.setClickable(true); - StatusBarView sb = (StatusBarView)View.inflate(context, R.layout.status_bar, null); + PhoneStatusBarView sb = (PhoneStatusBarView)View.inflate(context, + R.layout.status_bar, null); sb.mService = this; // figure out which pixel-format to use for the status bar. @@ -1304,7 +1305,6 @@ public class PhoneStatusBarService extends StatusBarService { } void setDateViewVisibility(boolean visible, int anim) { - mDateView.setUpdates(visible); mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); mDateView.startAnimation(loadAnim(anim, null)); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 4f5c3ae..73b6723 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.phone; import android.app.StatusBarManager; import android.bluetooth.BluetoothAdapter; @@ -71,8 +71,8 @@ import com.android.systemui.R; * bar at boot time. It goes through the normal API for icons, even though it probably * strictly doesn't need to. */ -public class StatusBarPolicy { - private static final String TAG = "StatusBarPolicy"; +public class PhoneStatusBarPolicy { + private static final String TAG = "PhoneStatusBarPolicy"; // message codes for the handler private static final int EVENT_BATTERY_CLOSE = 4; @@ -368,7 +368,7 @@ public class StatusBarPolicy { } }; - public StatusBarPolicy(Context context) { + public PhoneStatusBarPolicy(Context context) { mContext = context; mService = (StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE); mSignalStrength = new SignalStrength(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index 20fc41f..84c524a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.statusbar.phone; import android.content.Context; import android.content.res.Configuration; @@ -29,13 +29,14 @@ import android.view.ViewParent; import android.widget.FrameLayout; import com.android.systemui.R; +import com.android.systemui.statusbar.policy.FixedSizeDrawable; -public class StatusBarView extends FrameLayout { - private static final String TAG = "StatusBarView"; +public class PhoneStatusBarView extends FrameLayout { + private static final String TAG = "PhoneStatusBarView"; static final int DIM_ANIM_TIME = 400; - PhoneStatusBarService mService; + PhoneStatusBar mService; boolean mTracking; int mStartX, mStartY; ViewGroup mNotificationIcons; @@ -50,7 +51,7 @@ public class StatusBarView extends FrameLayout { Rect mButtonBounds = new Rect(); boolean mCapturingEvents = true; - public StatusBarView(Context context, AttributeSet attrs) { + public PhoneStatusBarView(Context context, AttributeSet attrs) { super(context, attrs); } @@ -98,7 +99,7 @@ public class StatusBarView extends FrameLayout { @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); - mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE); + mService.updateExpandedViewPos(PhoneStatusBar.EXPANDED_LEAVE_ALONE); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java index e7b0509..8ee12de 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.statusbar.phone; import android.content.Context; import android.graphics.drawable.Drawable; @@ -36,7 +36,9 @@ import java.util.ArrayList; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarNotification; import com.android.internal.util.CharSequences; + import com.android.systemui.R; +import com.android.systemui.statusbar.StatusBarIconView; public abstract class Ticker { private static final int TICKER_SEGMENT_DELAY = 3000; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TickerView.java index 8140811..8aa3837 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TickerView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.statusbar.phone; import android.content.Context; import android.util.AttributeSet; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TrackingPatternView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrackingPatternView.java index ba6f15d..d2ed5ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/TrackingPatternView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrackingPatternView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.statusbar.phone; import android.content.Context; import android.content.res.TypedArray; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrackingView.java index c59eb6a..fd32a3d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrackingView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.statusbar.phone; import android.content.Context; import android.util.AttributeSet; @@ -26,7 +26,7 @@ import android.widget.LinearLayout; public class TrackingView extends LinearLayout { final Display mDisplay; - PhoneStatusBarService mService; + PhoneStatusBar mService; boolean mTracking; int mStartX, mStartY; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java new file mode 100644 index 0000000..a1efdd4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java @@ -0,0 +1,74 @@ +/* + * 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 com.android.systemui.statusbar.policy; + +import java.util.ArrayList; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.BatteryManager; +import android.util.Slog; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.systemui.R; + +public class BatteryController extends BroadcastReceiver { + private static final String TAG = "StatusBar.BatteryController"; + + private Context mContext; + private ArrayList<ImageView> mIconViews = new ArrayList<ImageView>(); + private ArrayList<TextView> mLabelViews = new ArrayList<TextView>(); + + public BatteryController(Context context) { + mContext = context; + + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_BATTERY_CHANGED); + context.registerReceiver(this, filter); + } + + public void addIconView(ImageView v) { + mIconViews.add(v); + } + + public void addLabelView(TextView v) { + mLabelViews.add(v); + } + + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { + final int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); + int N = mIconViews.size(); + for (int i=0; i<N; i++) { + final int icon = intent.getIntExtra(BatteryManager.EXTRA_ICON_SMALL, 0); + ImageView v = mIconViews.get(i); + v.setImageResource(icon); + v.setImageLevel(level); + } + N = mLabelViews.size(); + for (int i=0; i<N; i++) { + //final boolean plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0; + TextView v = mLabelViews.get(i); + v.setText(mContext.getString(R.string.system_panel_battery_meter_format, level)); + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index 9fc8df5..69872df 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.statusbar.policy; import android.content.BroadcastReceiver; import android.content.Context; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java index e127038..136f4a94 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/DateView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.statusbar.policy; import android.content.BroadcastReceiver; import android.content.Context; @@ -24,6 +24,8 @@ import android.util.AttributeSet; import android.util.Slog; import android.widget.TextView; import android.view.MotionEvent; +import android.view.View; +import android.view.ViewParent; import java.text.DateFormat; import java.util.Date; @@ -31,7 +33,9 @@ import java.util.Date; public final class DateView extends TextView { private static final String TAG = "DateView"; - private boolean mUpdating = false; + private boolean mAttachedToWindow; + private boolean mWindowVisible; + private boolean mUpdating; private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override @@ -51,12 +55,28 @@ public final class DateView extends TextView { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + mAttachedToWindow = true; + setUpdates(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - setUpdates(false); + mAttachedToWindow = false; + setUpdates(); + } + + @Override + protected void onWindowVisibilityChanged(int visibility) { + super.onWindowVisibilityChanged(visibility); + mWindowVisible = visibility == VISIBLE; + setUpdates(); + } + + @Override + protected void onVisibilityChanged(View changedView, int visibility) { + super.onVisibilityChanged(changedView, visibility); + setUpdates(); } @Override @@ -67,10 +87,26 @@ public final class DateView extends TextView { private final void updateClock() { Date now = new Date(); - setText(DateFormat.getDateInstance(DateFormat.LONG).format(now)); + setText(DateFormat.getDateInstance(DateFormat.FULL).format(now)); + } + + private boolean isVisible() { + View v = this; + while (true) { + if (v.getVisibility() != VISIBLE) { + return false; + } + final ViewParent parent = v.getParent(); + if (parent instanceof View) { + v = (View)parent; + } else { + return true; + } + } } - public void setUpdates(boolean update) { + private void setUpdates() { + boolean update = mAttachedToWindow && mWindowVisible && isVisible(); if (update != mUpdating) { mUpdating = update; if (update) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FixedSizeDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FixedSizeDrawable.java index eb22b61..8f2f5f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FixedSizeDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FixedSizeDrawable.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.statusbar.policy; import android.graphics.drawable.Drawable; import android.graphics.Canvas; @@ -22,14 +22,14 @@ import android.graphics.ColorFilter; import android.graphics.Rect; import android.util.Slog; -class FixedSizeDrawable extends Drawable { +public class FixedSizeDrawable extends Drawable { Drawable mDrawable; int mLeft; int mTop; int mRight; int mBottom; - FixedSizeDrawable(Drawable that) { + public FixedSizeDrawable(Drawable that) { mDrawable = that; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java index b01c5e7..aed3dc7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.statusbar.policy; import android.content.Context; import android.content.res.TypedArray; @@ -37,6 +37,8 @@ import android.widget.RemoteViews.RemoteView; import com.android.systemui.R; public class KeyButtonView extends ImageView { + private static final String TAG = "StatusBar.KeyButtonView"; + IWindowManager mWindowManager; long mDownTime; boolean mSending, mLongPressed; @@ -68,8 +70,7 @@ public class KeyButtonView extends ImageView { mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, 0); if (mCode == 0) { - Slog.w(StatusBarService.TAG, "KeyButtonView without key code id=0x" - + Integer.toHexString(getId())); + Slog.w(TAG, "KeyButtonView without key code id=0x" + Integer.toHexString(getId())); } a.recycle(); @@ -99,22 +100,24 @@ public class KeyButtonView extends ImageView { if (mSending) { x = (int)ev.getX(); y = (int)ev.getY(); - if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight()) { - mSending = false; - sendEvent(KeyEvent.ACTION_UP, - KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY - | KeyEvent.FLAG_CANCELED); - setPressed(false); - removeCallbacks(mCheckLongPress); - } + setPressed(x >= 0 && x < getWidth() && y >= 0 && y < getHeight()); } break; - case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: setPressed(false); if (mSending && !mLongPressed) { mSending = false; sendEvent(KeyEvent.ACTION_UP, + KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY + | KeyEvent.FLAG_CANCELED); + removeCallbacks(mCheckLongPress); + } + break; + case MotionEvent.ACTION_UP: + setPressed(false); + if (mSending && !mLongPressed) { + mSending = false; + sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY); removeCallbacks(mCheckLongPress); } @@ -132,7 +135,7 @@ public class KeyButtonView extends ImageView { final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, mRepeat, 0, 0, 0, flags, InputDevice.SOURCE_KEYBOARD); try { - //Slog.d(StatusBarService.TAG, "injecting event " + ev); + //Slog.d(TAG, "injecting event " + ev); mWindowManager.injectInputEventNoWait(ev); } catch (RemoteException ex) { // System process is dead diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java new file mode 100644 index 0000000..4ff2429 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -0,0 +1,626 @@ +/* + * 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 com.android.systemui.statusbar.policy; + +import java.util.ArrayList; +import java.util.List; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.wifi.SupplicantState; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.os.Binder; +import android.os.RemoteException; +import android.provider.Settings; +import android.telephony.PhoneStateListener; +import android.telephony.ServiceState; +import android.telephony.SignalStrength; +import android.telephony.TelephonyManager; +import android.util.Slog; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.internal.app.IBatteryStats; +import com.android.internal.telephony.IccCard; +import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.telephony.cdma.EriInfo; +import com.android.server.am.BatteryStatsService; + +import com.android.systemui.R; + +public class NetworkController extends BroadcastReceiver { + // debug + static final String TAG = "StatusBar.NetworkController"; + static final boolean DEBUG = false; + + // telephony + boolean mHspaDataDistinguishable; + final TelephonyManager mPhone; + boolean mDataConnected; + int mPhoneSignalIconId; + int mDataIconId; + IccCard.State mSimState = IccCard.State.READY; + int mPhoneState = TelephonyManager.CALL_STATE_IDLE; + int mDataState = TelephonyManager.DATA_DISCONNECTED; + int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE; + ServiceState mServiceState; + SignalStrength mSignalStrength; + int[] mDataIconList = TelephonyIcons.DATA_G[0]; + + // wifi + final WifiManager mWifiManager; + boolean mWifiEnabled, mWifiConnected; + int mWifiLevel; + String mWifiSsid; + int mWifiIconId; + + // data connectivity (regardless of state, can we access the internet?) + // state of inet connection - 0 not connected, 100 connected + private int mInetCondition = 0; + private static final int INET_CONDITION_THRESHOLD = 50; + + // our ui + Context mContext; + ArrayList<ImageView> mPhoneIconViews = new ArrayList<ImageView>(); + ArrayList<ImageView> mDataIconViews = new ArrayList<ImageView>(); + ArrayList<TextView> mLabelViews = new ArrayList<TextView>(); + int mLastPhoneSignalIconId = -1; + int mLastCombinedDataIconId = -1; + String mLastLabel = ""; + + // yuck -- stop doing this here and put it in the framework + IBatteryStats mBatteryStats; + + /** + * Construct this controller object and register for updates. + */ + public NetworkController(Context context) { + mContext = context; + + // telephony + mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); + mPhone.listen(mPhoneStateListener, + PhoneStateListener.LISTEN_SERVICE_STATE + | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS + | PhoneStateListener.LISTEN_CALL_STATE + | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE + | PhoneStateListener.LISTEN_DATA_ACTIVITY); + mHspaDataDistinguishable = mContext.getResources().getBoolean( + R.bool.config_hspa_data_distinguishable); + + + // wifi + mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + + // broadcasts + IntentFilter filter = new IntentFilter(); + filter.addAction(WifiManager.RSSI_CHANGED_ACTION); + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); + context.registerReceiver(this, filter); + + // yuck + mBatteryStats = BatteryStatsService.getService(); + } + + public void addPhoneIconView(ImageView v) { + mPhoneIconViews.add(v); + } + + public void addCombinedDataIconView(ImageView v) { + mDataIconViews.add(v); + } + + public void addLabelView(TextView v) { + mLabelViews.add(v); + } + + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (action.equals(WifiManager.RSSI_CHANGED_ACTION) + || action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION) + || action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION) + || action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { + updateWifiState(intent); + refreshViews(); + } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) { + updateSimState(intent); + updateDataIcon(); + refreshViews(); + } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) || + action.equals(ConnectivityManager.INET_CONDITION_ACTION)) { + updateConnectivity(intent); + refreshViews(); + } + } + + + // ===== Telephony ============================================================== + + PhoneStateListener mPhoneStateListener = new PhoneStateListener() { + @Override + public void onSignalStrengthsChanged(SignalStrength signalStrength) { + if (DEBUG) { + Slog.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength); + } + mSignalStrength = signalStrength; + updateTelephonySignalStrength(); + refreshViews(); + } + + @Override + public void onServiceStateChanged(ServiceState state) { + if (DEBUG) { + Slog.d(TAG, "onServiceStateChanged state=" + state.getState()); + } + mServiceState = state; + updateTelephonySignalStrength(); + updateDataIcon(); + refreshViews(); + } + + @Override + public void onCallStateChanged(int state, String incomingNumber) { + if (DEBUG) { + Slog.d(TAG, "onCallStateChanged state=" + state); + } + // In cdma, if a voice call is made, RSSI should switch to 1x. + if (isCdma()) { + updateTelephonySignalStrength(); + refreshViews(); + } + } + + @Override + public void onDataConnectionStateChanged(int state, int networkType) { + if (DEBUG) { + Slog.d(TAG, "onDataConnectionStateChanged: state=" + state + + " type=" + networkType); + } + mDataState = state; + updateDataNetType(networkType); + updateDataIcon(); + refreshViews(); + } + + @Override + public void onDataActivity(int direction) { + if (DEBUG) { + Slog.d(TAG, "onDataActivity: direction=" + direction); + } + mDataActivity = direction; + updateDataIcon(); + refreshViews(); + } + }; + + private final void updateSimState(Intent intent) { + String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE); + if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { + mSimState = IccCard.State.ABSENT; + } + else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) { + mSimState = IccCard.State.READY; + } + else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) { + final String lockedReason = intent.getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON); + if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) { + mSimState = IccCard.State.PIN_REQUIRED; + } + else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) { + mSimState = IccCard.State.PUK_REQUIRED; + } + else { + mSimState = IccCard.State.NETWORK_LOCKED; + } + } else { + mSimState = IccCard.State.UNKNOWN; + } + } + + private boolean isCdma() { + return (mSignalStrength != null) && !mSignalStrength.isGsm(); + } + + private boolean isEvdo() { + return ((mServiceState != null) + && ((mServiceState.getRadioTechnology() == ServiceState.RADIO_TECHNOLOGY_EVDO_0) + || (mServiceState.getRadioTechnology() == ServiceState.RADIO_TECHNOLOGY_EVDO_A) + || (mServiceState.getRadioTechnology() == ServiceState.RADIO_TECHNOLOGY_EVDO_B))); + } + + private boolean hasService() { + if (mServiceState != null) { + switch (mServiceState.getState()) { + case ServiceState.STATE_OUT_OF_SERVICE: + case ServiceState.STATE_POWER_OFF: + return false; + default: + return true; + } + } else { + return false; + } + } + + private int getCdmaLevel() { + if (mSignalStrength == null) return 0; + final int cdmaDbm = mSignalStrength.getCdmaDbm(); + final int cdmaEcio = mSignalStrength.getCdmaEcio(); + int levelDbm = 0; + int levelEcio = 0; + + if (cdmaDbm >= -75) levelDbm = 4; + else if (cdmaDbm >= -85) levelDbm = 3; + else if (cdmaDbm >= -95) levelDbm = 2; + else if (cdmaDbm >= -100) levelDbm = 1; + else levelDbm = 0; + + // Ec/Io are in dB*10 + if (cdmaEcio >= -90) levelEcio = 4; + else if (cdmaEcio >= -110) levelEcio = 3; + else if (cdmaEcio >= -130) levelEcio = 2; + else if (cdmaEcio >= -150) levelEcio = 1; + else levelEcio = 0; + + return (levelDbm < levelEcio) ? levelDbm : levelEcio; + } + + private int getEvdoLevel() { + if (mSignalStrength == null) return 0; + int evdoDbm = mSignalStrength.getEvdoDbm(); + int evdoSnr = mSignalStrength.getEvdoSnr(); + int levelEvdoDbm = 0; + int levelEvdoSnr = 0; + + if (evdoDbm >= -65) levelEvdoDbm = 4; + else if (evdoDbm >= -75) levelEvdoDbm = 3; + else if (evdoDbm >= -90) levelEvdoDbm = 2; + else if (evdoDbm >= -105) levelEvdoDbm = 1; + else levelEvdoDbm = 0; + + if (evdoSnr >= 7) levelEvdoSnr = 4; + else if (evdoSnr >= 5) levelEvdoSnr = 3; + else if (evdoSnr >= 3) levelEvdoSnr = 2; + else if (evdoSnr >= 1) levelEvdoSnr = 1; + else levelEvdoSnr = 0; + + return (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr; + } + + private final void updateTelephonySignalStrength() { + // Display signal strength while in "emergency calls only" mode + if (mServiceState == null || (!hasService() && !mServiceState.isEmergencyOnly())) { + //Slog.d(TAG, "updateTelephonySignalStrength: no service"); + if (Settings.System.getInt(mContext.getContentResolver(), + Settings.System.AIRPLANE_MODE_ON, 0) == 1) { + mPhoneSignalIconId = R.drawable.stat_sys_signal_flightmode; + } else { + mPhoneSignalIconId = R.drawable.stat_sys_signal_null; + } + } else { + if (mSignalStrength == null) { + mPhoneSignalIconId = R.drawable.stat_sys_signal_null; + } else if (isCdma()) { + // If 3G(EV) and 1x network are available than 3G should be + // displayed, displayed RSSI should be from the EV side. + // If a voice call is made then RSSI should switch to 1x. + int iconLevel; + if ((mPhoneState == TelephonyManager.CALL_STATE_IDLE) && isEvdo()){ + iconLevel = getEvdoLevel(); + } else { + iconLevel = getCdmaLevel(); + } + int[] iconList; + if (isCdmaEri()) { + iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition]; + } else { + iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition]; + } + mPhoneSignalIconId = iconList[iconLevel]; + } else { + int asu = mSignalStrength.getGsmSignalStrength(); + + // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5 + // asu = 0 (-113dB or less) is very weak + // signal, its better to show 0 bars to the user in such cases. + // asu = 99 is a special case, where the signal strength is unknown. + int iconLevel; + if (asu <= 2 || asu == 99) iconLevel = 0; + else if (asu >= 12) iconLevel = 4; + else if (asu >= 8) iconLevel = 3; + else if (asu >= 5) iconLevel = 2; + else iconLevel = 1; + + // Though mPhone is a Manager, this call is not an IPC + int[] iconList; + if (mPhone.isNetworkRoaming()) { + iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition]; + } else { + iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition]; + } + mPhoneSignalIconId = iconList[iconLevel]; + } + } + } + + private final void updateDataNetType(int net) { + switch (net) { + case TelephonyManager.NETWORK_TYPE_EDGE: + mDataIconList = TelephonyIcons.DATA_E[mInetCondition]; + break; + case TelephonyManager.NETWORK_TYPE_UMTS: + mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; + break; + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + if (mHspaDataDistinguishable) { + mDataIconList = TelephonyIcons.DATA_H[mInetCondition]; + } else { + mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; + } + break; + case TelephonyManager.NETWORK_TYPE_CDMA: + // display 1xRTT for IS95A/B + mDataIconList = TelephonyIcons.DATA_1X[mInetCondition]; + break; + case TelephonyManager.NETWORK_TYPE_1xRTT: + mDataIconList = TelephonyIcons.DATA_1X[mInetCondition]; + break; + case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through + case TelephonyManager.NETWORK_TYPE_EVDO_A: + case TelephonyManager.NETWORK_TYPE_EVDO_B: + mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; + break; + // TODO - add support for NETWORK_TYPE_LTE and NETWORK_TYPE_EHRPD + default: + mDataIconList = TelephonyIcons.DATA_G[mInetCondition]; + break; + } + } + + boolean isCdmaEri() { + final int iconIndex = mServiceState.getCdmaEriIconIndex(); + if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) { + final int iconMode = mServiceState.getCdmaEriIconMode(); + if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL + || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) { + return true; + } + } + return false; + } + + private final void updateDataIcon() { + int iconId; + boolean visible = true; + + if (!isCdma()) { + // GSM case, we have to check also the sim state + if (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN) { + if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) { + switch (mDataActivity) { + case TelephonyManager.DATA_ACTIVITY_IN: + iconId = mDataIconList[1]; + break; + case TelephonyManager.DATA_ACTIVITY_OUT: + iconId = mDataIconList[2]; + break; + case TelephonyManager.DATA_ACTIVITY_INOUT: + iconId = mDataIconList[3]; + break; + default: + iconId = mDataIconList[0]; + break; + } + mDataIconId = iconId; + } else { + iconId = 0; + visible = false; + } + } else { + iconId = R.drawable.stat_sys_no_sim; + } + } else { + // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT + if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) { + switch (mDataActivity) { + case TelephonyManager.DATA_ACTIVITY_IN: + iconId = mDataIconList[1]; + break; + case TelephonyManager.DATA_ACTIVITY_OUT: + iconId = mDataIconList[2]; + break; + case TelephonyManager.DATA_ACTIVITY_INOUT: + iconId = mDataIconList[3]; + break; + case TelephonyManager.DATA_ACTIVITY_DORMANT: + default: + iconId = mDataIconList[0]; + break; + } + } else { + iconId = 0; + visible = false; + } + } + + // yuck - this should NOT be done by the status bar + long ident = Binder.clearCallingIdentity(); + try { + mBatteryStats.notePhoneDataConnectionState(mPhone.getNetworkType(), visible); + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(ident); + } + + mDataIconId = iconId; + mDataConnected = visible; + } + + // ===== Wifi =================================================================== + + private void updateWifiState(Intent intent) { + final String action = intent.getAction(); + if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { + mWifiEnabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, + WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; + + } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION) + || action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)) { + final NetworkInfo networkInfo = (NetworkInfo) + intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); + boolean wasConnected = mWifiConnected; + mWifiConnected = networkInfo != null && networkInfo.isConnected(); + // If we just connected, grab the inintial signal strength and ssid + if (mWifiConnected && !wasConnected) { + WifiInfo info = mWifiManager.getConnectionInfo(); + if (info != null) { + mWifiLevel = WifiManager.calculateSignalLevel(info.getRssi(), + WifiIcons.WIFI_LEVEL_COUNT); + mWifiSsid = huntForSsid(info); + } else { + mWifiLevel = 0; + mWifiSsid = null; + } + } else if (!mWifiConnected) { + mWifiLevel = 0; + mWifiSsid = null; + } + + } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) { + if (mWifiConnected) { + final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200); + mWifiLevel = WifiManager.calculateSignalLevel(newRssi, WifiIcons.WIFI_LEVEL_COUNT); + } + } + + updateWifiIcons(); + } + + private void updateWifiIcons() { + if (mWifiConnected) { + mWifiIconId = WifiIcons.WIFI_SIGNAL_STRENGTH[mInetCondition][mWifiLevel]; + } else { + mWifiIconId = WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]; + } + } + + private String huntForSsid(WifiInfo info) { + String ssid = info.getSSID(); + if (ssid != null) { + return ssid; + } + // OK, it's not in the connectionInfo; we have to go hunting for it + List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks(); + for (WifiConfiguration net : networks) { + if (net.networkId == info.getNetworkId()) { + return net.SSID; + } + } + return null; + } + + + // ===== Full or limited Internet connectivity ================================== + + private void updateConnectivity(Intent intent) { + NetworkInfo info = (NetworkInfo)(intent.getParcelableExtra( + ConnectivityManager.EXTRA_NETWORK_INFO)); + int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0); + + int inetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0); + + switch (info.getType()) { + case ConnectivityManager.TYPE_MOBILE: + mInetCondition = inetCondition; + updateDataNetType(info.getSubtype()); + updateDataIcon(); + updateTelephonySignalStrength(); // apply any change in connectionStatus + break; + case ConnectivityManager.TYPE_WIFI: + mInetCondition = inetCondition; + updateWifiIcons(); + break; + } + } + + + // ===== Update the views ======================================================= + + // figure out what to show: first wifi, then 3G, then nothing + void refreshViews() { + Context context = mContext; + + int combinedDataIconId; + String label; + + if (mWifiConnected) { + if (mWifiSsid == null) { + label = context.getString(R.string.system_panel_signal_meter_wifi_nossid); + } else { + label = context.getString(R.string.system_panel_signal_meter_wifi_ssid_format, + mWifiSsid); + } + combinedDataIconId = mWifiIconId; + } else if (mDataConnected) { + label = context.getString(R.string.system_panel_signal_meter_data_connected); + combinedDataIconId = mDataIconId; + } else { + label = context.getString(R.string.system_panel_signal_meter_disconnected); + combinedDataIconId = 0; + } + + int N; + + if (mLastPhoneSignalIconId != mPhoneSignalIconId) { + mLastPhoneSignalIconId = mPhoneSignalIconId; + N = mPhoneIconViews.size(); + for (int i=0; i<N; i++) { + ImageView v = mPhoneIconViews.get(i); + v.setImageResource(mPhoneSignalIconId); + } + } + + if (mLastCombinedDataIconId != combinedDataIconId) { + mLastCombinedDataIconId = combinedDataIconId; + N = mDataIconViews.size(); + for (int i=0; i<N; i++) { + ImageView v = mDataIconViews.get(i); + v.setImageResource(combinedDataIconId); + } + } + + if (!mLastLabel.equals(label)) { + mLastLabel = label; + N = mLabelViews.size(); + for (int i=0; i<N; i++) { + TextView v = mLabelViews.get(i); + v.setText(label); + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java new file mode 100644 index 0000000..050a746 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import com.android.systemui.R; + +class TelephonyIcons { + //***** Signal strength icons + + //GSM/UMTS + static final int[][] TELEPHONY_SIGNAL_STRENGTH = { + { R.drawable.stat_sys_signal_0, + R.drawable.stat_sys_signal_1, + R.drawable.stat_sys_signal_2, + R.drawable.stat_sys_signal_3, + R.drawable.stat_sys_signal_4 }, + { R.drawable.stat_sys_signal_0_fully, + R.drawable.stat_sys_signal_1_fully, + R.drawable.stat_sys_signal_2_fully, + R.drawable.stat_sys_signal_3_fully, + R.drawable.stat_sys_signal_4_fully } + }; + + static final int[][] TELEPHONY_SIGNAL_STRENGTH_ROAMING = { + { R.drawable.stat_sys_r_signal_0, + R.drawable.stat_sys_r_signal_1, + R.drawable.stat_sys_r_signal_2, + R.drawable.stat_sys_r_signal_3, + R.drawable.stat_sys_r_signal_4 }, + { R.drawable.stat_sys_r_signal_0_fully, + R.drawable.stat_sys_r_signal_1_fully, + R.drawable.stat_sys_r_signal_2_fully, + R.drawable.stat_sys_r_signal_3_fully, + R.drawable.stat_sys_r_signal_4_fully } + }; + + //***** Data connection icons + + //GSM/UMTS + static final int[][] DATA_G = { + { R.drawable.stat_sys_data_connected_g, + R.drawable.stat_sys_data_in_g, + R.drawable.stat_sys_data_out_g, + R.drawable.stat_sys_data_inandout_g }, + { R.drawable.stat_sys_data_fully_connected_g, + R.drawable.stat_sys_data_fully_in_g, + R.drawable.stat_sys_data_fully_out_g, + R.drawable.stat_sys_data_fully_inandout_g } + }; + + static final int[][] DATA_3G = { + { R.drawable.stat_sys_data_connected_3g, + R.drawable.stat_sys_data_in_3g, + R.drawable.stat_sys_data_out_3g, + R.drawable.stat_sys_data_inandout_3g }, + { R.drawable.stat_sys_data_fully_connected_3g, + R.drawable.stat_sys_data_fully_in_3g, + R.drawable.stat_sys_data_fully_out_3g, + R.drawable.stat_sys_data_fully_inandout_3g } + }; + + static final int[][] DATA_E = { + { R.drawable.stat_sys_data_connected_e, + R.drawable.stat_sys_data_in_e, + R.drawable.stat_sys_data_out_e, + R.drawable.stat_sys_data_inandout_e }, + { R.drawable.stat_sys_data_fully_connected_e, + R.drawable.stat_sys_data_fully_in_e, + R.drawable.stat_sys_data_fully_out_e, + R.drawable.stat_sys_data_fully_inandout_e } + }; + + //3.5G + static final int[][] DATA_H = { + { R.drawable.stat_sys_data_connected_h, + R.drawable.stat_sys_data_in_h, + R.drawable.stat_sys_data_out_h, + R.drawable.stat_sys_data_inandout_h }, + { R.drawable.stat_sys_data_fully_connected_h, + R.drawable.stat_sys_data_fully_in_h, + R.drawable.stat_sys_data_fully_out_h, + R.drawable.stat_sys_data_fully_inandout_h } + }; + + //CDMA + // Use 3G icons for EVDO data and 1x icons for 1XRTT data + static final int[][] DATA_1X = { + { R.drawable.stat_sys_data_connected_1x, + R.drawable.stat_sys_data_in_1x, + R.drawable.stat_sys_data_out_1x, + R.drawable.stat_sys_data_inandout_1x }, + { R.drawable.stat_sys_data_fully_connected_1x, + R.drawable.stat_sys_data_fully_in_1x, + R.drawable.stat_sys_data_fully_out_1x, + R.drawable.stat_sys_data_fully_inandout_1x } + }; + +} + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java new file mode 100644 index 0000000..0787289 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import com.android.systemui.R; + +class WifiIcons { + static final int[][] WIFI_SIGNAL_STRENGTH = { + { R.drawable.stat_sys_wifi_signal_1, + R.drawable.stat_sys_wifi_signal_2, + R.drawable.stat_sys_wifi_signal_3, + R.drawable.stat_sys_wifi_signal_4 }, + { R.drawable.stat_sys_wifi_signal_1_fully, + R.drawable.stat_sys_wifi_signal_2_fully, + R.drawable.stat_sys_wifi_signal_3_fully, + R.drawable.stat_sys_wifi_signal_4_fully } + }; + + static final int WIFI_LEVEL_COUNT = WIFI_SIGNAL_STRENGTH[0].length; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java index eee0a16..ce81fdc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java @@ -17,10 +17,25 @@ package com.android.systemui.statusbar.tablet; import android.content.Context; -import android.widget.LinearLayout; import android.util.AttributeSet; +import android.util.Slog; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.view.View; +import android.widget.FrameLayout; -public class NotificationPanel extends LinearLayout implements StatusBarPanel { +import com.android.systemui.R; + +public class NotificationPanel extends RelativeLayout implements StatusBarPanel, + View.OnClickListener { + static final String TAG = "NotificationPanel"; + + View mSettingsButton; + View mNotificationButton; + View mNotificationScroller; + FrameLayout mSettingsFrame; + View mSettingsPanel; public NotificationPanel(Context context, AttributeSet attrs) { this(context, attrs, 0); @@ -30,6 +45,51 @@ public class NotificationPanel extends LinearLayout implements StatusBarPanel { super(context, attrs, defStyle); } + @Override + public void onFinishInflate() { + super.onFinishInflate(); + + mSettingsButton = (ImageView)findViewById(R.id.settings_button); + mSettingsButton.setOnClickListener(this); + mNotificationButton = (ImageView)findViewById(R.id.notification_button); + mNotificationButton.setOnClickListener(this); + + mNotificationScroller = findViewById(R.id.notificationScroller); + mSettingsFrame = (FrameLayout)findViewById(R.id.settings_frame); + } + + @Override + public void onVisibilityChanged(View v, int vis) { + super.onVisibilityChanged(v, vis); + // when we hide, put back the notifications + if (!isShown()) { + switchToNotificationMode(); + } + } + + public void onClick(View v) { + if (v == mSettingsButton) { + switchToSettingsMode(); + } else if (v == mNotificationButton) { + switchToNotificationMode(); + } + } + + public void switchToSettingsMode() { + removeSettingsPanel(); + addSettingsPanel(); + mSettingsButton.setVisibility(View.INVISIBLE); + mNotificationScroller.setVisibility(View.GONE); + mNotificationButton.setVisibility(View.VISIBLE); + } + + public void switchToNotificationMode() { + removeSettingsPanel(); + mSettingsButton.setVisibility(View.VISIBLE); + mNotificationScroller.setVisibility(View.VISIBLE); + mNotificationButton.setVisibility(View.INVISIBLE); + } + public boolean isInContentArea(int x, int y) { final int l = getPaddingLeft(); final int r = getWidth() - getPaddingRight(); @@ -37,5 +97,16 @@ public class NotificationPanel extends LinearLayout implements StatusBarPanel { final int b = getHeight() - getPaddingBottom(); return x >= l && x < r && y >= t && y < b; } + + void removeSettingsPanel() { + if (mSettingsPanel != null) { + mSettingsFrame.removeViewAt(0); + mSettingsPanel = null; + } + } + + void addSettingsPanel() { + mSettingsPanel = View.inflate(getContext(), R.layout.sysbar_panel_settings, mSettingsFrame); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPeekPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPeekPanel.java new file mode 100644 index 0000000..744f667 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPeekPanel.java @@ -0,0 +1,43 @@ +/* + * 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 com.android.systemui.statusbar.tablet; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.RelativeLayout; + +import com.android.systemui.R; + +public class NotificationPeekPanel extends RelativeLayout implements StatusBarPanel { + public NotificationPeekPanel(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public NotificationPeekPanel(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public boolean isInContentArea(int x, int y) { + final int l = getPaddingLeft(); + final int r = getWidth() - getPaddingRight(); + final int t = getPaddingTop(); + final int b = getHeight() - getPaddingBottom(); + return x >= l && x < r && y >= t && y < b; + } + +} + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java index 1831eda..e0b05f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java @@ -20,6 +20,8 @@ import java.util.ArrayList; import java.util.List; import android.app.ActivityManager; +import android.app.ActivityManagerNative; +import android.app.IActivityManager; import android.app.IThumbnailReceiver; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; @@ -43,11 +45,11 @@ import com.android.systemui.R; public class RecentAppsPanel extends LinearLayout implements StatusBarPanel, OnClickListener { private static final String TAG = "RecentAppsPanel"; - private static final boolean DEBUG = TabletStatusBarService.DEBUG; + private static final boolean DEBUG = TabletStatusBar.DEBUG; private static final int DISPLAY_TASKS = 4; // number of recent tasks to display private static final int MAX_TASKS = 2 * DISPLAY_TASKS; // give some slack for non-apps private static final boolean DBG = true; - private TabletStatusBarService mBar; + private TabletStatusBar mBar; private TextView mNoRecents; private LinearLayout mRecentsContainer; private ArrayList<ActivityDescription> mActivityDescriptions; @@ -98,7 +100,7 @@ public class RecentAppsPanel extends LinearLayout implements StatusBarPanel, OnC return x >= l && x < r && y >= t && y < b; } - public void setBar(TabletStatusBarService bar) { + public void setBar(TabletStatusBar bar) { mBar = bar; } @@ -236,7 +238,7 @@ public class RecentAppsPanel extends LinearLayout implements StatusBarPanel, OnC appIcon.setImageDrawable(activityDescription.icon); appDescription.setText(activityDescription.label); view.setOnClickListener(this); - view.setTag(activityDescription.intent); + view.setTag(activityDescription); Log.v(TAG, "Adding task: " + activityDescription.label); mRecentsContainer.addView(view); } @@ -247,9 +249,19 @@ public class RecentAppsPanel extends LinearLayout implements StatusBarPanel, OnC } public void onClick(View v) { - Intent intent = (Intent) v.getTag(); - if (DEBUG) Log.v(TAG, "Starting activity " + intent); - getContext().startActivity(intent); + ActivityDescription ad = (ActivityDescription)v.getTag(); + if (ad.id >= 0) { + // This is an active task; it should just go to the foreground. + IActivityManager am = ActivityManagerNative.getDefault(); + try { + am.moveTaskToFront(ad.id); + } catch (RemoteException e) { + } + } else { + Intent intent = ad.intent; + if (DEBUG) Log.v(TAG, "Starting activity " + intent); + getContext().startActivity(intent); + } mBar.animateCollapse(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsPanel.java new file mode 100644 index 0000000..9013f5c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsPanel.java @@ -0,0 +1,38 @@ +/* + * 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 com.android.systemui.statusbar.tablet; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Slog; +import android.widget.LinearLayout; +import android.view.View; + +import com.android.systemui.R; + +public class SettingsPanel extends LinearLayout { + static final String TAG = "SettingsPanel"; + + public SettingsPanel(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SettingsPanel(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } +} + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java index 09c8cd2..0cb9249 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java @@ -206,7 +206,7 @@ public class ShirtPocket extends FrameLayout { // lp.x = pos[1]; // lp.y = 0; lp.setTitle("ShirtPocket"); - lp.windowAnimations = com.android.internal.R.style.Animation_SlidingCard; + lp.windowAnimations = R.style.Animation_ShirtPocketPanel; WindowManagerImpl.getDefault().addView(mWindow, lp); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java index c9a8d56..41b44c2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java @@ -16,60 +16,31 @@ package com.android.systemui.statusbar.tablet; -import android.app.Notification; -import android.app.PendingIntent; -import android.app.Service; -import android.app.StatusBarManager; import android.bluetooth.BluetoothAdapter; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.Resources; -import android.graphics.PixelFormat; -import android.graphics.Rect; import android.location.LocationManager; import android.media.AudioManager; -import android.net.NetworkInfo; -import android.net.wifi.SupplicantState; -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.os.AsyncTask; -import android.os.Handler; -import android.os.IBinder; +import android.telephony.PhoneStateListener; +import android.telephony.ServiceState; import android.os.IPowerManager; -import android.os.Message; -import android.os.RemoteException; import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; -import android.telephony.PhoneStateListener; -import android.telephony.ServiceState; -import android.telephony.SignalStrength; -import android.telephony.TelephonyManager; import android.util.AttributeSet; import android.util.Slog; -import android.view.Gravity; -import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.view.IWindowManager; import android.view.WindowManager; import android.view.WindowManagerImpl; -import android.widget.Button; import android.widget.ImageButton; -import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.RemoteViews; -import android.widget.ScrollView; -import android.widget.TextSwitcher; import android.widget.TextView; import android.widget.Toast; -import java.util.List; - import com.android.internal.telephony.IccCard; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.cdma.EriInfo; @@ -82,14 +53,13 @@ import com.android.systemui.R; public class SystemPanel extends LinearLayout implements StatusBarPanel { private static final String TAG = "SystemPanel"; - private static final boolean DEBUG = TabletStatusBarService.DEBUG; - private static final boolean DEBUG_SIGNAL = false; + private static final boolean DEBUG = TabletStatusBar.DEBUG; private static final int MINIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_DIM + 5; private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON; private static final int DEFAULT_BACKLIGHT = (int) (android.os.Power.BRIGHTNESS_ON * 0.4f); - private TabletStatusBarService mBar; + private TabletStatusBar mBar; private boolean mAirplaneMode; private ImageButton mBrightnessButton; @@ -99,34 +69,11 @@ public class SystemPanel extends LinearLayout implements StatusBarPanel { private ImageButton mGpsButton; private ImageButton mBluetoothButton; - private ImageView mBatteryMeter; - private ImageView mSignalMeter; - - private TextView mBatteryText; - private TextView mSignalText; - private final IWindowManager mWM; private final AudioManager mAudioManager; - private final WifiManager mWifiManager; - private final TelephonyManager mPhone; private final BluetoothAdapter mBluetoothAdapter; - // state trackers for telephony code - IccCard.State mSimState = IccCard.State.READY; - int mPhoneState = TelephonyManager.CALL_STATE_IDLE; - int mDataState = TelephonyManager.DATA_DISCONNECTED; - ServiceState mServiceState; - SignalStrength mSignalStrength; - - // state for the meters - boolean mWifiEnabled, mWifiConnected; - int mWifiLevel; - String mWifiSsid; - - boolean mDataEnabled, mDataConnected, mDataRoaming; - int mDataLevel; - public boolean isInContentArea(int x, int y) { final int l = getPaddingLeft(); final int r = getWidth() - getPaddingRight(); @@ -141,260 +88,16 @@ public class SystemPanel extends LinearLayout implements StatusBarPanel { final String action = intent.getAction(); if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) { refreshSound(); - } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { - updateBattery(intent); - } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION) - || action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION) - || action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION) - || action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { - updateWifiState(intent); - } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) { - updateSimState(intent); } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { refreshBluetooth(); } } }; - private final void updateSimState(Intent intent) { - String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE); - if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { - mSimState = IccCard.State.ABSENT; - } - else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) { - mSimState = IccCard.State.READY; - } - else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) { - final String lockedReason = intent.getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON); - if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) { - mSimState = IccCard.State.PIN_REQUIRED; - } - else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) { - mSimState = IccCard.State.PUK_REQUIRED; - } - else { - mSimState = IccCard.State.NETWORK_LOCKED; - } - } else { - mSimState = IccCard.State.UNKNOWN; - } - updateDataState(); - } - - private boolean isCdma() { - return (mSignalStrength != null) && !mSignalStrength.isGsm(); - } - - private boolean isEvdo() { - return ( (mServiceState != null) - && ((mServiceState.getRadioTechnology() - == ServiceState.RADIO_TECHNOLOGY_EVDO_0) - || (mServiceState.getRadioTechnology() - == ServiceState.RADIO_TECHNOLOGY_EVDO_A) - || (mServiceState.getRadioTechnology() - == ServiceState.RADIO_TECHNOLOGY_EVDO_B))); - } - - private boolean hasService() { - if (mServiceState != null) { - switch (mServiceState.getState()) { - case ServiceState.STATE_OUT_OF_SERVICE: - case ServiceState.STATE_POWER_OFF: - return false; - default: - return true; - } - } else { - return false; - } - } - - private int getCdmaLevel() { - if (mSignalStrength == null) return 0; - final int cdmaDbm = mSignalStrength.getCdmaDbm(); - final int cdmaEcio = mSignalStrength.getCdmaEcio(); - int levelDbm = 0; - int levelEcio = 0; - - if (cdmaDbm >= -75) levelDbm = 4; - else if (cdmaDbm >= -85) levelDbm = 3; - else if (cdmaDbm >= -95) levelDbm = 2; - else if (cdmaDbm >= -100) levelDbm = 1; - else levelDbm = 0; - - // Ec/Io are in dB*10 - if (cdmaEcio >= -90) levelEcio = 4; - else if (cdmaEcio >= -110) levelEcio = 3; - else if (cdmaEcio >= -130) levelEcio = 2; - else if (cdmaEcio >= -150) levelEcio = 1; - else levelEcio = 0; - - return (levelDbm < levelEcio) ? levelDbm : levelEcio; - } - - private int getEvdoLevel() { - if (mSignalStrength == null) return 0; - int evdoDbm = mSignalStrength.getEvdoDbm(); - int evdoSnr = mSignalStrength.getEvdoSnr(); - int levelEvdoDbm = 0; - int levelEvdoSnr = 0; - - if (evdoDbm >= -65) levelEvdoDbm = 4; - else if (evdoDbm >= -75) levelEvdoDbm = 3; - else if (evdoDbm >= -90) levelEvdoDbm = 2; - else if (evdoDbm >= -105) levelEvdoDbm = 1; - else levelEvdoDbm = 0; - - if (evdoSnr >= 7) levelEvdoSnr = 4; - else if (evdoSnr >= 5) levelEvdoSnr = 3; - else if (evdoSnr >= 3) levelEvdoSnr = 2; - else if (evdoSnr >= 1) levelEvdoSnr = 1; - else levelEvdoSnr = 0; - - return (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr; - } - - private void updateDataState() { - mDataConnected = hasService() && (mDataState == TelephonyManager.DATA_CONNECTED); - - if (isCdma()) { - // these functions return a value from 0 to 4, inclusive - if ((mPhoneState == TelephonyManager.CALL_STATE_IDLE) && isEvdo()){ - mDataLevel = getEvdoLevel() * 25; - } else { - mDataLevel = getCdmaLevel() * 25; - } - } else { - // GSM - - int asu = (mSignalStrength == null) ? 0 : mSignalStrength.getGsmSignalStrength(); - - // asu on [0,31]; 99 = unknown - // Android has historically shown anything >=12 as "full" - // XXX: tune this based on Industry Best Practices(TM) - if (asu <= 2 || asu == 99) mDataLevel = 0; - else mDataLevel = (int)(((float)Math.max(asu, 15) / 15) * 100); - - mDataRoaming = mPhone.isNetworkRoaming(); - - mDataConnected = mDataConnected - && (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN); - } - - if (DEBUG_SIGNAL || DEBUG) { - Slog.d(TAG, "updateDataState: connected=" + mDataConnected - + " level=" + mDataLevel - + " isEvdo=" + isEvdo() - + " isCdma=" + isCdma() - + " mPhoneState=" + mPhoneState - + " mDataState=" + mDataState - ); - } - - refreshSignalMeters(); - } - - private void updateWifiState(Intent intent) { - if (DEBUG) - Slog.d(TAG, "updateWifiState: " + intent); - - final String action = intent.getAction(); - final boolean wasConnected = mWifiConnected; - - if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { - mWifiEnabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, - WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; - } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { - final NetworkInfo networkInfo = (NetworkInfo) - intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); - mWifiConnected = networkInfo != null && networkInfo.isConnected(); - } else if (action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)) { - final NetworkInfo.DetailedState detailedState = WifiInfo.getDetailedStateOf( - (SupplicantState)intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)); - mWifiConnected = detailedState == NetworkInfo.DetailedState.CONNECTED; - } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) { - final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200); - int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 101); - mWifiLevel = mWifiConnected ? newSignalLevel : 0; - } - - if (mWifiConnected && !wasConnected) { - WifiInfo info = mWifiManager.getConnectionInfo(); - if (DEBUG) - Slog.d(TAG, "updateWifiState: just connected: info=" + info); - - if (info != null) { - // grab the initial signal strength - mWifiLevel = WifiManager.calculateSignalLevel(info.getRssi(), 101); - - // find the SSID - mWifiSsid = info.getSSID(); - if (mWifiSsid == null) { - // OK, it's not in the connectionInfo; we have to go hunting for it - List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks(); - for (WifiConfiguration net : networks) { - if (net.networkId == info.getNetworkId()) { - mWifiSsid = net.SSID; - break; - } - } - } - } - } - - refreshSignalMeters(); - } - - // figure out what to show: first wifi, then 3G, then nothing - void refreshSignalMeters() { - if (mSignalMeter == null) return; // no UI yet - - Context ctxt = getContext(); - - String text = null; - int level = 0; - - if (mWifiConnected) { - if (mWifiSsid == null) { - text = ctxt.getString(R.string.system_panel_signal_meter_wifi_nossid); - } else { - text = ctxt.getString(R.string.system_panel_signal_meter_wifi_ssid_format, - mWifiSsid); - } - level = mWifiLevel; - } else if (mDataConnected) { - text = ctxt.getString(R.string.system_panel_signal_meter_data_connected); - level = mDataLevel; - } else { - text = ctxt.getString(R.string.system_panel_signal_meter_disconnected); - level = 0; - } - - mSignalMeter.setImageResource(R.drawable.sysbar_signal); - mSignalMeter.setImageLevel(level); - mSignalText.setText(text); - - // hack for now - mBar.setSignalMeter(level, mWifiConnected); - } - - public void setBar(TabletStatusBarService bar) { + public void setBar(TabletStatusBar bar) { mBar = bar; } - public void updateBattery(Intent intent) { - final int level = intent.getIntExtra("level", 0); - final boolean plugged = intent.getIntExtra("plugged", 0) != 0; - - mBatteryMeter.setImageResource(R.drawable.sysbar_battery); - mBatteryMeter.setImageLevel(level); - mBatteryText.setText(getContext() - .getString(R.string.system_panel_battery_meter_format, level)); - - // hack for now - mBar.setBatteryMeter(level, plugged); - } - public SystemPanel(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -406,34 +109,14 @@ public class SystemPanel extends LinearLayout implements StatusBarPanel { mWM = IWindowManager.Stub.asInterface( ServiceManager.getService("window")); - - // get notified of phone state changes - TelephonyManager telephonyManager = - (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); - telephonyManager.listen(mPhoneStateListener, - PhoneStateListener.LISTEN_SERVICE_STATE - | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS - | PhoneStateListener.LISTEN_CALL_STATE - | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE - | PhoneStateListener.LISTEN_DATA_ACTIVITY); - - // wifi status info - mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); - // audio status mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); - // mobile data - mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); - // Bluetooth mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); } public void onAttachedToWindow() { - DateView date = (DateView)findViewById(R.id.date); - date.setUpdates(true); - TextView settingsButton = (TextView)findViewById(R.id.settings_button); settingsButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { @@ -508,26 +191,9 @@ public class SystemPanel extends LinearLayout implements StatusBarPanel { // register for broadcasts IntentFilter filter = new IntentFilter(); filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); - filter.addAction(Intent.ACTION_BATTERY_CHANGED); - filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); - filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); - filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); - filter.addAction(WifiManager.RSSI_CHANGED_ACTION); - filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); getContext().registerReceiver(mReceiver, filter); - mBatteryMeter = (ImageView)findViewById(R.id.battery_meter); - mBatteryMeter.setImageResource(R.drawable.sysbar_battery); - mBatteryMeter.setImageLevel(0); - mSignalMeter = (ImageView)findViewById(R.id.signal_meter); - mBatteryMeter.setImageResource(R.drawable.sysbar_signal); - mBatteryMeter.setImageLevel(0); - - mBatteryText = (TextView)findViewById(R.id.battery_info); - mSignalText = (TextView)findViewById(R.id.signal_info); - - refreshSignalMeters(); refreshBluetooth(); refreshGps(); } @@ -615,10 +281,9 @@ public class SystemPanel extends LinearLayout implements StatusBarPanel { PhoneStateListener mPhoneStateListener = new PhoneStateListener() { @Override public void onServiceStateChanged(ServiceState serviceState) { - if (DEBUG_SIGNAL || DEBUG) { + if (DEBUG) { Slog.d(TAG, "phone service state changed: " + serviceState.getState()); } - mServiceState = serviceState; mAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF; if (mAirplaneButton != null) { mAirplaneButton.setImageResource(mAirplaneMode @@ -628,34 +293,6 @@ public class SystemPanel extends LinearLayout implements StatusBarPanel { ? R.drawable.sysbar_toggle_bg_on : R.drawable.sysbar_toggle_bg_off); } - updateDataState(); - } - @Override - public void onSignalStrengthsChanged(SignalStrength signalStrength) { - if (DEBUG_SIGNAL || DEBUG) { - Slog.d(TAG, "onSignalStrengthsChanged: " + signalStrength); - } - mSignalStrength = signalStrength; - updateDataState(); - } - @Override - public void onCallStateChanged(int state, String incomingNumber) { - mPhoneState = state; - // In cdma, if a voice call is made, RSSI should switch to 1x. - if (isCdma()) { - updateDataState(); - } - } - - @Override - public void onDataConnectionStateChanged(int state, int networkType) { - if (DEBUG_SIGNAL || DEBUG) { - Slog.d(TAG, "onDataConnectionStateChanged: state=" + state - + " type=" + networkType); - } - mDataState = state; -// updateDataNetType(networkType); - updateDataState(); } }; @@ -745,7 +382,7 @@ public class SystemPanel extends LinearLayout implements StatusBarPanel { } else { mWM.freezeRotation(); } - } catch (android.os.RemoteException exc) { + } catch (RemoteException exc) { } } @@ -759,7 +396,7 @@ public class SystemPanel extends LinearLayout implements StatusBarPanel { int getDisplayRotation() { try { return mWM.getRotation(); - } catch (android.os.RemoteException exc) { + } catch (RemoteException exc) { return 0; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java index da44f43..983215e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java @@ -59,20 +59,20 @@ import android.widget.TextView; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarNotification; +import com.android.systemui.R; import com.android.systemui.statusbar.*; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.recent.RecentApplicationsActivity; -import com.android.systemui.R; -public class TabletStatusBarService extends StatusBarService { +public class TabletStatusBar extends StatusBar { public static final boolean DEBUG = false; - public static final String TAG = "TabletStatusBarService"; + public static final String TAG = "TabletStatusBar"; public static final int MSG_OPEN_NOTIFICATION_PANEL = 1000; public static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001; public static final int MSG_OPEN_NOTIFICATION_PEEK = 1002; public static final int MSG_CLOSE_NOTIFICATION_PEEK = 1003; - public static final int MSG_OPEN_SYSTEM_PANEL = 1010; - public static final int MSG_CLOSE_SYSTEM_PANEL = 1011; public static final int MSG_OPEN_RECENTS_PANEL = 1020; public static final int MSG_CLOSE_RECENTS_PANEL = 1021; public static final int MSG_LIGHTS_ON = 1030; @@ -89,10 +89,9 @@ public class TabletStatusBarService extends StatusBarService { private NotificationData mNotns = new NotificationData(); TabletStatusBarView mStatusBarView; - ImageView mNotificationTrigger; + View mNotificationArea; + View mNotificationTrigger; NotificationIconArea mNotificationIconArea; - View mNotificationButtons; - View mSystemInfo; View mNavigationArea; View mMenuButton; View mRecentButton; @@ -100,8 +99,7 @@ public class TabletStatusBarService extends StatusBarService { InputMethodButton mInputMethodButton; NotificationPanel mNotificationPanel; - SystemPanel mSystemPanel; - NotificationPanel mNotificationPeekWindow; + NotificationPeekPanel mNotificationPeekWindow; ViewGroup mNotificationPeekRow; int mNotificationPeekIndex; LayoutTransition mNotificationPeekScrubLeft, mNotificationPeekScrubRight; @@ -110,12 +108,9 @@ public class TabletStatusBarService extends StatusBarService { int mNotificationFlingVelocity; ViewGroup mPile; - TextView mClearButton; - TextView mDoNotDisturbButton; - ImageView mBatteryMeter; - ImageView mSignalMeter; - ImageView mSignalIcon; + BatteryController mBatteryController; + NetworkController mNetworkController; View mBarContents; View mCurtains; @@ -146,6 +141,15 @@ public class TabletStatusBarService extends StatusBarService { mNotificationPanel.setOnTouchListener( new TouchOutsideListener(MSG_CLOSE_NOTIFICATION_PANEL, mNotificationPanel)); + // the battery and network icons + mBatteryController.addIconView((ImageView)mNotificationPanel.findViewById(R.id.battery)); + mBatteryController.addLabelView( + (TextView)mNotificationPanel.findViewById(R.id.battery_text)); + mNetworkController.addCombinedDataIconView( + (ImageView)mNotificationPanel.findViewById(R.id.network)); + mNetworkController.addLabelView( + (TextView)mNotificationPanel.findViewById(R.id.network_text)); + mStatusBarView.setIgnoreChildren(0, mNotificationTrigger, mNotificationPanel); WindowManager.LayoutParams lp = new WindowManager.LayoutParams( @@ -158,12 +162,11 @@ public class TabletStatusBarService extends StatusBarService { PixelFormat.TRANSLUCENT); lp.gravity = Gravity.BOTTOM | Gravity.RIGHT; lp.setTitle("NotificationPanel"); - lp.windowAnimations = com.android.internal.R.style.Animation_SlidingCard; WindowManagerImpl.getDefault().addView(mNotificationPanel, lp); // Notification preview window - mNotificationPeekWindow = (NotificationPanel) View.inflate(context, + mNotificationPeekWindow = (NotificationPeekPanel) View.inflate(context, R.layout.sysbar_panel_notification_peek, null); mNotificationPeekRow = (ViewGroup) mNotificationPeekWindow.findViewById(R.id.content); mNotificationPeekWindow.setVisibility(View.GONE); @@ -198,29 +201,6 @@ public class TabletStatusBarService extends StatusBarService { WindowManagerImpl.getDefault().addView(mNotificationPeekWindow, lp); - // System Panel - mSystemPanel = (SystemPanel) View.inflate(context, R.layout.sysbar_panel_system, null); - mSystemPanel.setVisibility(View.GONE); - mSystemPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_SYSTEM_PANEL, - mSystemPanel)); - mStatusBarView.setIgnoreChildren(1, mSystemInfo, mSystemPanel); - - lp = new WindowManager.LayoutParams( - 800, - ViewGroup.LayoutParams.WRAP_CONTENT, - WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, - WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, - PixelFormat.TRANSLUCENT); - lp.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; - lp.setTitle("SystemPanel"); - lp.windowAnimations = com.android.internal.R.style.Animation_SlidingCard; - - WindowManagerImpl.getDefault().addView(mSystemPanel, lp); - mSystemPanel.setBar(this); - - // Recents Panel if (USE_2D_RECENTS) { mRecentsPanel = (RecentAppsPanel) View.inflate(context, R.layout.sysbar_panel_recent, @@ -240,7 +220,7 @@ public class TabletStatusBarService extends StatusBarService { PixelFormat.TRANSLUCENT); lp.gravity = Gravity.BOTTOM | Gravity.LEFT; lp.setTitle("RecentsPanel"); - lp.windowAnimations = com.android.internal.R.style.Animation_SlidingCard; + lp.windowAnimations = R.style.Animation_RecentPanel; WindowManagerImpl.getDefault().addView(mRecentsPanel, lp); mRecentsPanel.setBar(this); @@ -266,10 +246,6 @@ public class TabletStatusBarService extends StatusBarService { mBarContents = sb.findViewById(R.id.bar_contents); mCurtains = sb.findViewById(R.id.lights_out); - mSystemInfo = sb.findViewById(R.id.systemInfo); - mRecentButton = sb.findViewById(R.id.recent_apps); - - mSystemInfo.setOnTouchListener(new ClockTouchListener()); mRecentButton = sb.findViewById(R.id.recent_apps); mRecentButton.setOnClickListener(mOnClickListener); @@ -278,20 +254,16 @@ public class TabletStatusBarService extends StatusBarService { mCurtains.setOnClickListener(on); mCurtains.setOnLongClickListener(on); + // the whole right-hand side of the bar + mNotificationArea = sb.findViewById(R.id.notificationArea); + // the button to open the notification area - mNotificationTrigger = (ImageView) sb.findViewById(R.id.notificationTrigger); + mNotificationTrigger = sb.findViewById(R.id.notificationTrigger); mNotificationTrigger.setOnClickListener(mOnClickListener); // the more notifications icon mNotificationIconArea = (NotificationIconArea)sb.findViewById(R.id.notificationIcons); - // the clear and dnd buttons - mNotificationButtons = sb.findViewById(R.id.notificationButtons); - mClearButton = (TextView)mNotificationButtons.findViewById(R.id.clear_all_button); - mClearButton.setOnClickListener(mOnClickListener); - mDoNotDisturbButton = (TextView)mNotificationButtons.findViewById(R.id.do_not_disturb); - mDoNotDisturbButton.setOnClickListener(mOnClickListener); - // where the icons go mIconLayout = (NotificationIconArea.IconLayout) sb.findViewById(R.id.icons); mIconLayout.setOnTouchListener(new NotificationIconTouchListener()); @@ -302,10 +274,11 @@ public class TabletStatusBarService extends StatusBarService { mTicker = new TabletTicker(context, (FrameLayout)sb.findViewById(R.id.ticker)); - // System info (center) - mBatteryMeter = (ImageView) sb.findViewById(R.id.battery); - mSignalMeter = (ImageView) sb.findViewById(R.id.signal); - mSignalIcon = (ImageView) sb.findViewById(R.id.signal_icon); + // The icons + mBatteryController = new BatteryController(mContext); + mBatteryController.addIconView((ImageView)sb.findViewById(R.id.battery)); + mNetworkController = new NetworkController(mContext); + mNetworkController.addCombinedDataIconView((ImageView)sb.findViewById(R.id.network)); // The navigation buttons mNavigationArea = sb.findViewById(R.id.navigationArea); @@ -388,36 +361,17 @@ public class TabletStatusBarService extends StatusBarService { if (mNotificationPanel.getVisibility() == View.GONE) { mNotificationPeekWindow.setVisibility(View.GONE); - mDoNotDisturbButton.setText(mNotificationsOn - ? R.string.status_bar_do_not_disturb_button - : R.string.status_bar_please_disturb_button); mNotificationPanel.setVisibility(View.VISIBLE); - setViewVisibility(mNotificationIconArea, View.GONE, - R.anim.notification_icons_out); - setViewVisibility(mNotificationButtons, View.VISIBLE, - R.anim.notification_buttons_in); - refreshNotificationTrigger(); + mNotificationArea.setVisibility(View.GONE); } break; case MSG_CLOSE_NOTIFICATION_PANEL: if (DEBUG) Slog.d(TAG, "closing notifications panel"); if (mNotificationPanel.getVisibility() == View.VISIBLE) { mNotificationPanel.setVisibility(View.GONE); - setViewVisibility(mNotificationIconArea, View.VISIBLE, - R.anim.notification_icons_in); - setViewVisibility(mNotificationButtons, View.GONE, - R.anim.notification_buttons_out); - refreshNotificationTrigger(); + mNotificationArea.setVisibility(View.VISIBLE); } break; - case MSG_OPEN_SYSTEM_PANEL: - if (DEBUG) Slog.d(TAG, "opening system panel"); - mSystemPanel.setVisibility(View.VISIBLE); - break; - case MSG_CLOSE_SYSTEM_PANEL: - if (DEBUG) Slog.d(TAG, "closing system panel"); - mSystemPanel.setVisibility(View.GONE); - break; case MSG_OPEN_RECENTS_PANEL: if (DEBUG) Slog.d(TAG, "opening recents panel"); if (mRecentsPanel != null) mRecentsPanel.setVisibility(View.VISIBLE); @@ -440,6 +394,7 @@ public class TabletStatusBarService extends StatusBarService { } public void refreshNotificationTrigger() { + /* if (mNotificationTrigger == null) return; int resId; @@ -452,29 +407,8 @@ public class TabletStatusBarService extends StatusBarService { } else { resId = panel ? R.drawable.ic_sysbar_noti_none_open : R.drawable.ic_sysbar_noti_none; } - mNotificationTrigger.setImageResource(resId); - } - - public void setBatteryMeter(int level, boolean plugged) { - if (DEBUG) Slog.d(TAG, "battery=" + level + (plugged ? " - plugged" : " - unplugged")); - mBatteryMeter.setImageResource(R.drawable.sysbar_batterymini); - // adjust percent to permyriad for ClipDrawable's sake - mBatteryMeter.setImageLevel(level * (MAX_IMAGE_LEVEL / 100)); - } - - public void setSignalMeter(int level, boolean isWifi) { - if (DEBUG) Slog.d(TAG, "signal=" + level); - if (level < 0) { - mSignalMeter.setImageDrawable(null); - mSignalMeter.setImageLevel(0); - mSignalIcon.setImageDrawable(null); - } else { - mSignalMeter.setImageResource(R.drawable.sysbar_wifimini); - // adjust to permyriad - mSignalMeter.setImageLevel(level * (MAX_IMAGE_LEVEL / 100)); - mSignalIcon.setImageResource(isWifi ? R.drawable.ic_sysbar_wifi_mini - : R.drawable.ic_sysbar_wifi_mini); // XXX - } + //mNotificationTrigger.setImageResource(resId); + */ } public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) { @@ -599,9 +533,6 @@ public class TabletStatusBarService extends StatusBarService { public void disable(int state) { int old = mDisabled; int diff = state ^ old; - Slog.d(TAG, "disable... old=0x" + Integer.toHexString(old) - + " diff=0x" + Integer.toHexString(diff) - + " state=0x" + Integer.toHexString(state)); mDisabled = state; // act accordingly @@ -614,39 +545,24 @@ public class TabletStatusBarService extends StatusBarService { if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes"); - setViewVisibility(mNotificationTrigger, View.GONE, - R.anim.notification_icons_out); - setViewVisibility(mNotificationIconArea, View.GONE, - R.anim.notification_icons_out); + mNotificationIconArea.setVisibility(View.GONE); mTicker.halt(); } else { Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no"); - setViewVisibility(mNotificationTrigger, View.VISIBLE, - R.anim.notification_icons_in); - setViewVisibility(mNotificationIconArea, View.VISIBLE, - R.anim.notification_icons_in); + mNotificationIconArea.setVisibility(View.VISIBLE); } } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { if ((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { mTicker.halt(); } } - if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) { - if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) { - Slog.d(TAG, "DISABLE_SYSTEM_INFO: yes"); - setViewVisibility(mSystemInfo, View.GONE, R.anim.navigation_out); - } else { - Slog.d(TAG, "DISABLE_SYSTEM_INFO: no"); - setViewVisibility(mSystemInfo, View.VISIBLE, R.anim.navigation_in); - } - } if ((diff & StatusBarManager.DISABLE_NAVIGATION) != 0) { if ((state & StatusBarManager.DISABLE_NAVIGATION) != 0) { Slog.d(TAG, "DISABLE_NAVIGATION: yes"); - setViewVisibility(mNavigationArea, View.GONE, R.anim.navigation_out); + mNavigationArea.setVisibility(View.GONE); } else { Slog.d(TAG, "DISABLE_NAVIGATION: no"); - setViewVisibility(mNavigationArea, View.VISIBLE, R.anim.navigation_in); + mNavigationArea.setVisibility(View.VISIBLE); } } } @@ -682,13 +598,11 @@ public class TabletStatusBarService extends StatusBarService { public void animateCollapse() { mHandler.removeMessages(MSG_CLOSE_NOTIFICATION_PANEL); mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PANEL); - mHandler.removeMessages(MSG_CLOSE_SYSTEM_PANEL); - mHandler.sendEmptyMessage(MSG_CLOSE_SYSTEM_PANEL); mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL); mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL); } - // called by StatusBarService + // called by StatusBar @Override public void setLightsOn(boolean on) { mHandler.removeMessages(MSG_LIGHTS_OUT); @@ -700,9 +614,7 @@ public class TabletStatusBarService extends StatusBarService { if (DEBUG) { Slog.d(TAG, (visible?"showing":"hiding") + " the MENU button"); } - setViewVisibility(mMenuButton, - visible ? View.VISIBLE : View.INVISIBLE, - visible ? R.anim.navigation_in : R.anim.navigation_out); + mMenuButton.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); } public void setIMEButtonVisible(boolean visible) { @@ -717,24 +629,6 @@ public class TabletStatusBarService extends StatusBarService { //Slog.d(TAG, "setAreThereNotifications hasClerable=" + hasClearable); - // Show or hide the "Clear all" button. Note that we don't do an animation - // if it's not on screen, so that if someone opens the bar right then they - // don't see the animation in progress. - // (no ongoing notifications are clearable) - if (hasClearable) { - if (mNotificationButtons.getVisibility() == View.VISIBLE) { - setViewVisibility(mClearButton, View.VISIBLE, R.anim.notification_buttons_in); - } else { - mClearButton.setVisibility(View.VISIBLE); - } - } else { - if (mNotificationButtons.getVisibility() == View.VISIBLE) { - setViewVisibility(mClearButton, View.GONE, R.anim.notification_buttons_out); - } else { - mClearButton.setVisibility(View.GONE); - } - } - /* mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE); mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE); @@ -759,55 +653,9 @@ public class TabletStatusBarService extends StatusBarService { } } - private class ClockTouchListener implements View.OnTouchListener { - VelocityTracker mVT = null; - int mInitX, mInitY; - public boolean onTouch (View v, MotionEvent event) { - final int x = (int) event.getX(); - final int y = (int) event.getY(); - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - mVT = VelocityTracker.obtain(); - mInitX = x; - mInitY = y; - mHandler.sendEmptyMessageDelayed(MSG_LIGHTS_OUT, - ViewConfiguration.getLongPressTimeout()); - break; - case MotionEvent.ACTION_OUTSIDE: - case MotionEvent.ACTION_MOVE: - final Rect r = new Rect(); - final float radius = mSystemInfo.getHeight() / 2; - if (Math.abs(x - mInitX) > radius || Math.abs(y - mInitY) > radius) { - mHandler.removeMessages(MSG_LIGHTS_OUT); - } - if (mVT == null) break; - mVT.addMovement(event); - mVT.computeCurrentVelocity(1000); - if (mVT.getYVelocity() < -200 && mSystemPanel.getVisibility() == View.GONE) { - mHandler.removeMessages(MSG_OPEN_SYSTEM_PANEL); - mHandler.sendEmptyMessage(MSG_OPEN_SYSTEM_PANEL); - } else if (mVT.getYVelocity() > 200) { - mHandler.sendEmptyMessage(MSG_LIGHTS_OUT); - } - return true; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mVT.recycle(); - mVT = null; - mHandler.removeMessages(MSG_LIGHTS_OUT); - return true; - } - return false; - } - } - private View.OnClickListener mOnClickListener = new View.OnClickListener() { public void onClick(View v) { - if (v == mClearButton) { - onClickClearButton(); - } else if (v == mDoNotDisturbButton) { - onClickDoNotDisturb(); - } else if (v == mNotificationTrigger) { + if (v == mNotificationTrigger) { onClickNotificationTrigger(); } else if (v == mRecentButton) { onClickRecentButton(); @@ -815,23 +663,6 @@ public class TabletStatusBarService extends StatusBarService { } }; - void onClickClearButton() { - try { - mBarService.onClearAllNotifications(); - } catch (RemoteException ex) { - // system process is dead if we're here. - } - animateCollapse(); - refreshNotificationTrigger(); - } - - void onClickDoNotDisturb() { - mNotificationsOn = !mNotificationsOn; - mIconLayout.setVisibility(mNotificationsOn ? View.VISIBLE : View.INVISIBLE); // TODO: animation - animateCollapse(); - refreshNotificationTrigger(); - } - public void onClickNotificationTrigger() { if (DEBUG) Slog.d(TAG, "clicked notification icons"); if ((mDisabled & StatusBarManager.DISABLE_EXPAND) == 0) { @@ -849,17 +680,6 @@ public class TabletStatusBarService extends StatusBarService { } } - public void onClickSystemInfo() { - if (DEBUG) Slog.d(TAG, "clicked system info"); - if ((mDisabled & StatusBarManager.DISABLE_EXPAND) == 0) { - int msg = (mSystemPanel.getVisibility() == View.GONE) - ? MSG_OPEN_SYSTEM_PANEL - : MSG_CLOSE_SYSTEM_PANEL; - mHandler.removeMessages(msg); - mHandler.sendEmptyMessage(msg); - } - } - public void onClickRecentButton() { if (DEBUG) Slog.d(TAG, "clicked recent apps"); if (mRecentsPanel == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java index 15866fe..bd8266a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java @@ -40,12 +40,10 @@ public class TabletStatusBarView extends FrameLayout { public boolean onInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mHandler.removeMessages(TabletStatusBarService.MSG_CLOSE_NOTIFICATION_PANEL); - mHandler.sendEmptyMessage(TabletStatusBarService.MSG_CLOSE_NOTIFICATION_PANEL); - mHandler.removeMessages(TabletStatusBarService.MSG_CLOSE_SYSTEM_PANEL); - mHandler.sendEmptyMessage(TabletStatusBarService.MSG_CLOSE_SYSTEM_PANEL); - mHandler.removeMessages(TabletStatusBarService.MSG_CLOSE_RECENTS_PANEL); - mHandler.sendEmptyMessage(TabletStatusBarService.MSG_CLOSE_RECENTS_PANEL); + mHandler.removeMessages(TabletStatusBar.MSG_CLOSE_NOTIFICATION_PANEL); + mHandler.sendEmptyMessage(TabletStatusBar.MSG_CLOSE_NOTIFICATION_PANEL); + mHandler.removeMessages(TabletStatusBar.MSG_CLOSE_RECENTS_PANEL); + mHandler.sendEmptyMessage(TabletStatusBar.MSG_CLOSE_RECENTS_PANEL); for (int i=0; i < mPanels.length; i++) { if (mPanels[i] != null && mPanels[i].getVisibility() == View.VISIBLE) { diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 71bf956..18815f5 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -37,9 +37,7 @@ import com.android.internal.widget.ActionBarContextView; import com.android.internal.widget.ActionBarView; import android.app.KeyguardManager; -import android.content.ActivityNotFoundException; import android.content.Context; -import android.content.Intent; import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.Canvas; @@ -51,7 +49,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; -import android.os.SystemClock; import android.util.AndroidRuntimeException; import android.util.Config; import android.util.EventLog; @@ -60,6 +57,7 @@ import android.util.SparseArray; import android.util.TypedValue; import android.view.ActionMode; import android.view.Gravity; +import android.view.HardwareRenderer; import android.view.InputQueue; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -72,14 +70,12 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewManager; import android.view.ViewStub; -import android.view.VolumePanel; import android.view.Window; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.animation.Animation; import android.view.animation.AnimationUtils; -import android.view.inputmethod.InputMethodManager; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.PopupWindow; @@ -166,7 +162,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private MenuDialogHelper mContextMenuHelper; private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE; - private long mVolumeKeyUpTime; private AudioManager mAudioManager; private KeyguardManager mKeyguardManager; @@ -416,6 +411,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { public final void openPanel(int featureId, KeyEvent event) { if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null && mActionBar.isOverflowReserved()) { + // Invalidate the options menu, we want a prepare event that the app can respond to. + invalidatePanelMenu(FEATURE_OPTIONS_PANEL); mActionBar.showOverflowMenu(); } else { openPanel(getPanelState(featureId, true), event); @@ -1835,7 +1832,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback); - ActionMode mode = getCallback().onStartActionMode(wrappedCallback); + ActionMode mode = null; + try { + mode = getCallback().onWindowStartingActionMode(wrappedCallback); + } catch (AbstractMethodError ame) { + // Older apps might not implement this callback method. + } if (mode != null) { mActionMode = mode; } else { @@ -1879,6 +1881,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } } + if (mActionMode != null) { + try { + getCallback().onActionModeStarted(mActionMode); + } catch (AbstractMethodError ame) { + // Older apps might not implement this callback method. + } + } return mActionMode; } @@ -2094,6 +2103,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mActionModeView != null) { mActionModeView.removeAllViews(); } + try { + getCallback().onActionModeFinished(mActionMode); + } catch (AbstractMethodError ame) { + // Older apps might not implement this callback method. + } mActionMode = null; } } @@ -2234,12 +2248,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR); // System.out.println("Title Icons!"); - } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0) { + } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0 + && (features & (1 << FEATURE_ACTION_BAR)) == 0) { // Special case for a window with only a progress bar (and title). // XXX Need to have a no-title version of embedded windows. layoutResource = com.android.internal.R.layout.screen_progress; - // XXX Remove this once action bar supports these features. - removeFeature(FEATURE_ACTION_BAR); // System.out.println("Progress!"); } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) { // Special case for a window with a custom title. @@ -2352,6 +2365,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mActionBar.getTitle() == null) { mActionBar.setWindowTitle(mTitle); } + final int localFeatures = getLocalFeatures(); + if ((localFeatures & (1 << FEATURE_PROGRESS)) != 0) { + mActionBar.initProgress(); + } + if ((localFeatures & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) { + mActionBar.initIndeterminateProgress(); + } // Post the panel invalidate for later; avoid application onCreateOptionsMenu // being called in the middle of onCreate or similar. mDecor.post(new Runnable() { @@ -2543,8 +2563,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mContentParent == null && shouldInstallDecor) { installDecor(); } - mCircularProgressBar = (ProgressBar)findViewById(com.android.internal.R.id.progress_circular); - mCircularProgressBar.setVisibility(View.INVISIBLE); + mCircularProgressBar = (ProgressBar) findViewById(com.android.internal.R.id.progress_circular); + if (mCircularProgressBar != null) { + mCircularProgressBar.setVisibility(View.INVISIBLE); + } return mCircularProgressBar; } @@ -2555,8 +2577,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mContentParent == null && shouldInstallDecor) { installDecor(); } - mHorizontalProgressBar = (ProgressBar)findViewById(com.android.internal.R.id.progress_horizontal); - mHorizontalProgressBar.setVisibility(View.INVISIBLE); + mHorizontalProgressBar = (ProgressBar) findViewById(com.android.internal.R.id.progress_horizontal); + if (mHorizontalProgressBar != null) { + mHorizontalProgressBar.setVisibility(View.INVISIBLE); + } return mHorizontalProgressBar; } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 4bc7433..8c857da 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -117,7 +117,6 @@ import android.view.animation.AnimationUtils; import android.media.IAudioService; import android.media.AudioManager; -import java.io.File; import java.util.ArrayList; /** @@ -962,7 +961,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ public View addStartingWindow(IBinder appToken, String packageName, int theme, CharSequence nonLocalizedLabel, - int labelRes, int icon) { + int labelRes, int icon, int windowFlags) { if (!SHOW_STARTING_ANIMATIONS) { return null; } @@ -1006,9 +1005,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { // flag because we do know that the next window will take input // focus, so we want to get the IME window up on top of us right away. win.setFlags( + windowFlags| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, + windowFlags| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); @@ -2304,12 +2305,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { return getCurrentPortraitRotation(lastRotation); } - if (new File("/system/etc/allow_all_orientations").exists()) { - mOrientationListener.setAllow180Rotation(true); - } else { - mOrientationListener.setAllow180Rotation( - orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); - } + mOrientationListener.setAllow180Rotation( + orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); // case for nosensor meaning ignore sensor and consider only lid // or orientation sensor disabled diff --git a/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java index 9608b9a..d9e8c2b 100644 --- a/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java +++ b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java @@ -17,7 +17,9 @@ package com.android.internal.policy.impl; import android.app.ActivityManager; +import android.app.ActivityManagerNative; import android.app.Dialog; +import android.app.IActivityManager; import android.app.StatusBarManager; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; @@ -30,6 +32,8 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.Handler; +import android.os.RemoteException; import android.util.Log; import android.view.View; import android.view.Window; @@ -53,6 +57,22 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener View mNoAppsText; IntentFilter mBroadcastIntentFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + class RecentTag { + ActivityManager.RecentTaskInfo info; + Intent intent; + } + + Handler mHandler = new Handler(); + Runnable mCleanup = new Runnable() { + public void run() { + // dump extra memory we're hanging on to + for (TextView icon: mIcons) { + icon.setCompoundDrawables(null, null, null, null); + icon.setTag(null); + } + } + }; + private int mIconSize; public RecentApplicationsDialog(Context context) { @@ -115,12 +135,18 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener for (TextView b: mIcons) { if (b == v) { - // prepare a launch intent and send it - Intent intent = (Intent)b.getTag(); - if (intent != null) { - intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY); + RecentTag tag = (RecentTag)b.getTag(); + if (tag.info.id >= 0) { + // This is an active task; it should just go to the foreground. + IActivityManager am = ActivityManagerNative.getDefault(); try { - getContext().startActivity(intent); + am.moveTaskToFront(tag.info.id); + } catch (RemoteException e) { + } + } else if (tag.intent != null) { + tag.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY); + try { + getContext().startActivity(tag.intent); } catch (ActivityNotFoundException e) { Log.w("Recent", "Unable to launch recent task", e); } @@ -144,6 +170,8 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener // receive broadcasts getContext().registerReceiver(mBroadcastReceiver, mBroadcastIntentFilter); + + mHandler.removeCallbacks(mCleanup); } /** @@ -153,18 +181,14 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener public void onStop() { super.onStop(); - // dump extra memory we're hanging on to - for (TextView icon: mIcons) { - icon.setCompoundDrawables(null, null, null, null); - icon.setTag(null); - } - if (sStatusBar != null) { sStatusBar.disable(StatusBarManager.DISABLE_NONE); } // stop receiving broadcasts getContext().unregisterReceiver(mBroadcastReceiver); + + mHandler.postDelayed(mCleanup, 100); } /** @@ -224,7 +248,10 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener tv.setText(title); icon = iconUtilities.createIconDrawable(icon); tv.setCompoundDrawables(null, icon, null, null); - tv.setTag(intent); + RecentTag tag = new RecentTag(); + tag.info = info; + tag.intent = intent; + tv.setTag(tag); tv.setVisibility(View.VISIBLE); tv.setPressed(false); tv.clearFocus(); diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 90ebf3f..6a86076 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -585,6 +585,7 @@ class BackupManagerService extends IBackupManager.Stub { } } } + tf.close(); } catch (FileNotFoundException fnf) { // Probably innocuous Slog.v(TAG, "No ancestral data"); diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 1df4405..4f8862c 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -961,7 +961,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // enabled. String id = Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); - if (id != null && id.length() > 0) { + // There is no input method selected, try to choose new applicable input method. + if (TextUtils.isEmpty(id) && chooseNewDefaultIMELocked()) { + id = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD); + } + if (!TextUtils.isEmpty(id)) { try { setInputMethodLocked(id, getSelectedInputMethodSubtypeId(id)); } catch (IllegalArgumentException e) { @@ -983,8 +988,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (id.equals(mCurMethodId)) { - if (subtypeId != NOT_A_SUBTYPE_ID) { - InputMethodSubtype subtype = info.getSubtypes().get(subtypeId); + ArrayList<InputMethodSubtype> subtypes = info.getSubtypes(); + if (subtypeId >= 0 && subtypeId < subtypes.size()) { + InputMethodSubtype subtype = subtypes.get(subtypeId); if (subtype != mCurrentSubtype) { synchronized (mMethodMap) { if (mCurMethod != null) { @@ -1497,6 +1503,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } InputMethodInfo imi = enabled.get(i); + if (DEBUG) { + Slog.d(TAG, "New default IME was selected: " + imi.getId()); + } resetSelectedInputMethodAndSubtypeLocked(imi.getId()); return true; } @@ -1800,9 +1809,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // Disabled input method is currently selected, switch to another one. String selId = Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); - if (id.equals(selId)) { - resetSelectedInputMethodAndSubtypeLocked(enabledInputMethodsList.size() > 0 - ? enabledInputMethodsList.get(0).first : ""); + if (id.equals(selId) && !chooseNewDefaultIMELocked()) { + Slog.i(TAG, "Can't find new IME, unsetting the current input method."); + resetSelectedInputMethodAndSubtypeLocked(""); } // Previous state was enabled. return true; @@ -1926,7 +1935,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // The first subtype applicable to the system locale will be defined as the most applicable // subtype. if (DEBUG) { - Slog.d(TAG, "Applicable InputMethodSubtype was found: " + applicableSubtypeId + Slog.d(TAG, "Applicable InputMethodSubtype was found: " + applicableSubtypeId + "," + subtypes.get(applicableSubtypeId).getLocale()); } return applicableSubtypeId; @@ -1956,6 +1965,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return mCurrentSubtype; } + public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) { + synchronized (mMethodMap) { + if (subtype != null && mCurMethodId != null) { + InputMethodInfo imi = mMethodMap.get(mCurMethodId); + int subtypeId = getSubtypeIdFromHashCode(imi, subtype.hashCode()); + if (subtypeId != NOT_A_SUBTYPE_ID) { + setInputMethodLocked(mCurMethodId, subtypeId); + return true; + } + } + return false; + } + } + /** * Utility class for putting and getting settings for InputMethod * TODO: Move all putters and getters of settings to this class. diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 4707bbf..a0a1974 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -7327,7 +7327,7 @@ class PackageManagerService extends IPackageManager.Stub { pw.println(" "); pw.println("Package warning messages:"); File fname = getSettingsProblemFile(); - FileInputStream in; + FileInputStream in = null; try { in = new FileInputStream(fname); int avail = in.available(); @@ -7336,6 +7336,13 @@ class PackageManagerService extends IPackageManager.Stub { pw.print(new String(data)); } catch (FileNotFoundException e) { } catch (IOException e) { + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } } } } diff --git a/services/java/com/android/server/ProcessStats.java b/services/java/com/android/server/ProcessStats.java index 43dbcc0..1a12a84 100644 --- a/services/java/com/android/server/ProcessStats.java +++ b/services/java/com/android/server/ProcessStats.java @@ -799,8 +799,9 @@ public class ProcessStats { } private String readFile(String file, char endChar) { + FileInputStream is = null; try { - FileInputStream is = new FileInputStream(file); + is = new FileInputStream(file); int len = is.read(mBuffer); is.close(); @@ -815,6 +816,13 @@ public class ProcessStats { } } catch (java.io.FileNotFoundException e) { } catch (java.io.IOException e) { + } finally { + if (is != null) { + try { + is.close(); + } catch (java.io.IOException e) { + } + } } return null; } @@ -841,4 +849,3 @@ public class ProcessStats { } } } - diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index fb87d69..5038770 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -3729,7 +3729,7 @@ public class WindowManagerService extends IWindowManager.Stub public void setAppStartingWindow(IBinder token, String pkg, int theme, CharSequence nonLocalizedLabel, int labelRes, int icon, - IBinder transferFrom, boolean createIfNeeded) { + int windowFlags, IBinder transferFrom, boolean createIfNeeded) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppStartingIcon()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); @@ -3877,7 +3877,7 @@ public class WindowManagerService extends IWindowManager.Stub mStartingIconInTransition = true; wtoken.startingData = new StartingData( pkg, theme, nonLocalizedLabel, - labelRes, icon); + labelRes, icon, windowFlags); Message m = mH.obtainMessage(H.ADD_STARTING, wtoken); // Note: we really want to do sendMessageAtFrontOfQueue() because we // want to process the message ASAP, before any other queued @@ -8440,14 +8440,16 @@ public class WindowManagerService extends IWindowManager.Stub final CharSequence nonLocalizedLabel; final int labelRes; final int icon; + final int windowFlags; StartingData(String _pkg, int _theme, CharSequence _nonLocalizedLabel, - int _labelRes, int _icon) { + int _labelRes, int _icon, int _windowFlags) { pkg = _pkg; theme = _theme; nonLocalizedLabel = _nonLocalizedLabel; labelRes = _labelRes; icon = _icon; + windowFlags = _windowFlags; } } @@ -8568,7 +8570,7 @@ public class WindowManagerService extends IWindowManager.Stub view = mPolicy.addStartingWindow( wtoken.token, sd.pkg, sd.theme, sd.nonLocalizedLabel, sd.labelRes, - sd.icon); + sd.icon, sd.windowFlags); } catch (Exception e) { Slog.w(TAG, "Exception when adding starting window", e); } diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 47be6a2..c2f8d67 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -36,6 +36,7 @@ import android.util.Log; import android.util.Slog; import android.util.TimeUtils; import android.view.IApplicationToken; +import android.view.WindowManager; import java.io.PrintWriter; import java.lang.ref.WeakReference; @@ -68,6 +69,7 @@ class ActivityRecord extends IApplicationToken.Stub { int labelRes; // the label information from the package mgr. int icon; // resource identifier of activity's icon. int theme; // resource identifier of activity's theme. + int windowFlags; // custom window flags for preview window. TaskRecord task; // the task this is in. long launchTime; // when we starting launching this activity long startTime; // last time this activity was started @@ -244,6 +246,9 @@ class ActivityRecord extends IApplicationToken.Stub { } icon = aInfo.getIconResource(); theme = aInfo.getThemeResource(); + if ((aInfo.flags&ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) { + windowFlags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; + } if ((aInfo.flags&ActivityInfo.FLAG_MULTIPROCESS) != 0 && _caller != null && (aInfo.applicationInfo.uid == Process.SYSTEM_UID diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index 463493b..51dc84e 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -1301,7 +1301,8 @@ public class ActivityStack { mService.mWindowManager.setAppStartingWindow( next, next.packageName, next.theme, next.nonLocalizedLabel, - next.labelRes, next.icon, null, true); + next.labelRes, next.icon, next.windowFlags, + null, true); } } startSpecificActivityLocked(next, true, false); @@ -1336,7 +1337,8 @@ public class ActivityStack { mService.mWindowManager.setAppStartingWindow( next, next.packageName, next.theme, next.nonLocalizedLabel, - next.labelRes, next.icon, null, true); + next.labelRes, next.icon, next.windowFlags, + null, true); } if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next); } @@ -1460,7 +1462,7 @@ public class ActivityStack { } mService.mWindowManager.setAppStartingWindow( r, r.packageName, r.theme, r.nonLocalizedLabel, - r.labelRes, r.icon, prev, showStartingIcon); + r.labelRes, r.icon, r.windowFlags, prev, showStartingIcon); } } else { // If this is the first activity, don't do any fancy animations, diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index b5e73ac..22a45df 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -464,6 +464,7 @@ void SensorService::SensorEventConnection::onFirstRef() } bool SensorService::SensorEventConnection::addSensor(int32_t handle) { + Mutex::Autolock _l(mConnectionLock); if (mSensorInfo.indexOfKey(handle) <= 0) { SensorInfo info; mSensorInfo.add(handle, info); @@ -473,6 +474,7 @@ bool SensorService::SensorEventConnection::addSensor(int32_t handle) { } bool SensorService::SensorEventConnection::removeSensor(int32_t handle) { + Mutex::Autolock _l(mConnectionLock); if (mSensorInfo.removeItem(handle) >= 0) { return true; } @@ -480,16 +482,19 @@ bool SensorService::SensorEventConnection::removeSensor(int32_t handle) { } bool SensorService::SensorEventConnection::hasSensor(int32_t handle) const { + Mutex::Autolock _l(mConnectionLock); return mSensorInfo.indexOfKey(handle) >= 0; } bool SensorService::SensorEventConnection::hasAnySensor() const { + Mutex::Autolock _l(mConnectionLock); return mSensorInfo.size() ? true : false; } status_t SensorService::SensorEventConnection::setEventRateLocked( int handle, nsecs_t ns) { + Mutex::Autolock _l(mConnectionLock); ssize_t index = mSensorInfo.indexOfKey(handle); if (index >= 0) { SensorInfo& info = mSensorInfo.editValueFor(handle); @@ -506,6 +511,7 @@ status_t SensorService::SensorEventConnection::sendEvents( // filter out events not for this connection size_t count = 0; if (scratch) { + Mutex::Autolock _l(mConnectionLock); size_t i=0; while (i<numEvents) { const int32_t curr = buffer[i].sensor; diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index b442779..c0922f5 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -75,8 +75,9 @@ class SensorService : sp<SensorService> const mService; sp<SensorChannel> const mChannel; + mutable Mutex mConnectionLock; - // protected by SensorService::mLock + // protected mConnectionLock struct SensorInfo { SensorInfo() : ns(DEFAULT_EVENTS_PERIOD) { } nsecs_t ns; diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java index 4464236..710ff30 100644 --- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java @@ -164,7 +164,7 @@ public class WindowManagerPermissionTests extends TestCase { } try { - mWm.setAppStartingWindow(null, "foo", 0, null, 0, 0, null, false); + mWm.setAppStartingWindow(null, "foo", 0, null, 0, 0, 0, null, false); fail("IWindowManager.setAppStartingWindow did not throw SecurityException as" + " expected"); } catch (SecurityException e) { |
