diff options
Diffstat (limited to 'core')
364 files changed, 16109 insertions, 6553 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 4dc4b6a..8236943 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -53,6 +53,7 @@ import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.ViewManager; import android.view.Window; import android.view.WindowManager; import android.view.ContextMenu.ContextMenuInfo; @@ -93,11 +94,11 @@ import java.util.HashMap; * {@link android.R.styleable#AndroidManifestActivity <activity>} * declaration in their package's <code>AndroidManifest.xml</code>.</p> * - * <p>The Activity class is an important part of an - * <a href="{@docRoot}intro/lifecycle.html">application's overall lifecycle</a>, + * <p>The Activity class is an important part of an application's overall lifecycle, * and the way activities are launched and put together is a fundamental - * part of the platform's - * <a href="{@docRoot}intro/appmodel.html">application model</a>.</p> + * part of the platform's application model. For a detailed perspective on the structure of + * Android applications and lifecycles, please read the <em>Dev Guide</em> document on + * <a href="{@docRoot}guide/topics/fundamentals.html">Application Fundamentals</a>.</p> * * <p>Topics covered here: * <ol> @@ -527,7 +528,7 @@ import java.util.HashMap; * {@link android.R.styleable#AndroidManifestUsesPermission <uses-permission>} * element in their own manifest to be able to start that activity. * - * <p>See the <a href="{@docRoot}devel/security.html">Security Model</a> + * <p>See the <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> * document for more information on permissions and security in general. * * <a name="ProcessLifecycle"></a> @@ -629,6 +630,9 @@ public class Activity extends ContextThemeWrapper private WindowManager mWindowManager; /*package*/ View mDecor = null; + /*package*/ boolean mWindowAdded = false; + /*package*/ boolean mVisibleFromServer = false; + /*package*/ boolean mVisibleFromClient = true; private CharSequence mTitle; private int mTitleColor = 0; @@ -779,6 +783,8 @@ public class Activity extends ContextThemeWrapper * @see #onPostCreate */ protected void onCreate(Bundle savedInstanceState) { + mVisibleFromClient = mWindow.getWindowStyle().getBoolean( + com.android.internal.R.styleable.Window_windowNoDisplay, true); mCalled = true; } @@ -1134,12 +1140,19 @@ public class Activity extends ContextThemeWrapper /** * Called as part of the activity lifecycle when an activity is about to go * into the background as the result of user choice. For example, when the - * user presses the Home key, {@link #onUserLeaving} will be called, but + * user presses the Home key, {@link #onUserLeaveHint} will be called, but * when an incoming phone call causes the in-call Activity to be automatically - * brought to the foreground, {@link #onUserLeaving} will not be called on - * the activity being interrupted. + * brought to the foreground, {@link #onUserLeaveHint} will not be called on + * the activity being interrupted. In cases when it is invoked, this method + * is called right before the activity's {@link #onPause} callback. + * + * <p>This callback and {@link #onUserInteraction} are intended to help + * activities manage status bar notifications intelligently; specifically, + * for helping activities determine the proper time to cancel a notfication. + * + * @see #onUserInteraction() */ - protected void onUserLeaving() { + protected void onUserLeaveHint() { } /** @@ -1443,7 +1456,6 @@ public class Activity extends ContextThemeWrapper * @return The Cursor that was returned by query(). * * @see ContentResolver#query(android.net.Uri , String[], String, String[], String) - * @see #managedCommitUpdates * @see #startManagingCursor * @hide */ @@ -1475,7 +1487,6 @@ public class Activity extends ContextThemeWrapper * @return The Cursor that was returned by query(). * * @see ContentResolver#query(android.net.Uri , String[], String, String[], String) - * @see #managedCommitUpdates * @see #startManagingCursor */ public final Cursor managedQuery(Uri uri, @@ -1863,6 +1874,28 @@ public class Activity extends ContextThemeWrapper return false; } + /** + * Called whenever a key, touch, or trackball event is dispatched to the + * activity. Implement this method if you wish to know that the user has + * interacted with the device in some way while your activity is running. + * This callback and {@link #onUserLeaveHint} are intended to help + * activities manage status bar notifications intelligently; specifically, + * for helping activities determine the proper time to cancel a notfication. + * + * <p>All calls to your activity's {@link #onUserLeaveHint} callback will + * be accompanied by calls to {@link #onUserInteraction}. This + * ensures that your activity will be told of relevant user activity such + * as pulling down the notification pane and touching an item there. + * + * <p>Note that this callback will be invoked for the touch down action + * that begins a touch gesture, but may not be invoked for the touch-moved + * and touch-up actions that follow. + * + * @see #onUserLeaveHint() + */ + public void onUserInteraction() { + } + public void onWindowAttributesChanged(WindowManager.LayoutParams params) { // Update window manager if: we have a view, that view is // attached to its parent (which will be a RootView), and @@ -1935,6 +1968,7 @@ public class Activity extends ContextThemeWrapper * @return boolean Return true if this event was consumed. */ public boolean dispatchKeyEvent(KeyEvent event) { + onUserInteraction(); if (getWindow().superDispatchKeyEvent(event)) { return true; } @@ -1952,6 +1986,9 @@ public class Activity extends ContextThemeWrapper * @return boolean Return true if this event was consumed. */ public boolean dispatchTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + onUserInteraction(); + } if (getWindow().superDispatchTouchEvent(ev)) { return true; } @@ -1969,6 +2006,7 @@ public class Activity extends ContextThemeWrapper * @return boolean Return true if this event was consumed. */ public boolean dispatchTrackballEvent(MotionEvent ev) { + onUserInteraction(); if (getWindow().superDispatchTrackballEvent(ev)) { return true; } @@ -2865,6 +2903,35 @@ public class Activity extends ContextThemeWrapper } /** + * Control whether this activity's main window is visible. This is intended + * only for the special case of an activity that is not going to show a + * UI itself, but can't just finish prior to onResume() because it needs + * to wait for a service binding or such. Setting this to false allows + * you to prevent your UI from being shown during that time. + * + * <p>The default value for this is taken from the + * {@link android.R.attr#windowNoDisplay} attribute of the activity's theme. + */ + public void setVisible(boolean visible) { + if (mVisibleFromClient != visible) { + mVisibleFromClient = visible; + if (mVisibleFromServer) { + if (visible) makeVisible(); + else mDecor.setVisibility(View.INVISIBLE); + } + } + } + + void makeVisible() { + if (!mWindowAdded) { + ViewManager wm = getWindowManager(); + wm.addView(mDecor, getWindow().getAttributes()); + mWindowAdded = true; + } + mDecor.setVisibility(View.VISIBLE); + } + + /** * Check to see whether this activity is in the process of finishing, * either because you called {@link #finish} on it or someone else * has requested that it finished. This is often used in @@ -3482,7 +3549,8 @@ public class Activity extends ContextThemeWrapper } final void performUserLeaving() { - onUserLeaving(); + onUserInteraction(); + onUserLeaveHint(); } final void performStop() { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index f9b9221..07520c9d 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -19,16 +19,14 @@ package android.app; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; import android.graphics.Bitmap; import android.os.RemoteException; import android.os.Handler; -import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; -import android.os.Parcelable.Creator; import android.text.TextUtils; -import android.util.Log; import java.util.List; /** @@ -617,7 +615,59 @@ public class ActivityManager { public String pkgList[]; + /** + * Constant for {@link #importance}: this process is running the + * foreground UI. + */ + public static final int IMPORTANCE_FOREGROUND = 100; + + /** + * Constant for {@link #importance}: this process is running something + * that is considered to be actively visible to the user. + */ + public static final int IMPORTANCE_VISIBLE = 200; + + /** + * Constant for {@link #importance}: this process is contains services + * that should remain running. + */ + public static final int IMPORTANCE_SERVICE = 300; + + /** + * Constant for {@link #importance}: this process process contains + * background code that is expendable. + */ + public static final int IMPORTANCE_BACKGROUND = 400; + + /** + * Constant for {@link #importance}: this process is empty of any + * actively running code. + */ + public static final int IMPORTANCE_EMPTY = 500; + + /** + * The relative importance level that the system places on this + * process. May be one of {@link #IMPORTANCE_FOREGROUND}, + * {@link #IMPORTANCE_VISIBLE}, {@link #IMPORTANCE_SERVICE}, + * {@link #IMPORTANCE_BACKGROUND}, or {@link #IMPORTANCE_EMPTY}. These + * constants are numbered so that "more important" values are always + * smaller than "less important" values. + */ + public int importance; + + /** + * An additional ordering within a particular {@link #importance} + * category, providing finer-grained information about the relative + * utility of processes within a category. This number means nothing + * except that a smaller values are more recently used (and thus + * more important). Currently an LRU value is only maintained for + * the {@link #IMPORTANCE_BACKGROUND} category, though others may + * be maintained in the future. + */ + public int lru; + public RunningAppProcessInfo() { + importance = IMPORTANCE_FOREGROUND; } public RunningAppProcessInfo(String pProcessName, int pPid, String pArr[]) { @@ -634,12 +684,16 @@ public class ActivityManager { dest.writeString(processName); dest.writeInt(pid); dest.writeStringArray(pkgList); + dest.writeInt(importance); + dest.writeInt(lru); } public void readFromParcel(Parcel source) { processName = source.readString(); pid = source.readInt(); pkgList = source.readStringArray(); + importance = source.readInt(); + lru = source.readInt(); } public static final Creator<RunningAppProcessInfo> CREATOR = @@ -671,4 +725,37 @@ public class ActivityManager { return null; } } + + /** + * Have the system perform a force stop of everything associated with + * the given application package. All processes that share its uid + * will be killed, all services it has running stopped, all activities + * removed, etc. In addition, a {@link Intent#ACTION_PACKAGE_RESTARTED} + * broadcast will be sent, so that any of its registered alarms can + * be stopped, notifications removed, etc. + * + * <p>You must hold the permission + * {@link android.Manifest.permission#RESTART_PACKAGES} to be able to + * call this method. + * + * @param packageName The name of the package to be stopped. + */ + public void restartPackage(String packageName) { + try { + ActivityManagerNative.getDefault().restartPackage(packageName); + } catch (RemoteException e) { + } + } + + /** + * Get the device configuration attributes. + */ + public ConfigurationInfo getDeviceConfigurationInfo() { + try { + return ActivityManagerNative.getDefault().getDeviceConfigurationInfo(); + } catch (RemoteException e) { + } + return null; + } + } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index ae9f3bf..f11dbec 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.content.ContentResolver; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; import android.content.res.Configuration; import android.graphics.Bitmap; @@ -83,6 +84,17 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM } /** + * Convenience for checking whether the system is ready. For internal use only. + */ + static public boolean isSystemReady() { + if (!sSystemReady) { + sSystemReady = getDefault().testIsSystemReady(); + } + return sSystemReady; + } + static boolean sSystemReady = false; + + /** * Convenience for sending a sticky broadcast. For internal use only. * If you don't care about permission, use null. */ @@ -959,6 +971,23 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case GET_DEVICE_CONFIGURATION_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + ConfigurationInfo config = getDeviceConfigurationInfo(); + reply.writeNoException(); + config.writeToParcel(reply, 0); + return true; + } + + case PEEK_SERVICE_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + Intent service = Intent.CREATOR.createFromParcel(data); + String resolvedType = data.readString(); + IBinder binder = peekService(service, resolvedType); + reply.writeNoException(); + reply.writeStrongBinder(binder); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -1604,6 +1633,20 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } + + public IBinder peekService(Intent service, String resolvedType) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + service.writeToParcel(data, 0); + data.writeString(resolvedType); + mRemote.transact(PEEK_SERVICE_TRANSACTION, data, reply, 0); + reply.readException(); + IBinder binder = reply.readStrongBinder(); + reply.recycle(); + data.recycle(); + return binder; + } public boolean startInstrumentation(ComponentName className, String profileFile, int flags, Bundle arguments, IInstrumentationWatcher watcher) @@ -2028,6 +2071,11 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } + public boolean testIsSystemReady() + { + /* this base class version is never called */ + return true; + } public int handleApplicationError(IBinder app, int flags, String tag, String shortMsg, String longMsg, byte[] crashData) throws RemoteException @@ -2071,5 +2119,17 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + public ConfigurationInfo getDeviceConfigurationInfo() throws RemoteException + { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(GET_DEVICE_CONFIGURATION_TRANSACTION, data, reply, 0); + reply.readException(); + ConfigurationInfo res = ConfigurationInfo.CREATOR.createFromParcel(reply); + reply.recycle(); + data.recycle(); + return res; + } private IBinder mRemote; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index e4c1057..bf5616e 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -144,13 +144,19 @@ public final class ActivityThread { return sPackageManager; } - DisplayMetrics getDisplayMetricsLocked() { + DisplayMetrics getDisplayMetricsLocked(boolean forceUpdate) { + if (mDisplayMetrics != null && !forceUpdate) { + return mDisplayMetrics; + } if (mDisplay == null) { WindowManager wm = WindowManagerImpl.getDefault(); mDisplay = wm.getDefaultDisplay(); } - DisplayMetrics metrics = new DisplayMetrics(); + DisplayMetrics metrics = mDisplayMetrics = new DisplayMetrics(); mDisplay.getMetrics(metrics); + //Log.i("foo", "New metrics: w=" + metrics.widthPixels + " h=" + // + metrics.heightPixels + " den=" + metrics.density + // + " xdpi=" + metrics.xdpi + " ydpi=" + metrics.ydpi); return metrics; } @@ -173,7 +179,7 @@ public final class ActivityThread { if (assets.addAssetPath(appDir) == 0) { return null; } - DisplayMetrics metrics = getDisplayMetricsLocked(); + DisplayMetrics metrics = getDisplayMetricsLocked(false); r = new Resources(assets, metrics, getConfiguration()); //Log.i(TAG, "Created app resources " + r + ": " + r.getConfiguration()); // XXX need to remove entries when weak references go away @@ -235,7 +241,7 @@ public final class ActivityThread { ApplicationContext.createSystemContext(mainThread); mSystemContext.getResources().updateConfiguration( mainThread.getConfiguration(), - mainThread.getDisplayMetricsLocked()); + mainThread.getDisplayMetricsLocked(false)); //Log.i(TAG, "Created system resources " // + mSystemContext.getResources() + ": " // + mSystemContext.getResources().getConfiguration()); @@ -1205,7 +1211,10 @@ public final class ActivityThread { private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s"; private static final String ONE_COUNT_COLUMN = "%17s %8d"; private static final String TWO_COUNT_COLUMNS = "%17s %8d %17s %8d"; - + + // Formatting for checkin service - update version if row format changes + private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 1; + public final void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving, int configChanges) { queueOrSendMessage( @@ -1462,7 +1471,101 @@ public final class ActivityThread { long dalvikMax = runtime.totalMemory() / 1024; long dalvikFree = runtime.freeMemory() / 1024; long dalvikAllocated = dalvikMax - dalvikFree; - + long viewInstanceCount = ViewDebug.getViewInstanceCount(); + long viewRootInstanceCount = ViewDebug.getViewRootInstanceCount(); + long appContextInstanceCount = ApplicationContext.getInstanceCount(); + long activityInstanceCount = Activity.getInstanceCount(); + int globalAssetCount = AssetManager.getGlobalAssetCount(); + int globalAssetManagerCount = AssetManager.getGlobalAssetManagerCount(); + int binderLocalObjectCount = Debug.getBinderLocalObjectCount(); + int binderProxyObjectCount = Debug.getBinderProxyObjectCount(); + int binderDeathObjectCount = Debug.getBinderDeathObjectCount(); + int openSslSocketCount = OpenSSLSocketImpl.getInstanceCount(); + long sqliteAllocated = SQLiteDebug.getHeapAllocatedSize() / 1024; + SQLiteDebug.PagerStats stats = new SQLiteDebug.PagerStats(); + SQLiteDebug.getPagerStats(stats); + + // Check to see if we were called by checkin server. If so, print terse format. + boolean doCheckinFormat = false; + if (args != null) { + for (String arg : args) { + if ("-c".equals(arg)) doCheckinFormat = true; + } + } + + // For checkin, we print one long comma-separated list of values + if (doCheckinFormat) { + // NOTE: if you change anything significant below, also consider changing + // ACTIVITY_THREAD_CHECKIN_VERSION. + String processName = (mBoundApplication != null) + ? mBoundApplication.processName : "unknown"; + + // Header + pw.print(ACTIVITY_THREAD_CHECKIN_VERSION); pw.print(','); + pw.print(Process.myPid()); pw.print(','); + pw.print(processName); pw.print(','); + + // Heap info - max + pw.print(nativeMax); pw.print(','); + pw.print(dalvikMax); pw.print(','); + pw.print("N/A,"); + pw.print(nativeMax + dalvikMax); pw.print(','); + + // Heap info - allocated + pw.print(nativeAllocated); pw.print(','); + pw.print(dalvikAllocated); pw.print(','); + pw.print("N/A,"); + pw.print(nativeAllocated + dalvikAllocated); pw.print(','); + + // Heap info - free + pw.print(nativeFree); pw.print(','); + pw.print(dalvikFree); pw.print(','); + pw.print("N/A,"); + pw.print(nativeFree + dalvikFree); pw.print(','); + + // Heap info - proportional set size + pw.print(memInfo.nativePss); pw.print(','); + pw.print(memInfo.dalvikPss); pw.print(','); + pw.print(memInfo.otherPss); pw.print(','); + pw.print(memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss); pw.print(','); + + // Heap info - shared + pw.print(nativeShared); pw.print(','); + pw.print(dalvikShared); pw.print(','); + pw.print(otherShared); pw.print(','); + pw.print(nativeShared + dalvikShared + otherShared); pw.print(','); + + // Heap info - private + pw.print(nativePrivate); pw.print(','); + pw.print(dalvikPrivate); pw.print(','); + pw.print(otherPrivate); pw.print(','); + pw.print(nativePrivate + dalvikPrivate + otherPrivate); pw.print(','); + + // Object counts + pw.print(viewInstanceCount); pw.print(','); + pw.print(viewRootInstanceCount); pw.print(','); + pw.print(appContextInstanceCount); pw.print(','); + pw.print(activityInstanceCount); pw.print(','); + + pw.print(globalAssetCount); pw.print(','); + pw.print(globalAssetManagerCount); pw.print(','); + pw.print(binderLocalObjectCount); pw.print(','); + pw.print(binderProxyObjectCount); pw.print(','); + + pw.print(binderDeathObjectCount); pw.print(','); + pw.print(openSslSocketCount); pw.print(','); + + // SQL + pw.print(sqliteAllocated); pw.print(','); + pw.print(stats.databaseBytes / 1024); pw.print(','); + pw.print(stats.numPagers); pw.print(','); + pw.print((stats.totalBytes - stats.referencedBytes) / 1024); pw.print(','); + pw.print(stats.referencedBytes / 1024); pw.print('\n'); + + return; + } + + // otherwise, show human-readable format printRow(pw, HEAP_COLUMN, "", "native", "dalvik", "other", "total"); printRow(pw, HEAP_COLUMN, "size:", nativeMax, dalvikMax, "N/A", nativeMax + dalvikMax); printRow(pw, HEAP_COLUMN, "allocated:", nativeAllocated, dalvikAllocated, "N/A", @@ -1480,26 +1583,22 @@ public final class ActivityThread { pw.println(" "); pw.println(" Objects"); - printRow(pw, TWO_COUNT_COLUMNS, "Views:", ViewDebug.getViewInstanceCount(), "ViewRoots:", - ViewDebug.getViewRootInstanceCount()); - - printRow(pw, TWO_COUNT_COLUMNS, "AppContexts:", ApplicationContext.getInstanceCount(), - "Activities:", Activity.getInstanceCount()); + printRow(pw, TWO_COUNT_COLUMNS, "Views:", viewInstanceCount, "ViewRoots:", + viewRootInstanceCount); - printRow(pw, TWO_COUNT_COLUMNS, "Assets:", AssetManager.getGlobalAssetCount(), - "AssetManagers:", AssetManager.getGlobalAssetManagerCount()); + printRow(pw, TWO_COUNT_COLUMNS, "AppContexts:", appContextInstanceCount, + "Activities:", activityInstanceCount); - printRow(pw, TWO_COUNT_COLUMNS, "Local Binders:", Debug.getBinderLocalObjectCount(), - "Proxy Binders:", Debug.getBinderProxyObjectCount()); - printRow(pw, ONE_COUNT_COLUMN, "Death Recipients:", Debug.getBinderDeathObjectCount()); + printRow(pw, TWO_COUNT_COLUMNS, "Assets:", globalAssetCount, + "AssetManagers:", globalAssetManagerCount); - printRow(pw, ONE_COUNT_COLUMN, "OpenSSL Sockets:", OpenSSLSocketImpl.getInstanceCount()); + printRow(pw, TWO_COUNT_COLUMNS, "Local Binders:", binderLocalObjectCount, + "Proxy Binders:", binderProxyObjectCount); + printRow(pw, ONE_COUNT_COLUMN, "Death Recipients:", binderDeathObjectCount); + printRow(pw, ONE_COUNT_COLUMN, "OpenSSL Sockets:", openSslSocketCount); + // SQLite mem info - long sqliteAllocated = SQLiteDebug.getHeapAllocatedSize() / 1024; - SQLiteDebug.PagerStats stats = new SQLiteDebug.PagerStats(); - SQLiteDebug.getPagerStats(stats); - pw.println(" "); pw.println(" SQL"); printRow(pw, TWO_COUNT_COLUMNS, "heap:", sqliteAllocated, "dbFiles:", @@ -1751,6 +1850,7 @@ public final class ActivityThread { final HashMap<String, WeakReference<PackageInfo>> mResourcePackages = new HashMap<String, WeakReference<PackageInfo>>(); Display mDisplay = null; + DisplayMetrics mDisplayMetrics = null; HashMap<String, WeakReference<Resources> > mActiveResources = new HashMap<String, WeakReference<Resources> >(); @@ -1918,7 +2018,7 @@ public final class ActivityThread { PackageInfo info = new PackageInfo(this, "android", context); context.init(info, null, this); context.getResources().updateConfiguration( - getConfiguration(), getDisplayMetricsLocked()); + getConfiguration(), getDisplayMetricsLocked(false)); mSystemContext = context; //Log.i(TAG, "Created system resources " + context.getResources() // + ": " + context.getResources().getConfiguration()); @@ -2557,7 +2657,10 @@ public final class ActivityThread { a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; - wm.addView(decor, l); + if (a.mVisibleFromClient) { + a.mWindowAdded = true; + wm.addView(decor, l); + } // If the window has already been added, but during resume // we started another activity, then don't yet make the @@ -2576,7 +2679,8 @@ public final class ActivityThread { performConfigurationChanged(r.activity, r.newConfig); r.newConfig = null; } - Log.v(TAG, "Resuming " + r + " with isForward=" + isForward); + if (localLOGV) Log.v(TAG, "Resuming " + r + " with isForward=" + + isForward); WindowManager.LayoutParams l = r.window.getAttributes(); if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) @@ -2588,8 +2692,11 @@ public final class ActivityThread { View decor = r.window.getDecorView(); wm.updateViewLayout(decor, l); } - r.activity.mDecor.setVisibility(View.VISIBLE); + r.activity.mVisibleFromServer = true; mNumVisibleActivities++; + if (r.activity.mVisibleFromClient) { + r.activity.makeVisible(); + } } r.nextIdle = mNewActivities; @@ -2800,18 +2907,22 @@ public final class ActivityThread { View v = r.activity.mDecor; if (v != null) { if (show) { - if (v.getVisibility() != View.VISIBLE) { - v.setVisibility(View.VISIBLE); + if (!r.activity.mVisibleFromServer) { + r.activity.mVisibleFromServer = true; mNumVisibleActivities++; + if (r.activity.mVisibleFromClient) { + r.activity.makeVisible(); + } } if (r.newConfig != null) { performConfigurationChanged(r.activity, r.newConfig); r.newConfig = null; } } else { - if (v.getVisibility() == View.VISIBLE) { - v.setVisibility(View.INVISIBLE); + if (r.activity.mVisibleFromServer) { + r.activity.mVisibleFromServer = false; mNumVisibleActivities--; + v.setVisibility(View.INVISIBLE); } } } @@ -3037,11 +3148,13 @@ public final class ActivityThread { WindowManager wm = r.activity.getWindowManager(); View v = r.activity.mDecor; if (v != null) { - if (v.getVisibility() == View.VISIBLE) { + if (r.activity.mVisibleFromServer) { mNumVisibleActivities--; } IBinder wtoken = v.getWindowToken(); - wm.removeViewImmediate(v); + if (r.activity.mWindowAdded) { + wm.removeViewImmediate(v); + } if (wtoken != null) { WindowManagerImpl.getDefault().closeAll(wtoken, r.activity.getClass().getName(), "Activity"); @@ -3271,6 +3384,7 @@ public final class ActivityThread { mConfiguration = new Configuration(); } mConfiguration.updateFrom(config); + DisplayMetrics dm = getDisplayMetricsLocked(true); // set it for java, this also affects newly created Resources if (config.locale != null) { @@ -3290,7 +3404,7 @@ public final class ActivityThread { WeakReference<Resources> v = it.next(); Resources r = v.get(); if (r != null) { - r.updateConfiguration(config, null); + r.updateConfiguration(config, dm); //Log.i(TAG, "Updated app resources " + v.getKey() // + " " + r + ": " + r.getConfiguration()); } else { diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java index 4236a00..394b8e3 100644 --- a/core/java/android/app/ApplicationContext.java +++ b/core/java/android/app/ApplicationContext.java @@ -1630,6 +1630,15 @@ class ApplicationContext extends Context { } @Override + public String[] getSystemSharedLibraryNames() { + try { + return mPM.getSystemSharedLibraryNames(); + } catch (RemoteException e) { + throw new RuntimeException("Package manager has died", e); + } + } + + @Override public int checkPermission(String permName, String pkgName) { try { return mPM.checkPermission(permName, pkgName); @@ -1974,6 +1983,18 @@ class ApplicationContext extends Context { getApplicationInfo(appPackageName, 0)); } + int mCachedSafeMode = -1; + @Override public boolean isSafeMode() { + try { + if (mCachedSafeMode < 0) { + mCachedSafeMode = mPM.isSafeMode() ? 1 : 0; + } + return mCachedSafeMode != 0; + } catch (RemoteException e) { + throw new RuntimeException("Package manager has died", e); + } + } + static void configurationChanged() { synchronized (sSync) { sIconCache.clear(); diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 951b48d..b09a57f 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -122,7 +122,7 @@ public class Dialog implements DialogInterface, Window.Callback, * uses the window manager and theme from this context to * present its UI. * @param theme A style resource describing the theme to use for the - * window. See <a href="{@docRoot}reference/available-resources.html#stylesandthemes">Style + * window. See <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">Style * and Theme Resources</a> for more information about defining and using * styles. This theme is applied on top of the current theme in * <var>context</var>. If 0, the default dialog theme will be used. @@ -518,7 +518,7 @@ public class Dialog implements DialogInterface, Window.Callback, private boolean isOutOfBounds(MotionEvent event) { final int x = (int) event.getX(); final int y = (int) event.getY(); - final int slop = ViewConfiguration.getWindowTouchSlop(); + final int slop = ViewConfiguration.get(mContext).getScaledWindowTouchSlop(); final View decorView = getWindow().getDecorView(); return (x < -slop) || (y < -slop) || (x > (decorView.getWidth()+slop)) diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 353500e..cd3701f 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -22,6 +22,7 @@ import android.content.ContentProviderNative; import android.content.IContentProvider; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.ProviderInfo; import android.content.res.Configuration; @@ -127,7 +128,8 @@ public interface IActivityManager extends IInterface { boolean doRebind) throws RemoteException; /* oneway */ public void serviceDoneExecuting(IBinder token) throws RemoteException; - + public IBinder peekService(Intent service, String resolvedType) throws RemoteException; + public boolean startInstrumentation(ComponentName className, String profileFile, int flags, Bundle arguments, IInstrumentationWatcher watcher) throws RemoteException; @@ -216,6 +218,13 @@ public interface IActivityManager extends IInterface { // Retrieve running application processes in the system public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() throws RemoteException; + // Get device configuration + public ConfigurationInfo getDeviceConfigurationInfo() throws RemoteException; + + /* + * Private non-Binder interfaces + */ + /* package */ boolean testIsSystemReady(); /** Information you can retrieve about a particular application. */ public static class ContentProviderHolder implements Parcelable { @@ -354,4 +363,6 @@ public interface IActivityManager extends IInterface { int GET_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+80; int REPORT_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+81; int GET_RUNNING_APP_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+82; + int GET_DEVICE_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+83; + int PEEK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+84; } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index f96d787..f6a28b2 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1267,7 +1267,7 @@ public class Instrumentation { } /** - * Perform calling of an activity's {@link Activity#onUserLeaving} method. + * Perform calling of an activity's {@link Activity#onUserLeaveHint} method. * The default implementation simply calls through to that method. * * @param activity The activity being notified that the user has navigated away diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java index 8f0a4f5..c363f04 100644 --- a/core/java/android/app/LauncherActivity.java +++ b/core/java/android/app/LauncherActivity.java @@ -21,11 +21,23 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PaintFlagsDrawFilter; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.PaintDrawable; +import android.os.AsyncTask; import android.os.Bundle; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.Window; import android.widget.BaseAdapter; import android.widget.Filter; import android.widget.Filterable; @@ -43,33 +55,59 @@ import java.util.List; * */ public abstract class LauncherActivity extends ListActivity { + + Intent mIntent; + PackageManager mPackageManager; /** + * An item in the list + */ + public static class ListItem { + public CharSequence label; + //public CharSequence description; + public Drawable icon; + public String packageName; + public String className; + + ListItem(PackageManager pm, ResolveInfo resolveInfo, IconResizer resizer) { + label = resolveInfo.loadLabel(pm); + if (label == null && resolveInfo.activityInfo != null) { + label = resolveInfo.activityInfo.name; + } + + /* + if (resolveInfo.activityInfo != null && + resolveInfo.activityInfo.applicationInfo != null) { + description = resolveInfo.activityInfo.applicationInfo.loadDescription(pm); + } + */ + + icon = resizer.createIconThumbnail(resolveInfo.loadIcon(pm)); + packageName = resolveInfo.activityInfo.applicationInfo.packageName; + className = resolveInfo.activityInfo.name; + } + + public ListItem() { + } + } + + /** * Adapter which shows the set of activities that can be performed for a given intent. */ private class ActivityAdapter extends BaseAdapter implements Filterable { private final Object lock = new Object(); - private ArrayList<ResolveInfo> mOriginalValues; + private ArrayList<ListItem> mOriginalValues; - protected final Context mContext; - protected final Intent mIntent; protected final LayoutInflater mInflater; - protected List<ResolveInfo> mActivitiesList; + protected List<ListItem> mActivitiesList; private Filter mFilter; - - public ActivityAdapter(Context context, Intent intent) { - mContext = context; - mIntent = new Intent(intent); - mIntent.setComponent(null); - mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - PackageManager pm = context.getPackageManager(); - mActivitiesList = pm.queryIntentActivities(intent, 0); - if (mActivitiesList != null) { - Collections.sort(mActivitiesList, new ResolveInfo.DisplayNameComparator(pm)); - } + + public ActivityAdapter() { + mInflater = (LayoutInflater) LauncherActivity.this.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + mActivitiesList = makeListItems(); } public Intent intentForPosition(int position) { @@ -78,8 +116,8 @@ public abstract class LauncherActivity extends ListActivity { } Intent intent = new Intent(mIntent); - ActivityInfo ai = mActivitiesList.get(position).activityInfo; - intent.setClassName(ai.applicationInfo.packageName, ai.name); + ListItem item = mActivitiesList.get(position); + intent.setClassName(item.packageName, item.className); return intent; } @@ -99,7 +137,7 @@ public abstract class LauncherActivity extends ListActivity { View view; if (convertView == null) { view = mInflater.inflate( - com.android.internal.R.layout.simple_list_item_1, parent, false); + com.android.internal.R.layout.activity_list_item_2, parent, false); } else { view = convertView; } @@ -108,7 +146,7 @@ public abstract class LauncherActivity extends ListActivity { } private char getCandidateLetter(ResolveInfo info) { - PackageManager pm = mContext.getPackageManager(); + PackageManager pm = LauncherActivity.this.getPackageManager(); CharSequence label = info.loadLabel(pm); if (label == null) { @@ -118,24 +156,22 @@ public abstract class LauncherActivity extends ListActivity { return Character.toLowerCase(label.charAt(0)); } - private void bindView(View view, ResolveInfo info) { - TextView text = (TextView) view.findViewById(com.android.internal.R.id.text1); - - PackageManager pm = mContext.getPackageManager(); - CharSequence label = info.loadLabel(pm); - text.setText(label != null ? label : info.activityInfo.name); + private void bindView(View view, ListItem item) { + TextView text = (TextView) view; + text.setText(item.label); + text.setCompoundDrawablesWithIntrinsicBounds(item.icon, null, null, null); } - + public Filter getFilter() { if (mFilter == null) { mFilter = new ArrayFilter(); } return mFilter; } - + /** - * <p>An array filters constrains the content of the array adapter with a prefix. Each item that - * does not start with the supplied prefix is removed from the list.</p> + * An array filters constrains the content of the array adapter with a prefix. Each + * item that does not start with the supplied prefix is removed from the list. */ private class ArrayFilter extends Filter { @Override @@ -144,39 +180,36 @@ public abstract class LauncherActivity extends ListActivity { if (mOriginalValues == null) { synchronized (lock) { - mOriginalValues = new ArrayList<ResolveInfo>(mActivitiesList); + mOriginalValues = new ArrayList<ListItem>(mActivitiesList); } } if (prefix == null || prefix.length() == 0) { synchronized (lock) { - ArrayList<ResolveInfo> list = new ArrayList<ResolveInfo>(mOriginalValues); + ArrayList<ListItem> list = new ArrayList<ListItem>(mOriginalValues); results.values = list; results.count = list.size(); } } else { - final PackageManager pm = mContext.getPackageManager(); + final PackageManager pm = LauncherActivity.this.getPackageManager(); final String prefixString = prefix.toString().toLowerCase(); - ArrayList<ResolveInfo> values = mOriginalValues; + ArrayList<ListItem> values = mOriginalValues; int count = values.size(); - ArrayList<ResolveInfo> newValues = new ArrayList<ResolveInfo>(count); + ArrayList<ListItem> newValues = new ArrayList<ListItem>(count); for (int i = 0; i < count; i++) { - ResolveInfo value = values.get(i); - - final CharSequence label = value.loadLabel(pm); - final CharSequence name = label != null ? label : value.activityInfo.name; + ListItem item = values.get(i); - String[] words = name.toString().toLowerCase().split(" "); + String[] words = item.label.toString().toLowerCase().split(" "); int wordCount = words.length; for (int k = 0; k < wordCount; k++) { final String word = words[k]; if (word.startsWith(prefixString)) { - newValues.add(value); + newValues.add(item); break; } } @@ -192,7 +225,7 @@ public abstract class LauncherActivity extends ListActivity { @Override protected void publishResults(CharSequence constraint, FilterResults results) { //noinspection unchecked - mActivitiesList = (List<ResolveInfo>) results.values; + mActivitiesList = (List<ListItem>) results.values; if (results.count > 0) { notifyDataSetChanged(); } else { @@ -201,19 +234,121 @@ public abstract class LauncherActivity extends ListActivity { } } } - - + + /** + * Utility class to resize icons to match default icon size. + */ + public class IconResizer { + // Code is borrowed from com.android.launcher.Utilities. + private int mIconWidth = -1; + private int mIconHeight = -1; + + private final Paint mPaint = new Paint(); + private final Rect mBounds = new Rect(); + private final Rect mOldBounds = new Rect(); + private Canvas mCanvas = new Canvas(); + + public IconResizer() { + mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, + Paint.FILTER_BITMAP_FLAG)); + + final Resources resources = LauncherActivity.this.getResources(); + mIconWidth = mIconHeight = (int) resources.getDimension( + android.R.dimen.app_icon_size); + } + + /** + * Returns a Drawable representing the thumbnail of the specified Drawable. + * The size of the thumbnail is defined by the dimension + * android.R.dimen.launcher_application_icon_size. + * + * This method is not thread-safe and should be invoked on the UI thread only. + * + * @param icon The icon to get a thumbnail of. + * + * @return A thumbnail for the specified icon or the icon itself if the + * thumbnail could not be created. + */ + public Drawable createIconThumbnail(Drawable icon) { + int width = mIconWidth; + int height = mIconHeight; + + final int iconWidth = icon.getIntrinsicWidth(); + final int iconHeight = icon.getIntrinsicHeight(); + + if (icon instanceof PaintDrawable) { + PaintDrawable painter = (PaintDrawable) icon; + painter.setIntrinsicWidth(width); + painter.setIntrinsicHeight(height); + } + + if (width > 0 && height > 0) { + if (width < iconWidth || height < iconHeight) { + final float ratio = (float) iconWidth / iconHeight; + + if (iconWidth > iconHeight) { + height = (int) (width / ratio); + } else if (iconHeight > iconWidth) { + width = (int) (height * ratio); + } + + final Bitmap.Config c = icon.getOpacity() != PixelFormat.OPAQUE ? + Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; + final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c); + final Canvas canvas = mCanvas; + canvas.setBitmap(thumb); + // Copy the old bounds to restore them later + // If we were to do oldBounds = icon.getBounds(), + // the call to setBounds() that follows would + // change the same instance and we would lose the + // old bounds + mOldBounds.set(icon.getBounds()); + final int x = (mIconWidth - width) / 2; + final int y = (mIconHeight - height) / 2; + icon.setBounds(x, y, x + width, y + height); + icon.draw(canvas); + icon.setBounds(mOldBounds); + icon = new BitmapDrawable(thumb); + } else if (iconWidth < width && iconHeight < height) { + final Bitmap.Config c = Bitmap.Config.ARGB_8888; + final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c); + final Canvas canvas = mCanvas; + canvas.setBitmap(thumb); + mOldBounds.set(icon.getBounds()); + final int x = (width - iconWidth) / 2; + final int y = (height - iconHeight) / 2; + icon.setBounds(x, y, x + iconWidth, y + iconHeight); + icon.draw(canvas); + icon.setBounds(mOldBounds); + icon = new BitmapDrawable(thumb); + } + } + + return icon; + } + } + @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + mPackageManager = getPackageManager(); + + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setProgressBarIndeterminateVisibility(true); + setContentView(com.android.internal.R.layout.activity_list); - mAdapter = new ActivityAdapter(this, getTargetIntent()); + + mIntent = new Intent(getTargetIntent()); + mIntent.setComponent(null); + mAdapter = new ActivityAdapter(); setListAdapter(mAdapter); getListView().setTextFilterEnabled(true); + + setProgressBarIndeterminateVisibility(false); } - @Override protected void onListItemClick(ListView l, View v, int position, long id) { Intent intent = ((ActivityAdapter)mAdapter).intentForPosition(position); @@ -221,6 +356,42 @@ public abstract class LauncherActivity extends ListActivity { startActivity(intent); } - protected abstract Intent getTargetIntent(); - + /** + * Return the actual Intent for a specific position in our + * {@link android.widget.ListView}. + * @param position The item whose Intent to return + */ + protected Intent intentForPosition(int position) { + ActivityAdapter adapter = (ActivityAdapter) mAdapter; + return adapter.intentForPosition(position); + } + + /** + * Get the base intent to use when running + * {@link PackageManager#queryIntentActivities(Intent, int)}. + */ + protected Intent getTargetIntent() { + return new Intent(); + } + + /** + * Perform the query to determine which results to show and return a list of them. + */ + public List<ListItem> makeListItems() { + // Load all matching activities and sort correctly + List<ResolveInfo> list = mPackageManager.queryIntentActivities(mIntent, + /* no flags */ 0); + Collections.sort(list, new ResolveInfo.DisplayNameComparator(mPackageManager)); + + IconResizer resizer = new IconResizer(); + + ArrayList<ListItem> result = new ArrayList<ListItem>(list.size()); + int listSize = list.size(); + for (int i = 0; i < listSize; i++) { + ResolveInfo resolveInfo = list.get(i); + result.add(new ListItem(mPackageManager, resolveInfo, resizer)); + } + + return result; + } } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index ea67cdb..51fddb1 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -90,8 +90,9 @@ public class Notification implements Parcelable * The intent to execute when the expanded status entry is clicked. If * this is an activity, it must include the * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires - * that you take care of task management as described in the - * <a href="{@docRoot}intro/appmodel.html">application model</a> document. + * that you take care of task management as described in the <em>Activities and Tasks</em> + * section of the <a href="{@docRoot}guide/topics/fundamentals.html#acttask">Application + * Fundamentals</a> document. */ public PendingIntent contentIntent; @@ -420,8 +421,8 @@ public class Notification implements Parcelable * @param contentIntent The intent to launch when the user clicks the expanded notification. * If this is an activity, it must include the * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires - * that you take care of task management as described in the - * <a href="{@docRoot}intro/appmodel.html">application model</a> document. + * that you take care of task management as described in + * <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">Application Fundamentals: Activities and Tasks</a>. */ public void setLatestEventInfo(Context context, CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) { diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 2e2a1a1..64f1ba2 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -16,11 +16,14 @@ package android.app; +import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; @@ -34,6 +37,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.server.search.SearchableInfo; +import android.speech.RecognizerIntent; import android.text.Editable; import android.text.InputType; import android.text.TextUtils; @@ -50,6 +54,7 @@ import android.widget.AdapterView; import android.widget.AutoCompleteTextView; import android.widget.Button; import android.widget.CursorAdapter; +import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ListView; import android.widget.SimpleCursorAdapter; @@ -59,6 +64,7 @@ import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; import java.lang.ref.WeakReference; +import java.util.List; import java.util.concurrent.atomic.AtomicLong; /** @@ -94,6 +100,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private TextView mBadgeLabel; private AutoCompleteTextView mSearchTextField; private Button mGoButton; + private ImageButton mVoiceButton; // interaction with searchable application private ComponentName mLaunchComponent; @@ -115,6 +122,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private Uri mSuggestionData = null; private String mSuggestionQuery = null; + // For voice searching + private Intent mVoiceWebSearchIntent; + private Intent mVoiceAppSearchIntent; + // support for AutoCompleteTextView suggestions display private SuggestionsAdapter mSuggestionsAdapter; @@ -153,12 +164,15 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mSearchTextField = (AutoCompleteTextView) findViewById(com.android.internal.R.id.search_src_text); mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn); + mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn); // attach listeners mSearchTextField.addTextChangedListener(mTextWatcher); mSearchTextField.setOnKeyListener(mTextKeyListener); mGoButton.setOnClickListener(mGoButtonClickListener); mGoButton.setOnKeyListener(mButtonsKeyListener); + mVoiceButton.setOnClickListener(mVoiceButtonClickListener); + mVoiceButton.setOnKeyListener(mButtonsKeyListener); // pre-hide all the extraneous elements mBadgeLabel.setVisibility(View.GONE); @@ -169,13 +183,19 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS setCanceledOnTouchOutside(true); // Set up broadcast filters - mCloseDialogsFilter = new - IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + mCloseDialogsFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); mPackageFilter = new IntentFilter(); mPackageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); mPackageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); mPackageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); mPackageFilter.addDataScheme("package"); + + // Save voice intent for later queries/launching + mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); + mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, + RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH); + + mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); } /** @@ -261,16 +281,15 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** - * Dismiss the search dialog. + * The search dialog is being dismissed, so handle all of the local shutdown operations. * - * This function is designed to be idempotent so it can be safely called at any time + * This function is designed to be idempotent so that dismiss() can be safely called at any time * (even if already closed) and more likely to really dump any memory. No leaks! */ @Override - public void dismiss() { - if (isShowing()) { - super.dismiss(); - } + public void onStop() { + super.onStop(); + setOnCancelListener(null); setOnDismissListener(null); @@ -281,6 +300,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // This is OK - it just means we didn't have any registered } + // close any leftover cursor + if (mSuggestionsAdapter != null) { + mSuggestionsAdapter.changeCursor(null); + } + // dump extra memory we're hanging on to mLaunchComponent = null; mAppSearchData = null; @@ -408,6 +432,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS updateSearchButton(); updateSearchBadge(); updateQueryHint(); + updateVoiceButton(); // In order to properly configure the input method (if one is being used), we // need to let it know if we'll be providing suggestions. Although it would be @@ -500,6 +525,30 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** + * Update the visibility of the voice button. There are actually two voice search modes, + * either of which will activate the button. + */ + private void updateVoiceButton() { + int visibility = View.GONE; + if (mSearchable.getVoiceSearchEnabled()) { + Intent testIntent = null; + if (mSearchable.getVoiceSearchLaunchWebSearch()) { + testIntent = mVoiceWebSearchIntent; + } else if (mSearchable.getVoiceSearchLaunchRecognizer()) { + testIntent = mVoiceAppSearchIntent; + } + if (testIntent != null) { + List<ResolveInfo> list = getContext().getPackageManager(). + queryIntentActivities(testIntent, PackageManager.MATCH_DEFAULT_ONLY); + if (list.size() > 0) { + visibility = View.VISIBLE; + } + } + } + mVoiceButton.setVisibility(visibility); + } + + /** * Listeners of various types */ @@ -642,11 +691,97 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS }; /** + * React to a click in the voice search button. + */ + View.OnClickListener mVoiceButtonClickListener = new View.OnClickListener() { + public void onClick(View v) { + try { + if (mSearchable.getVoiceSearchLaunchWebSearch()) { + getContext().startActivity(mVoiceWebSearchIntent); + dismiss(); + } else if (mSearchable.getVoiceSearchLaunchRecognizer()) { + Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent); + getContext().startActivity(appSearchIntent); + dismiss(); + } + } catch (ActivityNotFoundException e) { + // Should not happen, since we check the availability of + // voice search before showing the button. But just in case... + Log.w(LOG_TAG, "Could not find voice search activity"); + } + } + }; + + /** + * Create and return an Intent that can launch the voice search activity, perform a specific + * voice transcription, and forward the results to the searchable activity. + * + * @param baseIntent The voice app search intent to start from + * @return A completely-configured intent ready to send to the voice search activity + */ + private Intent createVoiceAppSearchIntent(Intent baseIntent) { + // create the necessary intent to set up a search-and-forward operation + // in the voice search system. We have to keep the bundle separate, + // because it becomes immutable once it enters the PendingIntent + Intent queryIntent = new Intent(Intent.ACTION_SEARCH); + queryIntent.setComponent(mSearchable.mSearchActivity); + PendingIntent pending = PendingIntent.getActivity( + getContext(), 0, queryIntent, PendingIntent.FLAG_ONE_SHOT); + + // Now set up the bundle that will be inserted into the pending intent + // when it's time to do the search. We always build it here (even if empty) + // because the voice search activity will always need to insert "QUERY" into + // it anyway. + Bundle queryExtras = new Bundle(); + if (mAppSearchData != null) { + queryExtras.putBundle(SearchManager.APP_DATA, mAppSearchData); + } + + // Now build the intent to launch the voice search. Add all necessary + // extras to launch the voice recognizer, and then all the necessary extras + // to forward the results to the searchable activity + Intent voiceIntent = new Intent(baseIntent); + + // Add all of the configuration options supplied by the searchable's metadata + String languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM; + String prompt = null; + String language = null; + int maxResults = 1; + Resources resources = mActivityContext.getResources(); + if (mSearchable.getVoiceLanguageModeId() != 0) { + languageModel = resources.getString(mSearchable.getVoiceLanguageModeId()); + } + if (mSearchable.getVoicePromptTextId() != 0) { + prompt = resources.getString(mSearchable.getVoicePromptTextId()); + } + if (mSearchable.getVoiceLanguageId() != 0) { + language = resources.getString(mSearchable.getVoiceLanguageId()); + } + if (mSearchable.getVoiceMaxResults() != 0) { + maxResults = mSearchable.getVoiceMaxResults(); + } + voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel); + voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt); + voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language); + voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults); + + // Add the values that configure forwarding the results + voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending); + voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, queryExtras); + + return voiceIntent; + } + + /** * React to the user typing "enter" or other hardwired keys while typing in the search box. * This handles these special keys while the edit box has focus. */ View.OnKeyListener mTextKeyListener = new View.OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + cancel(); + return true; + } // also guard against possible race conditions (late arrival after dismiss) if (mSearchable != null && TextUtils.getTrimmedLength(mSearchTextField.getText()) > 0) { @@ -793,24 +928,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS }; /** - * UI-thread handling of dialog dismiss. Called by mBroadcastReceiver.onReceive(). - * - * TODO: This is a really heavyweight solution for something that should be so simple. - * For example, we already have a handler, in our superclass, why aren't we sharing that? - * I think we need to investigate simplifying this entire methodology, or perhaps boosting - * it up into the Dialog class. - */ - private static final int MESSAGE_DISMISS = 0; - private Handler mDismissHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - if (msg.what == MESSAGE_DISMISS) { - dismiss(); - } - } - }; - - /** * Various ways to launch searches */ diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 0a37e81..2cc6de9 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -168,7 +168,8 @@ import android.view.KeyEvent; * <p><b>Managing focus and knowing if Search is active.</b> The search UI is not a separate * activity, and when the UI is invoked or dismissed, your activity will not typically be paused, * resumed, or otherwise notified by the methods defined in - * <a href="android.app.Activity#ActivityLifecycle">Activity Lifecycle</a>. The search UI is + * <a href="{@docRoot}guide/topics/fundamentals.html#actlife">Application Fundamentals: + * Activity Lifecycle</a>. The search UI is * handled in the same way as other system UI elements which may appear from time to time, such as * notifications, screen locks, or other system alerts: * <p>When the search UI appears, your activity will lose input focus. @@ -212,11 +213,11 @@ import android.view.KeyEvent; * {@link #QUERY getStringExtra(SearchManager.QUERY)}.</li> * <li>To identify and support your searchable activity, you'll need to * provide an XML file providing searchability configuration parameters, a reference to that - * in your searchable activity's <a href="../../../devel/bblocks-manifest.html">manifest</a> + * in your searchable activity's <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> * entry, and an intent-filter declaring that you can * receive ACTION_SEARCH intents. This is described in more detail in the * <a href="#SearchabilityMetadata">Searchability Metadata</a> section.</li> - * <li>Your <a href="../../../devel/bblocks-manifest.html">manifest</a> also needs a metadata entry + * <li>Your <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> also needs a metadata entry * providing a global reference to the searchable activity. This is the "glue" directing the search * UI, when invoked from any of your <i>other</i> activities, to use your application as the * default search context. This is also described in more detail in the @@ -359,7 +360,7 @@ import android.view.KeyEvent; * <li>Implement a Content Provider that provides suggestions. If you already have one, and it * has access to your suggestions data. If not, you'll have to create one. * You'll also provide information about your Content Provider in your - * package's <a href="../../../devel/bblocks-manifest.html">manifest</a>.</li> + * package's <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a>.</li> * <li>Update your searchable activity's XML configuration file. There are two categories of * information used for suggestions: * <ul><li>The first is (required) data that the search manager will @@ -634,7 +635,7 @@ import android.view.KeyEvent; * * <p><b>Metadata for searchable activity.</b> As with your search implementations described * above, you must first identify which of your activities is searchable. In the - * <a href="../../../devel/bblocks-manifest.html">manifest</a> entry for this activity, you must + * <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> entry for this activity, you must * provide two elements: * <ul><li>An intent-filter specifying that you can receive and process the * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} {@link android.content.Intent Intent}. @@ -643,7 +644,7 @@ import android.view.KeyEvent; * remaining configuration information for how your application implements search.</li></ul> * * <p>Here is a snippet showing the necessary elements in the - * <a href="../../../devel/bblocks-manifest.html">manifest</a> entry for your searchable activity. + * <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> entry for your searchable activity. * <pre class="prettyprint"> * <!-- Search Activity - searchable --> * <activity android:name="MySearchActivity" @@ -765,9 +766,8 @@ import android.view.KeyEvent; * <li>.../res/values/strings.xml</li></ul> * * <p>For more complete documentation on this capability, see - * <a href="../../../devel/resources-i18n.html#AlternateResources">Resources and - * Internationalization: Supporting Alternate Resources for Alternate Languages and Configurations - * </a>. + * <a href="{@docRoot}guide/topics/resources/resources-i18n.html#AlternateResources">Resources and + * Internationalization: Alternate Resources</a>. * * <p><b>Metadata for non-searchable activities.</b> Activities which are part of a searchable * application, but don't implement search itself, require a bit of "glue" in order to cause @@ -775,7 +775,7 @@ import android.view.KeyEvent; * provided, then searches from these activities will use the system default search context. * * <p>The simplest way to specify this is to add a <i>search reference</i> element to the - * application entry in the <a href="../../../devel/bblocks-manifest.html">manifest</a> file. + * application entry in the <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> file. * The value of this reference can be either of: * <ul><li>The name of your searchable activity. * It is typically prefixed by '.' to indicate that it's in the same package.</li> @@ -803,7 +803,7 @@ import android.view.KeyEvent; * to generate search suggestions, you'll need to publish it to the system, and you'll need to * provide a bit of additional XML metadata in order to configure communications with it. * - * <p>First, in your <a href="../../../devel/bblocks-manifest.html">manifest</a>, you'll add the + * <p>First, in your <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a>, you'll add the * following lines. * <pre class="prettyprint"> * <!-- Content provider for search suggestions --> @@ -832,7 +832,7 @@ import android.view.KeyEvent; * <tbody> * <tr><th>android:searchSuggestAuthority</th> * <td>This value must match the authority string provided in the <i>provider</i> section - * of your <a href="../../../devel/bblocks-manifest.html">manifest</a>.</td> + * of your <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a>.</td> * <td align="center">Yes</td> * </tr> * diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index 6c08e75..72692f4 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -42,12 +42,12 @@ import java.io.PrintWriter; * thread of their hosting process. This means that, if your service is going * to do any CPU intensive (such as MP3 playback) or blocking (such as * networking) operations, it should spawn its own thread in which to do that - * work. More information on this can be found in the - * <a href="{@docRoot}intro/appmodel.html#Threads">Threading section of the - * Application Model overview</a>.</p> + * work. More information on this can be found in + * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: + * Processes and Threads</a>.</p> * * <p>The Service class is an important part of an - * <a href="{@docRoot}intro/lifecycle.html">application's overall lifecycle</a>.</p> + * <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">application's overall lifecycle</a>.</p> * * <p>Topics covered here: * <ol> @@ -79,7 +79,7 @@ import java.io.PrintWriter; * to the service. The service will remain running as long as the connection * is established (whether or not the client retains a reference on the * service's IBinder). Usually the IBinder returned is for a complex - * interface that has been <a href="{@docRoot}reference/aidl.html">written + * interface that has been <a href="{@docRoot}guide/developing/tools/aidl.html">written * in aidl</a>. * * <p>A service can be both started and have connections bound to it. In such @@ -106,7 +106,7 @@ import java.io.PrintWriter; * {@link #checkCallingPermission} * method before executing the implementation of that call. * - * <p>See the <a href="{@docRoot}devel/security.html">Security Model</a> + * <p>See the <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> * document for more information on permissions and security in general. * * <a name="ProcessLifecycle"></a> @@ -201,14 +201,14 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * Return the communication channel to the service. May return null if * clients can not bind to the service. The returned * {@link android.os.IBinder} is usually for a complex interface - * that has been <a href="{@docRoot}reference/aidl.html">described using + * that has been <a href="{@docRoot}guide/developing/tools/aidl.html">described using * aidl</a>. * * <p><em>Note that unlike other application components, calls on to the * IBinder interface returned here may not happen on the main thread * of the process</em>. More information about this can be found - * in the <a href="{@docRoot}intro/appmodel.html#Threads">Threading section - * of the Application Model overview</a>.</p> + * in <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: + * Processes and Threads</a>.</p> * * @param intent The Intent that was used to bind to this service, * as given to {@link android.content.Context#bindService diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index d613e1c..56b231f 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -31,10 +31,15 @@ import java.io.UnsupportedEncodingException; * @hide */ public class BluetoothDevice { - public static final int MODE_UNKNOWN = -1; - public static final int MODE_OFF = 0; - public static final int MODE_CONNECTABLE = 1; - public static final int MODE_DISCOVERABLE = 2; + /** Inquiry scan and page scan are both off. + * Device is neither discoverable nor connectable */ + public static final int SCAN_MODE_NONE = 0; + /** Page scan is on, inquiry scan is off. + * Device is connectable, but not discoverable */ + public static final int SCAN_MODE_CONNECTABLE = 1; + /** Page scan and inquiry scan are on. + * Device is connectable and discoverable */ + public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 3; public static final int RESULT_FAILURE = -1; public static final int RESULT_SUCCESS = 0; @@ -54,10 +59,10 @@ public class BluetoothDevice { /** A bond attempt failed because the other side explicilty rejected * bonding */ public static final int UNBOND_REASON_AUTH_REJECTED = 2; - /** A bond attempt failed because we cancelled the bonding process */ - public static final int UNBOND_REASON_CANCELLED = 3; + /** A bond attempt failed because we canceled the bonding process */ + public static final int UNBOND_REASON_AUTH_CANCELED = 3; /** A bond attempt failed because we could not contact the remote device */ - public static final int UNBOND_REASON_AUTH_REMOTE_DEVICE_DOWN = 4; + public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; /** An existing bond was explicitly revoked */ public static final int UNBOND_REASON_REMOVED = 5; @@ -174,18 +179,6 @@ public class BluetoothDevice { return false; } - public String getMajorClass() { - try { - return mService.getMajorClass(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getMinorClass() { - try { - return mService.getMinorClass(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } public String getVersion() { try { return mService.getVersion(); @@ -211,15 +204,26 @@ public class BluetoothDevice { return null; } - public int getMode() { + /** + * Get the current scan mode. + * Used to determine if the local device is connectable and/or discoverable + * @return Scan mode, one of SCAN_MODE_* or an error code + */ + public int getScanMode() { try { - return mService.getMode(); + return mService.getScanMode(); } catch (RemoteException e) {Log.e(TAG, "", e);} - return MODE_UNKNOWN; + return BluetoothError.ERROR_IPC; } - public void setMode(int mode) { + + /** + * Set the current scan mode. + * Used to make the local device connectable and/or discoverable + * @param scanMode One of SCAN_MODE_* + */ + public void setScanMode(int scanMode) { try { - mService.setMode(mode); + mService.setScanMode(scanMode); } catch (RemoteException e) {Log.e(TAG, "", e);} } @@ -435,24 +439,6 @@ public class BluetoothDevice { return null; } - public String getRemoteAlias(String address) { - try { - return mService.getRemoteAlias(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public boolean setRemoteAlias(String address, String alias) { - try { - return mService.setRemoteAlias(address, alias); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - public boolean clearRemoteAlias(String address) { - try { - return mService.clearRemoteAlias(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } public String getRemoteVersion(String address) { try { return mService.getRemoteVersion(address); @@ -477,24 +463,6 @@ public class BluetoothDevice { } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } - public String getRemoteMajorClass(String address) { - try { - return mService.getRemoteMajorClass(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRemoteMinorClass(String address) { - try { - return mService.getRemoteMinorClass(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String[] getRemoteServiceClasses(String address) { - try { - return mService.getRemoteServiceClasses(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } /** * Returns the RFCOMM channel associated with the 16-byte UUID on @@ -512,12 +480,19 @@ public class BluetoothDevice { return false; } + /** + * Get the major, minor and servics classes of a remote device. + * These classes are encoded as a 32-bit integer. See BluetoothClass. + * @param address remote device + * @return 32-bit class suitable for use with BluetoothClass. + */ public int getRemoteClass(String address) { try { return mService.getRemoteClass(address); } catch (RemoteException e) {Log.e(TAG, "", e);} return BluetoothClass.ERROR; } + public byte[] getRemoteFeatures(String address) { try { return mService.getRemoteFeatures(address); @@ -576,8 +551,8 @@ public class BluetoothDevice { } - /* Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */ private static final int ADDRESS_LENGTH = 17; + /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */ public static boolean checkBluetoothAddress(String address) { if (address == null || address.length() != ADDRESS_LENGTH) { return false; diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index c315271..34196bf 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -69,8 +69,8 @@ public class BluetoothHeadset { public static final int RESULT_FAILURE = 0; public static final int RESULT_SUCCESS = 1; - /** Connection cancelled before completetion. */ - public static final int RESULT_CANCELLED = 2; + /** Connection canceled before completetion. */ + public static final int RESULT_CANCELED = 2; /** Default priority for headsets that should be auto-connected */ public static final int PRIORITY_AUTO = 100; @@ -318,6 +318,12 @@ public class BluetoothHeadset { * @return True if this device might support HSP or HFP. */ public static boolean doesClassMatch(int btClass) { + // The render service class is required by the spec for HFP, so is a + // pretty good signal + if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) { + return true; + } + // Just in case they forgot the render service class switch (BluetoothClass.Device.getDevice(btClass)) { case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: diff --git a/core/java/android/bluetooth/BluetoothIntent.java b/core/java/android/bluetooth/BluetoothIntent.java index 57c46f9..b66b06e 100644 --- a/core/java/android/bluetooth/BluetoothIntent.java +++ b/core/java/android/bluetooth/BluetoothIntent.java @@ -29,8 +29,8 @@ import android.annotation.SdkConstant.SdkConstantType; * @hide */ public interface BluetoothIntent { - public static final String MODE = - "android.bluetooth.intent.MODE"; + public static final String SCAN_MODE = + "android.bluetooth.intent.SCAN_MODE"; public static final String ADDRESS = "android.bluetooth.intent.ADDRESS"; public static final String NAME = @@ -62,9 +62,14 @@ public interface BluetoothIntent { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String NAME_CHANGED_ACTION = "android.bluetooth.intent.action.NAME_CHANGED"; + + /** + * Broadcast when the scan mode changes. Always contains an int extra + * named SCAN_MODE that contains the new scan mode. + */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String MODE_CHANGED_ACTION = - "android.bluetooth.intent.action.MODE_CHANGED"; + public static final String SCAN_MODE_CHANGED_ACTION = + "android.bluetooth.intent.action.SCAN_MODE_CHANGED"; @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String DISCOVERY_STARTED_ACTION = @@ -104,12 +109,6 @@ public interface BluetoothIntent { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String REMOTE_NAME_FAILED_ACTION = "android.bluetooth.intent.action.REMOTE_NAME_FAILED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_ALIAS_CHANGED_ACTION = - "android.bluetooth.intent.action.REMOTE_ALIAS_CHANGED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_ALIAS_CLEARED_ACTION = - "android.bluetooth.intent.action.REMOTE_ALIAS_CLEARED"; /** * Broadcast when the bond state of a remote device changes. diff --git a/core/java/android/bluetooth/HeadsetBase.java b/core/java/android/bluetooth/HeadsetBase.java index bce3388..fd2d2ab 100644 --- a/core/java/android/bluetooth/HeadsetBase.java +++ b/core/java/android/bluetooth/HeadsetBase.java @@ -21,13 +21,10 @@ import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.util.Log; -import java.io.IOException; -import java.lang.Thread; - /** * The Android Bluetooth API is not finalized, and *will* change. Use at your * own risk. - * + * * The base RFCOMM (service) connection for a headset or handsfree device. * * In the future this class will be removed. @@ -90,7 +87,7 @@ public class HeadsetBase { /* Create from an already exisiting rfcomm connection */ public HeadsetBase(PowerManager pm, BluetoothDevice bluetooth, String address, int socketFd, - int rfcommChannel, Handler handler) { + int rfcommChannel, Handler handler) { mDirection = DIRECTION_INCOMING; mConnectTimestamp = System.currentTimeMillis(); mBluetooth = bluetooth; @@ -132,30 +129,8 @@ public class HeadsetBase { */ protected void initializeAtParser() { mAtParser = new AtParser(); - - // Microphone Gain - mAtParser.register("+VGM", new AtCommandHandler() { - @Override - public AtCommandResult handleSetCommand(Object[] args) { - // AT+VGM=<gain> in range [0,15] - // Headset/Handsfree is reporting its current gain setting - //TODO: sync to android UI - //TODO: Send unsolicited +VGM when volume changed on AG - return new AtCommandResult(AtCommandResult.OK); - } - }); - - // Speaker Gain - mAtParser.register("+VGS", new AtCommandHandler() { - @Override - public AtCommandResult handleSetCommand(Object[] args) { - // AT+VGS=<gain> in range [0,15] - // Headset/Handsfree is reporting its current gain to Android - //TODO: sync to AG UI - //TODO: Send unsolicited +VGS when volume changed on AG - return new AtCommandResult(AtCommandResult.OK); - } - }); + //TODO(): Get rid of this as there are no parsers registered. But because of dependencies, + //it needs to be done as part of refactoring HeadsetBase and BluetoothHandsfree } public AtParser getAtParser() { diff --git a/core/java/android/bluetooth/IBluetoothDevice.aidl b/core/java/android/bluetooth/IBluetoothDevice.aidl index 59f679f..4351d2e 100644 --- a/core/java/android/bluetooth/IBluetoothDevice.aidl +++ b/core/java/android/bluetooth/IBluetoothDevice.aidl @@ -32,15 +32,13 @@ interface IBluetoothDevice String getAddress(); String getName(); boolean setName(in String name); - String getMajorClass(); - String getMinorClass(); String getVersion(); String getRevision(); String getManufacturer(); String getCompany(); - int getMode(); - boolean setMode(int mode); + int getScanMode(); + boolean setScanMode(int mode); int getDiscoverableTimeout(); boolean setDiscoverableTimeout(int timeout); @@ -64,17 +62,11 @@ interface IBluetoothDevice int getBondState(in String address); String getRemoteName(in String address); - String getRemoteAlias(in String address); - boolean setRemoteAlias(in String address, in String alias); - boolean clearRemoteAlias(in String address); String getRemoteVersion(in String address); String getRemoteRevision(in String address); int getRemoteClass(in String address); String getRemoteManufacturer(in String address); String getRemoteCompany(in String address); - String getRemoteMajorClass(in String address); - String getRemoteMinorClass(in String address); - String[] getRemoteServiceClasses(in String address); boolean getRemoteServiceChannel(in String address, int uuid16, in IBluetoothDeviceCallback callback); byte[] getRemoteFeatures(in String adddress); String lastSeen(in String address); diff --git a/core/java/android/content/AsyncQueryHandler.java b/core/java/android/content/AsyncQueryHandler.java index 2d651a7..ac851cc 100644 --- a/core/java/android/content/AsyncQueryHandler.java +++ b/core/java/android/content/AsyncQueryHandler.java @@ -146,6 +146,20 @@ public abstract class AsyncQueryHandler extends Handler { * @param token A token passed into {@link #onQueryComplete} to identify * the query. * @param cookie An object that gets passed into {@link #onQueryComplete} + * @param uri The URI, using the content:// scheme, for the content to + * retrieve. + * @param projection A list of which columns to return. Passing null will + * return all columns, which is discouraged to prevent reading data + * from storage that isn't going to be used. + * @param selection A filter declaring which rows to return, formatted as an + * SQL WHERE clause (excluding the WHERE itself). Passing null will + * return all rows for the given URI. + * @param selectionArgs You may include ?s in selection, which will be + * replaced by the values from selectionArgs, in the order that they + * appear in the selection. The values will be bound as Strings. + * @param orderBy How to order the rows, formatted as an SQL ORDER BY + * clause (excluding the ORDER BY itself). Passing null will use the + * default sort order, which may be unordered. */ public void startQuery(int token, Object cookie, Uri uri, String[] projection, String selection, String[] selectionArgs, diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java index cd92002..ee08eea 100644 --- a/core/java/android/content/BroadcastReceiver.java +++ b/core/java/android/content/BroadcastReceiver.java @@ -16,7 +16,11 @@ package android.content; +import android.app.ActivityManagerNative; +import android.app.IActivityManager; import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; import android.util.Log; /** @@ -175,7 +179,9 @@ public abstract class BroadcastReceiver { * return a result to you asynchronously -- in particular, for interacting * with services, you should use * {@link Context#startService(Intent)} instead of - * {@link Context#bindService(Intent, ServiceConnection, int)}. + * {@link Context#bindService(Intent, ServiceConnection, int)}. If you wish + * to interact with a service that is already running, you can use + * {@link #peekService}. * * @param context The Context in which the receiver is running. * @param intent The Intent being received. @@ -183,6 +189,26 @@ public abstract class BroadcastReceiver { public abstract void onReceive(Context context, Intent intent); /** + * Provide a binder to an already-running service. This method is synchronous + * and will not start the target service if it is not present, so it is safe + * to call from {@link #onReceive}. + * + * @param myContext The Context that had been passed to {@link #onReceive(Context, Intent)} + * @param service The Intent indicating the service you wish to use. See {@link + * Context#startService(Intent)} for more information. + */ + public IBinder peekService(Context myContext, Intent service) { + IActivityManager am = ActivityManagerNative.getDefault(); + IBinder binder = null; + try { + binder = am.peekService(service, service.resolveTypeIfNeeded( + myContext.getContentResolver())); + } catch (RemoteException e) { + } + return binder; + } + + /** * Change the current result code of this broadcast; only works with * broadcasts sent through * {@link Context#sendOrderedBroadcast(Intent, String) diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 3908aa1..e0fe533 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -127,7 +127,7 @@ public abstract class Context { * current process. */ public abstract Context getApplicationContext(); - + /** * Return a localized, styled CharSequence from the application's package's * default string table. @@ -428,7 +428,7 @@ public abstract class Context { * cursor when query is called. * * @return The contents of a newly created database with the given name. - * @throws SQLiteException if the database file could not be opened. + * @throws android.database.sqlite.SQLiteException if the database file could not be opened. * * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE @@ -1064,7 +1064,7 @@ public abstract class Context { * @see #AUDIO_SERVICE * @see android.media.AudioManager * @see #TELEPHONY_SERVICE - * @see android.internal.TelephonyManager + * @see android.telephony.TelephonyManager * @see #INPUT_METHOD_SERVICE * @see android.view.inputmethod.InputMethodManager */ diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e2d3576..52aae0d 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -504,6 +504,8 @@ import java.util.Set; * <li> {@link #ACTION_PACKAGE_ADDED} * <li> {@link #ACTION_PACKAGE_CHANGED} * <li> {@link #ACTION_PACKAGE_REMOVED} + * <li> {@link #ACTION_PACKAGE_RESTARTED} + * <li> {@link #ACTION_PACKAGE_DATA_CLEARED} * <li> {@link #ACTION_UID_REMOVED} * <li> {@link #ACTION_BATTERY_CHANGED} * </ul> @@ -1107,6 +1109,10 @@ public class Intent implements Parcelable { /** * Broadcast Action: A new application package has been installed on the * device. The data contains the name of the package. + * <p>My include the following extras: + * <ul> + * <li> {@link #EXTRA_UID} containing the integer uid assigned to the new package. + * </ul> */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED"; @@ -1114,23 +1120,49 @@ public class Intent implements Parcelable { * Broadcast Action: An existing application package has been removed from * the device. The data contains the name of the package. The package * that is being installed does <em>not</em> receive this Intent. + * <ul> + * <li> {@link #EXTRA_UID} containing the integer uid previously assigned + * to the package. + * <li> {@link #EXTRA_DATA_REMOVED} is set to true if the entire + * application -- data and code -- is being removed. + * <li> {@link #EXTRA_REPLACING} is set to true if this will be followed + * by an {@link #ACTION_PACKAGE_ADDED} broadcast for the same package. + * </ul> */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED"; /** * Broadcast Action: An existing application package has been changed (e.g. a component has been * enabled or disabled. The data contains the name of the package. + * <ul> + * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. + * </ul> */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED"; /** - * Broadcast Action: The user has restarted a package, all runtime state + * Broadcast Action: The user has restarted a package, and all of its + * processes have been killed. All runtime state * associated with it (processes, alarms, notifications, etc) should - * be remove. The data contains the name of the package. + * be removed. The data contains the name of the package. + * <ul> + * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. + * </ul> */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED"; /** + * Broadcast Action: The user has cleared the data of a package. This should + * be preceded by {@link #ACTION_PACKAGE_RESTARTED}, after which all of + * its persistent data is erased and this broadcast sent. The data contains + * the name of the package. + * <ul> + * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. + * </ul> + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED"; + /** * Broadcast Action: A user ID has been removed from the system. The user * ID number is stored in the extra data under {@link #EXTRA_UID}. */ @@ -1227,7 +1259,6 @@ public class Intent implements Parcelable { /** * Broadcast Action: External media is present, and being disk-checked * The path to the mount point for the checking media is contained in the Intent.mData field. - * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MEDIA_CHECKING = "android.intent.action.MEDIA_CHECKING"; @@ -1235,7 +1266,6 @@ public class Intent implements Parcelable { /** * Broadcast Action: External media is present, but is using an incompatible fs (or is blank) * The path to the mount point for the checking media is contained in the Intent.mData field. - * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MEDIA_NOFS = "android.intent.action.MEDIA_NOFS"; @@ -1656,6 +1686,22 @@ public class Intent implements Parcelable { public static final String EXTRA_UID = "android.intent.extra.UID"; /** + * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED} + * intents to indicate whether this represents a full uninstall (removing + * both the code and its data) or a partial uninstall (leaving its data, + * implying that this is an update). + */ + public static final String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED"; + + /** + * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED} + * intents to indicate that this is a replacement of the package, so this + * broadcast will immediately be followed by an add broadcast for a + * different version of the same package. + */ + public static final String EXTRA_REPLACING = "android.intent.extra.REPLACING"; + + /** * Used as an int extra field in {@link android.app.AlarmManager} intents * to tell the application being invoked how many pending alarms are being * delievered with the intent. For one-shot alarms this will always be 1. @@ -1770,8 +1816,8 @@ public class Intent implements Parcelable { * Intent, resulting in the stack now being: A, B. * * <p>The currently running instance of task B in the above example will - * either receiving the new intent you are starting here in its - * onNewIntent() method, or be itself finished and restarting with the + * either receive the new intent you are starting here in its + * onNewIntent() method, or be itself finished and restarted with the * new intent. If it has declared its launch mode to be "multiple" (the * default) it will be finished and re-created; for all other launch modes * it will receive the Intent in the current instance. @@ -1855,7 +1901,7 @@ public class Intent implements Parcelable { */ public static final int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 0x00080000; /** - * If set, this flag will prevent the normal {@link android.app.Activity#onUserLeaving} + * If set, this flag will prevent the normal {@link android.app.Activity#onUserLeaveHint} * callback from occurring on the current frontmost activity before it is * paused as the newly-started activity is brought to the front. * @@ -1871,12 +1917,39 @@ public class Intent implements Parcelable { * activity does not think the user has acknowledged its notification. */ public static final int FLAG_ACTIVITY_NO_USER_ACTION = 0x00040000; - + /** + * If set in an Intent passed to {@link Context#startActivity Context.startActivity()}, + * this flag will cause the launched activity to be brought to the front of its + * task's history stack if it is already running. + * + * <p>For example, consider a task consisting of four activities: A, B, C, D. + * If D calls startActivity() with an Intent that resolves to the component + * of activity B, then B will be brought to the front of the history stack, + * with this resulting order: A, C, D, B. + * + * This flag will be ignored if {@link #FLAG_ACTIVITY_CLEAR_TOP} is also + * specified. + */ + public static final int FLAG_ACTIVITY_REORDER_TO_FRONT = 0X00020000; /** * If set, when sending a broadcast only registered receivers will be * called -- no BroadcastReceiver components will be launched. */ public static final int FLAG_RECEIVER_REGISTERED_ONLY = 0x40000000; + /** + * If set, when sending a broadcast <i>before boot has completed</i> only + * registered receivers will be called -- no BroadcastReceiver components + * will be launched. Sticky intent state will be recorded properly even + * if no receivers wind up being called. If {@link #FLAG_RECEIVER_REGISTERED_ONLY} + * is specified in the broadcast intent, this flag is unnecessary. + * + * <p>This flag is only for use by system sevices as a convenience to + * avoid having to implement a more complex mechanism around detection + * of boot completion. + * + * @hide + */ + public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 0x20000000; // --------------------------------------------------------------------- diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 6bc3774..96470c3 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -123,7 +123,7 @@ class SyncManager { private static final String SYNC_WAKE_LOCK = "SyncManagerSyncWakeLock"; private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarmWakeLock"; - + private Context mContext; private ContentResolver mContentResolver; @@ -249,7 +249,7 @@ class SyncManager { mSyncQueue = new SyncQueue(mSyncStorageEngine); mContext = context; - + mSyncThread = new HandlerThread("SyncHandlerThread", Process.THREAD_PRIORITY_BACKGROUND); mSyncThread.start(); mSyncHandler = new SyncHandler(mSyncThread.getLooper()); @@ -489,7 +489,7 @@ class SyncManager { // Require the precise value "yes" to discourage accidental activation. return "yes".equals(SystemProperties.get("ro.config.sync")); } - + /** * Initiate a sync. This can start a sync for all providers * (pass null to url, set onlyTicklable to false), only those @@ -515,7 +515,7 @@ class SyncManager { * syncs of a specific provider. Can be null. Is ignored * if the url is null. * @param delay how many milliseconds in the future to wait before performing this - * sync. -1 means to make this the next sync to perform. + * sync. -1 means to make this the next sync to perform. */ public void scheduleSync(Uri url, Bundle extras, long delay) { boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); @@ -694,7 +694,7 @@ class SyncManager { class SyncHandlerMessagePayload { public final ActiveSyncContext activeSyncContext; public final SyncResult syncResult; - + SyncHandlerMessagePayload(ActiveSyncContext syncContext, SyncResult syncResult) { this.activeSyncContext = syncContext; this.syncResult = syncResult; @@ -740,7 +740,7 @@ class SyncManager { if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) { newDelayInMs = maxSyncRetryTimeInSeconds * 1000; } - + SyncOperation rescheduledSyncOperation = new SyncOperation(syncOperation); rescheduledSyncOperation.setDelay(newDelayInMs); scheduleSyncOperation(rescheduledSyncOperation); @@ -786,7 +786,7 @@ class SyncManager { // key than the one we are scheduling. if (!activeIsExpedited && !hasSameKey) { rescheduleImmediately(activeSyncContext.mSyncOperation); - sendSyncFinishedOrCanceledMessage(activeSyncContext, + sendSyncFinishedOrCanceledMessage(activeSyncContext, null /* no result since this is a cancel */); } } @@ -1323,7 +1323,7 @@ class SyncManager { public SyncHandler(Looper looper) { super(looper); } - + public void handleMessage(Message msg) { handleSyncHandlerMessage(msg); } @@ -1462,6 +1462,9 @@ class SyncManager { // start it, otherwise just get out. SyncOperation syncOperation; final Sync.Settings.QueryMap syncSettings = getSyncSettings(); + final ConnectivityManager connManager = (ConnectivityManager) + mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + final boolean backgroundDataSetting = connManager.getBackgroundDataSetting(); synchronized (mSyncQueue) { while (true) { syncOperation = mSyncQueue.head(); @@ -1484,10 +1487,10 @@ class SyncManager { // skip the sync if it isn't a force and the settings are off for this provider final boolean force = syncOperation.extras.getBoolean( ContentResolver.SYNC_EXTRAS_FORCE, false); - if (!force && (!syncSettings.getBackgroundData() + if (!force && (!backgroundDataSetting || !syncSettings.getListenForNetworkTickles() || !syncSettings.getSyncProviderAutomatically( - syncOperation.authority))) { + syncOperation.authority))) { if (isLoggable) { Log.v(TAG, "runStateIdle: sync off, dropping " + syncOperation); } @@ -1669,7 +1672,7 @@ class SyncManager { * @param syncResult the SyncResult from which to read * @return the most "serious" error set in the SyncResult * @throws IllegalStateException if the SyncResult does not indicate any errors. - * If SyncResult.error() is true then it is safe to call this. + * If SyncResult.error() is true then it is safe to call this. */ private int syncResultToErrorNumber(SyncResult syncResult) { if (syncResult.syncAlreadyInProgress) return History.ERROR_SYNC_ALREADY_IN_PROGRESS; @@ -1679,7 +1682,8 @@ class SyncManager { if (syncResult.stats.numConflictDetectedExceptions > 0) return History.ERROR_CONFLICT; if (syncResult.tooManyDeletions) return History.ERROR_TOO_MANY_DELETIONS; if (syncResult.tooManyRetries) return History.ERROR_TOO_MANY_RETRIES; - throw new IllegalStateException("we are not in an error state, " + toString()); + if (syncResult.databaseError) return History.ERROR_INTERNAL; + throw new IllegalStateException("we are not in an error state, " + syncResult); } private void manageSyncNotification() { @@ -1717,7 +1721,7 @@ class SyncManager { if (mSyncNotificationInfo.isActive) { shouldInstall = shouldCancel; } else { - final boolean timeToShowNotification = + final boolean timeToShowNotification = now > mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY; final boolean syncIsForced = syncOperation.extras .getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false); @@ -1769,7 +1773,7 @@ class SyncManager { if (!mDataConnectionIsConnected) return; if (mAccounts == null) return; if (mStorageIsLow) return; - + // Compute the alarm fire time: // - not syncing: time of the next sync operation // - syncing, no notification: time from sync start to notification create time @@ -1850,12 +1854,12 @@ class SyncManager { clickIntent.putExtra("account", account); clickIntent.putExtra("provider", authority); clickIntent.putExtra("numDeletes", numDeletes); - + if (!isActivityAvailable(clickIntent)) { Log.w(TAG, "No activity found to handle too many deletes."); return; } - + final PendingIntent pendingIntent = PendingIntent .getActivity(mContext, 0, clickIntent, PendingIntent.FLAG_CANCEL_CURRENT); @@ -1877,7 +1881,7 @@ class SyncManager { /** * Checks whether an activity exists on the system image for the given intent. - * + * * @param intent The intent for an activity. * @return Whether or not an activity exists. */ @@ -1892,10 +1896,10 @@ class SyncManager { return true; } } - + return false; } - + public long insertStartSyncEvent(SyncOperation syncOperation) { final int source = syncOperation.syncSource; final long now = System.currentTimeMillis(); diff --git a/core/java/android/content/TempProviderSyncAdapter.java b/core/java/android/content/TempProviderSyncAdapter.java index 78510aa..eb3a5da 100644 --- a/core/java/android/content/TempProviderSyncAdapter.java +++ b/core/java/android/content/TempProviderSyncAdapter.java @@ -1,11 +1,11 @@ package android.content; -import com.google.android.net.NetStats; - import android.database.SQLException; import android.os.Bundle; import android.os.Debug; +import android.os.NetStat; import android.os.Parcelable; +import android.os.Process; import android.os.SystemProperties; import android.text.TextUtils; import android.util.Config; @@ -177,7 +177,8 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter { private final Bundle mExtras; private final SyncContext mSyncContext; private volatile boolean mIsCanceled = false; - private long[] mNetStats; + private long mInitialTxBytes; + private long mInitialRxBytes; private final SyncResult mResult; SyncThread(SyncContext syncContext, String account, Bundle extras) { @@ -193,15 +194,18 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter { if (mAdapterSyncStarted) onSyncCanceled(); if (mProviderSyncStarted) mProvider.onSyncCanceled(); // We may lose the last few sync events when canceling. Oh well. - long[] newNetStats = NetStats.getStats(); - logSyncDetails(newNetStats[0] - mNetStats[0], newNetStats[1] - mNetStats[1], mResult); + int uid = Process.myUid(); + logSyncDetails(NetStat.getUidTxBytes(uid) - mInitialTxBytes, + NetStat.getUidRxBytes(uid) - mInitialRxBytes, mResult); } @Override public void run() { - android.os.Process.setThreadPriority(android.os.Process.myTid(), - android.os.Process.THREAD_PRIORITY_BACKGROUND); - mNetStats = NetStats.getStats(); + Process.setThreadPriority(Process.myTid(), + Process.THREAD_PRIORITY_BACKGROUND); + int uid = Process.myUid(); + mInitialTxBytes = NetStat.getUidTxBytes(uid); + mInitialRxBytes = NetStat.getUidRxBytes(uid); try { sync(mSyncContext, mAccount, mExtras); } catch (SQLException e) { @@ -210,8 +214,8 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter { } finally { mSyncThread = null; if (!mIsCanceled) { - long[] newNetStats = NetStats.getStats(); - logSyncDetails(newNetStats[0] - mNetStats[0], newNetStats[1] - mNetStats[1], mResult); + logSyncDetails(NetStat.getUidTxBytes(uid) - mInitialTxBytes, + NetStat.getUidRxBytes(uid) - mInitialRxBytes, mResult); mSyncContext.onFinished(mResult); } } diff --git a/core/java/android/content/pm/ConfigurationInfo.java b/core/java/android/content/pm/ConfigurationInfo.java index 9115225..dcc7463 100755 --- a/core/java/android/content/pm/ConfigurationInfo.java +++ b/core/java/android/content/pm/ConfigurationInfo.java @@ -16,7 +16,6 @@ package android.content.pm; -import android.content.res.Configuration; import android.os.Parcel; import android.os.Parcelable; @@ -60,12 +59,12 @@ public class ConfigurationInfo implements Parcelable { /** * Value for {@link #reqInputFeatures}: if set, indicates that the application - * requires a hard keyboard + * requires a five way navigation device */ public static final int INPUT_FEATURE_FIVE_WAY_NAV = 0x00000002; /** - * Flags associated with the application. Any combination of + * Flags associated with the input features. Any combination of * {@link #INPUT_FEATURE_HARD_KEYBOARD}, * {@link #INPUT_FEATURE_FIVE_WAY_NAV} */ diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index ea86188..d3f6f3c 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -255,8 +255,15 @@ interface IPackageManager { * retrieval of information is complete. */ void getPackageSizeInfo(in String packageName, IPackageStatsObserver observer); + + /** + * Get a list of shared libraries that are available on the + * system. + */ + String[] getSystemSharedLibraryNames(); void enterSafeMode(); + boolean isSafeMode(); void systemReady(); boolean hasSystemUidErrors(); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 4b902e9..698f27f 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -851,6 +851,16 @@ public abstract class PackageManager { * @see #GET_UNINSTALLED_PACKAGES */ public abstract List<ApplicationInfo> getInstalledApplications(int flags); + + /** + * Get a list of shared libraries that are available on the + * system. + * + * @return An array of shared library names that are + * available on the system, or null if none are installed. + * + */ + public abstract String[] getSystemSharedLibraryNames(); /** * Determine the best action to perform for a given Intent. This is how @@ -1608,4 +1618,9 @@ public abstract class PackageManager { * the manifest as found in {@link ComponentInfo}. */ public abstract int getApplicationEnabledSetting(String packageName); + + /** + * Return whether the device has been booted into safe mode. + */ + public abstract boolean isSafeMode(); } diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java index 4b1e678..17cb687 100644 --- a/core/java/android/content/res/ColorStateList.java +++ b/core/java/android/content/res/ColorStateList.java @@ -304,6 +304,11 @@ public class ColorStateList implements Parcelable { } public void writeToParcel(Parcel dest, int flags) { + final int N = mStateSpecs.length; + dest.writeInt(N); + for (int i=0; i<N; i++) { + dest.writeIntArray(mStateSpecs[i]); + } dest.writeArray(mStateSpecs); dest.writeIntArray(mColors); } @@ -315,14 +320,11 @@ public class ColorStateList implements Parcelable { } public ColorStateList createFromParcel(Parcel source) { - Object[] o = source.readArray( - ColorStateList.class.getClassLoader()); - int[][] stateSpecs = new int[o.length][]; - - for (int i = 0; i < o.length; i++) { - stateSpecs[i] = (int[]) o[i]; + final int N = source.readInt(); + int[][] stateSpecs = new int[N][]; + for (int i=0; i<N; i++) { + stateSpecs[i] = source.createIntArray(); } - int[] colors = source.createIntArray(); return new ColorStateList(stateSpecs, colors); } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 10eced6..5a0daea 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -22,7 +22,6 @@ import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import android.content.Intent; import android.graphics.Movie; import android.graphics.drawable.Drawable; import android.graphics.drawable.ColorDrawable; @@ -399,7 +398,6 @@ public class Resources { * * @throws NotFoundException Throws NotFoundException if the given ID does not exist. * - * @return CharSequence The string data associated with the resource, plus * @see #getDimensionPixelOffset * @see #getDimensionPixelSize */ @@ -432,7 +430,6 @@ public class Resources { * * @throws NotFoundException Throws NotFoundException if the given ID does not exist. * - * @return CharSequence The string data associated with the resource, plus * @see #getDimension * @see #getDimensionPixelSize */ @@ -467,7 +464,6 @@ public class Resources { * * @throws NotFoundException Throws NotFoundException if the given ID does not exist. * - * @return CharSequence The string data associated with the resource, plus * @see #getDimension * @see #getDimensionPixelOffset */ @@ -486,6 +482,36 @@ public class Resources { } /** + * Retrieve a fractional unit for a particular resource ID. + * + * @param id The desired resource identifier, as generated by the aapt + * tool. This integer encodes the package, type, and resource + * entry. The value 0 is an invalid identifier. + * @param base The base value of this fraction. In other words, a + * standard fraction is multiplied by this value. + * @param pbase The parent base value of this fraction. In other + * words, a parent fraction (nn%p) is multiplied by this + * value. + * + * @return Attribute fractional value multiplied by the appropriate + * base value. + * + * @throws NotFoundException Throws NotFoundException if the given ID does not exist. + */ + public float getFraction(int id, int base, int pbase) { + synchronized (mTmpValue) { + TypedValue value = mTmpValue; + getValue(id, value, true); + if (value.type == TypedValue.TYPE_FRACTION) { + return TypedValue.complexToFraction(value.data, base, pbase); + } + throw new NotFoundException( + "Resource ID #0x" + Integer.toHexString(id) + " type #0x" + + Integer.toHexString(value.type) + " is not valid"); + } + } + + /** * Return a drawable object associated with a particular resource ID. * Various types of objects will be returned depending on the underlying * resource -- for example, a solid color, PNG image, scalable image, etc. @@ -721,22 +747,36 @@ public class Resources { */ public InputStream openRawResource(int id) throws NotFoundException { synchronized (mTmpValue) { - TypedValue value = mTmpValue; - getValue(id, value, true); + return openRawResource(id, mTmpValue); + } + } - try { - return mAssets.openNonAsset( - value.assetCookie, value.string.toString(), - AssetManager.ACCESS_STREAMING); - } catch (Exception e) { - NotFoundException rnf = new NotFoundException( - "File " + value.string.toString() - + " from drawable resource ID #0x" - + Integer.toHexString(id)); - rnf.initCause(e); - throw rnf; - } + /** + * Open a data stream for reading a raw resource. This can only be used + * with resources whose value is the name of an asset files -- that is, it can be + * used to open drawable, sound, and raw resources; it will fail on string + * and color resources. + * + * @param id The resource identifier to open, as generated by the appt tool. + * @param value The TypedValue object to hold the resource information. + * + * @return InputStream Access to the resource data. + * + * @throws NotFoundException Throws NotFoundException if the given ID does not exist. + * + * @hide Pending API council approval + */ + public InputStream openRawResource(int id, TypedValue value) throws NotFoundException { + getValue(id, value, true); + try { + return mAssets.openNonAsset(value.assetCookie, value.string.toString(), + AssetManager.ACCESS_STREAMING); + } catch (Exception e) { + NotFoundException rnf = new NotFoundException("File " + value.string.toString() + + " from drawable resource ID #0x" + Integer.toHexString(id)); + rnf.initCause(e); + throw rnf; } } @@ -1588,7 +1628,7 @@ public class Resources { InputStream is = mAssets.openNonAsset( value.assetCookie, file, AssetManager.ACCESS_BUFFER); // System.out.println("Opened file " + file + ": " + is); - dr = Drawable.createFromStream(is, file); + dr = Drawable.createFromResourceStream(this, value, is, file); is.close(); // System.out.println("Created stream: " + dr); } catch (Exception e) { diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index fa062c8..87bb277 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -25,6 +25,7 @@ import android.os.SystemClock; import android.text.TextUtils; import android.util.Config; import android.util.Log; +import android.util.EventLog; import java.io.File; import java.util.HashMap; @@ -51,7 +52,8 @@ import java.util.concurrent.locks.ReentrantLock; * is the Unicode Collation Algorithm and not tailored to the current locale. */ public class SQLiteDatabase extends SQLiteClosable { - private final static String TAG = "Database"; + private static final String TAG = "Database"; + private static final int DB_OPERATION_EVENT = 52000; /** * Algorithms used in ON CONFLICT clause @@ -207,6 +209,11 @@ public class SQLiteDatabase extends SQLiteClosable { private WeakHashMap<SQLiteClosable, Object> mPrograms; private final RuntimeException mLeakedException; + + // package visible, since callers will access directly to minimize overhead in the case + // that logging is not enabled. + /* package */ final boolean mLogStats; + /** * @param closable */ @@ -436,7 +443,14 @@ public class SQLiteDatabase extends SQLiteClosable { if (mTransactionIsSuccessful) { execSQL("COMMIT;"); } else { - execSQL("ROLLBACK;"); + try { + execSQL("ROLLBACK;"); + } catch (SQLException e) { + if (Config.LOGD) { + Log.d(TAG, "exception during rollback, maybe the DB previously " + + "performed an auto-rollback"); + } + } } } finally { unlockForced(); @@ -1472,10 +1486,8 @@ public class SQLiteDatabase extends SQLiteClosable { * @throws SQLException If the SQL string is invalid for some reason */ public void execSQL(String sql) throws SQLException { - long timeStart = 0; - if (Config.LOGV) { - timeStart = System.currentTimeMillis(); - } + boolean logStats = mLogStats; + long timeStart = logStats ? SystemClock.elapsedRealtime() : 0; lock(); try { native_execSQL(sql); @@ -1485,9 +1497,8 @@ public class SQLiteDatabase extends SQLiteClosable { } finally { unlock(); } - if (Config.LOGV) { - long timeEnd = System.currentTimeMillis(); - Log.v(TAG, "Executed (" + (timeEnd - timeStart) + " ms):" + sql); + if (logStats) { + logTimeStat(false /* not a read */, timeStart, SystemClock.elapsedRealtime()); } } @@ -1504,10 +1515,9 @@ public class SQLiteDatabase extends SQLiteClosable { if (bindArgs == null) { throw new IllegalArgumentException("Empty bindArgs"); } - long timeStart = 0; - if (Config.LOGV) { - timeStart = System.currentTimeMillis(); - } + + boolean logStats = mLogStats; + long timeStart = logStats ? SystemClock.elapsedRealtime() : 0; lock(); SQLiteStatement statement = null; try { @@ -1528,9 +1538,8 @@ public class SQLiteDatabase extends SQLiteClosable { } unlock(); } - if (Config.LOGV) { - long timeEnd = System.currentTimeMillis(); - Log.v(TAG, "Executed (" + (timeEnd - timeStart) + " ms):" + sql); + if (logStats) { + logTimeStat(false /* not a read */, timeStart, SystemClock.elapsedRealtime()); } } @@ -1550,7 +1559,7 @@ public class SQLiteDatabase extends SQLiteClosable { } /** - * Private constructor. See {@link createDatabase} and {@link openDatabase}. + * Private constructor. See {@link #create} and {@link #openDatabase}. * * @param path The full path to the database * @param factory The factory to use when creating cursors, may be NULL. @@ -1563,6 +1572,8 @@ public class SQLiteDatabase extends SQLiteClosable { } mFlags = flags; mPath = path; + mLogStats = "1".equals(android.os.SystemProperties.get("db.logstats")); + mLeakedException = new IllegalStateException(path + " SQLiteDatabase created and never closed"); mFactory = factory; @@ -1605,6 +1616,10 @@ public class SQLiteDatabase extends SQLiteClosable { return mPath; } + /* package */ void logTimeStat(boolean read, long begin, long end) { + EventLog.writeEvent(DB_OPERATION_EVENT, mPath, read ? 0 : 1, end - begin); + } + /** * Sets the locale for this database. Does nothing if this database has * the NO_LOCALIZED_COLLATORS flag set or was opened read only. @@ -1629,7 +1644,7 @@ public class SQLiteDatabase extends SQLiteClosable { private native void dbopen(String path, int flags); /** - * Native call to execute a raw SQL statement. {@link mLock} must be held + * Native call to execute a raw SQL statement. {@link #lock} must be held * when calling this method. * * @param sql The raw SQL string @@ -1638,7 +1653,7 @@ public class SQLiteDatabase extends SQLiteClosable { /* package */ native void native_execSQL(String sql) throws SQLException; /** - * Native call to set the locale. {@link mLock} must be held when calling + * Native call to set the locale. {@link #lock} must be held when calling * this method. * @throws SQLException */ diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java index 35bf645..52aac3a 100644 --- a/core/java/android/database/sqlite/SQLiteOpenHelper.java +++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java @@ -26,6 +26,8 @@ import android.util.Log; * optionally {@link #onOpen}, and this class takes care of opening the database * if it exists, creating it if it does not, and upgrading it as necessary. * Transactions are used to make sure the database is always in a sensible state. + * <p>For an example, see the NotePadProvider class in the NotePad sample application, + * in the <em>samples/</em> directory of the SDK.</p> */ public abstract class SQLiteOpenHelper { private static final String TAG = SQLiteOpenHelper.class.getSimpleName(); diff --git a/core/java/android/database/sqlite/SQLiteQuery.java b/core/java/android/database/sqlite/SQLiteQuery.java index 22c53ab..5bfa0e8 100644 --- a/core/java/android/database/sqlite/SQLiteQuery.java +++ b/core/java/android/database/sqlite/SQLiteQuery.java @@ -17,6 +17,7 @@ package android.database.sqlite; import android.database.CursorWindow; +import android.os.SystemClock; /** * A SQLite program that represents a query that reads the resulting rows into a CursorWindow. @@ -55,12 +56,14 @@ public class SQLiteQuery extends SQLiteProgram { * Reads rows into a buffer. This method acquires the database lock. * * @param window The window to fill into - * @param startPos The position to start reading rows from * @return number of total rows in the query */ /* package */ int fillWindow(CursorWindow window, int maxRead, int lastPos) { mDatabase.lock(); + + boolean logStats = mDatabase.mLogStats; + long startTime = logStats ? SystemClock.elapsedRealtime() : 0; try { acquireReference(); try { @@ -68,8 +71,13 @@ public class SQLiteQuery extends SQLiteProgram { // if the start pos is not equal to 0, then most likely window is // too small for the data set, loading by another thread // is not safe in this situation. the native code will ignore maxRead - return native_fill_window(window, window.getStartPosition(), mOffsetIndex, + int numRows = native_fill_window(window, window.getStartPosition(), mOffsetIndex, maxRead, lastPos); + if (logStats) { + mDatabase.logTimeStat(true /* read */, startTime, + SystemClock.elapsedRealtime()); + } + return numRows; } catch (IllegalStateException e){ // simply ignore it return 0; diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java index bf9361d..d169259 100644 --- a/core/java/android/database/sqlite/SQLiteStatement.java +++ b/core/java/android/database/sqlite/SQLiteStatement.java @@ -16,6 +16,8 @@ package android.database.sqlite; +import android.os.SystemClock; + /** * A pre-compiled statement against a {@link SQLiteDatabase} that can be reused. * The statement cannot return multiple rows, but 1x1 result sets are allowed. @@ -43,10 +45,16 @@ public class SQLiteStatement extends SQLiteProgram */ public void execute() { mDatabase.lock(); + boolean logStats = mDatabase.mLogStats; + long startTime = logStats ? SystemClock.elapsedRealtime() : 0; + acquireReference(); try { native_execute(); - } finally { + if (logStats) { + mDatabase.logTimeStat(false /* write */, startTime, SystemClock.elapsedRealtime()); + } + } finally { releaseReference(); mDatabase.unlock(); } @@ -64,9 +72,15 @@ public class SQLiteStatement extends SQLiteProgram */ public long executeInsert() { mDatabase.lock(); + boolean logStats = mDatabase.mLogStats; + long startTime = logStats ? SystemClock.elapsedRealtime() : 0; + acquireReference(); try { native_execute(); + if (logStats) { + mDatabase.logTimeStat(false /* write */, startTime, SystemClock.elapsedRealtime()); + } return mDatabase.lastInsertRow(); } finally { releaseReference(); @@ -84,9 +98,16 @@ public class SQLiteStatement extends SQLiteProgram */ public long simpleQueryForLong() { mDatabase.lock(); + boolean logStats = mDatabase.mLogStats; + long startTime = logStats ? SystemClock.elapsedRealtime() : 0; + acquireReference(); try { - return native_1x1_long(); + long retValue = native_1x1_long(); + if (logStats) { + mDatabase.logTimeStat(false /* write */, startTime, SystemClock.elapsedRealtime()); + } + return retValue; } finally { releaseReference(); mDatabase.unlock(); @@ -103,9 +124,16 @@ public class SQLiteStatement extends SQLiteProgram */ public String simpleQueryForString() { mDatabase.lock(); + boolean logStats = mDatabase.mLogStats; + long startTime = logStats ? SystemClock.elapsedRealtime() : 0; + acquireReference(); try { - return native_1x1_string(); + String retValue = native_1x1_string(); + if (logStats) { + mDatabase.logTimeStat(false /* write */, startTime, SystemClock.elapsedRealtime()); + } + return retValue; } finally { releaseReference(); mDatabase.unlock(); diff --git a/core/java/android/database/sqlite/package.html b/core/java/android/database/sqlite/package.html index c03a8dc..ff0f9f5 100644 --- a/core/java/android/database/sqlite/package.html +++ b/core/java/android/database/sqlite/package.html @@ -6,7 +6,7 @@ classes that an application would use to manage its own private database. Applications use these classes to maange private databases. If creating a content provider, you will probably have to use these classes to create and manage your own database to store content. See <a -href="{@docRoot}devel/data.html">Storing, Retrieving and Exposing Data</a> to learn +href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a> to learn the conventions for implementing a content provider. See the NotePadProvider class in the NotePad sample application in the SDK for an example of a content provider. Android ships with SQLite version 3.4.0 diff --git a/core/java/android/gadget/GadgetHost.java b/core/java/android/gadget/GadgetHost.java index 418f2aa..9176d18 100644 --- a/core/java/android/gadget/GadgetHost.java +++ b/core/java/android/gadget/GadgetHost.java @@ -17,14 +17,67 @@ package android.gadget; import android.content.Context; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; import android.widget.RemoteViews; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import com.android.internal.gadget.IGadgetHost; +import com.android.internal.gadget.IGadgetService; + /** * GadgetHost provides the interaction with the Gadget Service for apps, * like the home screen, that want to embed gadgets in their UI. */ public class GadgetHost { + + static final int HANDLE_UPDATE = 1; + + static Object sServiceLock = new Object(); + static IGadgetService sService; + + Context mContext; + String mPackageName; + + class Callbacks extends IGadgetHost.Stub { + public void updateGadget(int gadgetId, RemoteViews views) { + Message msg = mHandler.obtainMessage(HANDLE_UPDATE); + msg.arg1 = gadgetId; + msg.obj = views; + msg.sendToTarget(); + } + } + + Handler mHandler = new Handler() { + public void handleMessage(Message msg) { + switch (msg.what) { + case HANDLE_UPDATE: { + updateGadgetView(msg.arg1, (RemoteViews)msg.obj); + break; + } + } + } + }; + + int mHostId; + Callbacks mCallbacks = new Callbacks(); + HashMap<Integer,GadgetHostView> mViews = new HashMap(); + public GadgetHost(Context context, int hostId) { + mContext = context; + mHostId = hostId; + synchronized (sServiceLock) { + if (sService == null) { + IBinder b = ServiceManager.getService(Context.GADGET_SERVICE); + sService = IGadgetService.Stub.asInterface(b); + } + } } /** @@ -32,6 +85,23 @@ public class GadgetHost { * becomes visible, i.e. from onStart() in your Activity. */ public void startListening() { + int[] updatedIds = null; + ArrayList<RemoteViews> updatedViews = new ArrayList(); + + try { + if (mPackageName == null) { + mPackageName = mContext.getPackageName(); + } + updatedIds = sService.startListening(mCallbacks, mPackageName, mHostId, updatedViews); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + + final int N = updatedIds.length; + for (int i=0; i<N; i++) { + updateGadgetView(updatedIds[i], updatedViews.get(i)); + } } /** @@ -39,25 +109,93 @@ public class GadgetHost { * no longer visible, i.e. from onStop() in your Activity. */ public void stopListening() { + try { + sService.stopListening(mHostId); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } } /** - * Stop listening to changes for this gadget. + * Get a gadgetId for a host in the calling process. + * + * @return a gadgetId */ - public void gadgetRemoved(int gadgetId) { + public int allocateGadgetId() { + try { + if (mPackageName == null) { + mPackageName = mContext.getPackageName(); + } + return sService.allocateGadgetId(mPackageName, mHostId); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } } /** - * Remove all records about gadget instances from the gadget manager. Call this when - * initializing your database, as it might be because of a data wipe. + * Stop listening to changes for this gadget. */ - public void clearGadgets() { + public void deleteGadgetId(int gadgetId) { + synchronized (mViews) { + mViews.remove(gadgetId); + try { + sService.deleteGadgetId(gadgetId); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + } + + /** + * Remove all records about this host from the gadget manager. + * <ul> + * <li>Call this when initializing your database, as it might be because of a data wipe.</li> + * <li>Call this to have the gadget manager release all resources associated with your + * host. Any future calls about this host will cause the records to be re-allocated.</li> + * </ul> + */ + public void deleteHost() { + try { + sService.deleteHost(mHostId); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + + /** + * Remove all records about all hosts for your package. + * <ul> + * <li>Call this when initializing your database, as it might be because of a data wipe.</li> + * <li>Call this to have the gadget manager release all resources associated with your + * host. Any future calls about this host will cause the records to be re-allocated.</li> + * </ul> + */ + public static void deleteAllHosts() { + try { + sService.deleteAllHosts(); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } } public final GadgetHostView createView(Context context, int gadgetId, GadgetInfo gadget) { GadgetHostView view = onCreateView(context, gadgetId, gadget); view.setGadget(gadgetId, gadget); - view.updateGadget(null); + synchronized (mViews) { + mViews.put(gadgetId, view); + } + RemoteViews views = null; + try { + views = sService.getGadgetViews(gadgetId); + } catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + view.updateGadget(views); return view; } @@ -68,5 +206,16 @@ public class GadgetHost { protected GadgetHostView onCreateView(Context context, int gadgetId, GadgetInfo gadget) { return new GadgetHostView(context); } + + void updateGadgetView(int gadgetId, RemoteViews views) { + GadgetHostView v; + synchronized (mViews) { + v = mViews.get(gadgetId); + } + if (v != null) { + v.updateGadget(views); + } + } } + diff --git a/core/java/android/gadget/GadgetHostView.java b/core/java/android/gadget/GadgetHostView.java index e2bef8c..d92c123 100644 --- a/core/java/android/gadget/GadgetHostView.java +++ b/core/java/android/gadget/GadgetHostView.java @@ -19,16 +19,21 @@ package android.gadget; import android.content.Context; import android.content.pm.PackageManager; import android.gadget.GadgetInfo; -import android.util.AttributeSet; +import android.graphics.Color; +import android.util.Config; import android.util.Log; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.RemoteViews; import android.widget.TextView; +import android.widget.ViewAnimator; -public class GadgetHostView extends FrameLayout { +public class GadgetHostView extends ViewAnimator { static final String TAG = "GadgetHostView"; + static final boolean LOGD = Config.LOGD || true; // When we're inflating the initialLayout for a gadget, we only allow // views that are allowed in RemoteViews. @@ -40,63 +45,139 @@ public class GadgetHostView extends FrameLayout { int mGadgetId; GadgetInfo mInfo; - View mContentView; + View mActiveView; + View mStaleView; + + protected int mDefaultGravity = Gravity.CENTER; public GadgetHostView(Context context) { super(context); } public void setGadget(int gadgetId, GadgetInfo info) { + if (LOGD) Log.d(TAG, "setGadget is incoming with info=" + info); if (mInfo != null) { // TODO: remove the old view, or whatever } mGadgetId = gadgetId; mInfo = info; + + View defaultView = getDefaultView(); + flipUpdate(defaultView); + } + + /** + * Trigger actual animation between current and new content in the + * {@link ViewAnimator}. + */ + protected void flipUpdate(View newContent) { + if (LOGD) Log.d(TAG, "pushing an update to surface"); + + // Take requested dimensions from parent, but apply default gravity. + ViewGroup.LayoutParams requested = newContent.getLayoutParams(); + if (requested == null) { + requested = new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT, + LayoutParams.FILL_PARENT); + } + + FrameLayout.LayoutParams params = + new FrameLayout.LayoutParams(requested.width, requested.height); + params.gravity = mDefaultGravity; + newContent.setLayoutParams(params); + + // Add new content and animate to it + addView(newContent); + showNext(); + + // Dispose old stale view + removeView(mStaleView); + mStaleView = mActiveView; + mActiveView = newContent; } + /** + * Process a set of {@link RemoteViews} coming in as an update from the + * gadget provider. Will animate into these new views as needed. + */ public void updateGadget(RemoteViews remoteViews) { - Context context = getContext(); - - View contentView = null; + if (LOGD) Log.d(TAG, "updateGadget() with remoteViews = " + remoteViews); + + View newContent = null; Exception exception = null; + try { if (remoteViews == null) { // there is no remoteViews (yet), so use the initial layout - Context theirContext = context.createPackageContext(mInfo.provider.getPackageName(), - 0); - LayoutInflater inflater = (LayoutInflater)theirContext.getSystemService( - Context.LAYOUT_INFLATER_SERVICE); - inflater = inflater.cloneInContext(theirContext); - inflater.setFilter(sInflaterFilter); - contentView = inflater.inflate(mInfo.initialLayout, this, false); + newContent = getDefaultView(); } else { // use the RemoteViews - contentView = remoteViews.apply(mContext, this); + // TODO: try applying RemoteViews to existing staleView if available + newContent = remoteViews.apply(mContext, this); } - } - catch (PackageManager.NameNotFoundException e) { + } catch (RuntimeException e) { exception = e; } - catch (RuntimeException e) { - exception = e; - } - if (contentView == null) { + + if (exception != null && LOGD) { Log.w(TAG, "Error inflating gadget " + mInfo, exception); + } + + if (newContent == null) { // TODO: Should we throw an exception here for the host activity to catch? // Maybe we should show a generic error widget. - TextView tv = new TextView(context); - tv.setText("Error inflating gadget"); - contentView = tv; + if (LOGD) Log.d(TAG, "updateGadget couldn't find any view, so inflating error"); + newContent = getErrorView(); } - FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( - FrameLayout.LayoutParams.WRAP_CONTENT, - FrameLayout.LayoutParams.WRAP_CONTENT); - - mContentView = contentView; - this.addView(contentView, lp); - - // TODO: do an animation (maybe even one provided by the gadget). + flipUpdate(newContent); + } + + /** + * Inflate and return the default layout requested by gadget provider. + */ + protected View getDefaultView() { + View defaultView = null; + Exception exception = null; + + try { + if (mInfo != null) { + Context theirContext = mContext.createPackageContext( + mInfo.provider.getPackageName(), 0 /* no flags */); + LayoutInflater inflater = (LayoutInflater) + theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater = inflater.cloneInContext(theirContext); + inflater.setFilter(sInflaterFilter); + defaultView = inflater.inflate(mInfo.initialLayout, this, false); + } else { + Log.w(TAG, "can't inflate defaultView because mInfo is missing"); + } + } catch (PackageManager.NameNotFoundException e) { + exception = e; + } catch (RuntimeException e) { + exception = e; + } + + if (exception != null && LOGD) { + Log.w(TAG, "Error inflating gadget " + mInfo, exception); + } + + if (defaultView == null) { + if (LOGD) Log.d(TAG, "getDefaultView couldn't find any view, so inflating error"); + defaultView = getErrorView(); + } + + return defaultView; + } + + /** + * Inflate and return a view that represents an error state. + */ + protected View getErrorView() { + TextView tv = new TextView(mContext); + // TODO: move this error string and background color into resources + tv.setText("Error inflating gadget"); + tv.setBackgroundColor(Color.argb(127, 0, 0, 0)); + return tv; } } diff --git a/core/java/android/gadget/GadgetInfo.java b/core/java/android/gadget/GadgetInfo.java index 1a7a9a0..5ac3da9 100644 --- a/core/java/android/gadget/GadgetInfo.java +++ b/core/java/android/gadget/GadgetInfo.java @@ -58,6 +58,16 @@ public class GadgetInfo implements Parcelable { */ public ComponentName configure; + /** + * The label to display to the user. + */ + public String label; + + /** + * The icon to display for this gadget in the picker list. + */ + public int icon; + public GadgetInfo() { } @@ -75,6 +85,8 @@ public class GadgetInfo implements Parcelable { if (0 != in.readInt()) { this.configure = new ComponentName(in); } + this.label = in.readString(); + this.icon = in.readInt(); } @@ -95,6 +107,8 @@ public class GadgetInfo implements Parcelable { } else { out.writeInt(0); } + out.writeString(this.label); + out.writeInt(this.icon); } public int describeContents() { diff --git a/core/java/android/gadget/GadgetManager.java b/core/java/android/gadget/GadgetManager.java index 088dc86..20f4014 100644 --- a/core/java/android/gadget/GadgetManager.java +++ b/core/java/android/gadget/GadgetManager.java @@ -30,6 +30,10 @@ import java.lang.ref.WeakReference; import java.util.List; import java.util.WeakHashMap; +/** + * Updates gadget state; gets information about installed gadget providers and other + * gadget related state. + */ public class GadgetManager { static final String TAG = "GadgetManager"; @@ -40,9 +44,8 @@ public class GadgetManager { * The system will respond with an onActivityResult call with the following extras in * the intent: * <ul> - * <li><b>gadgetId</b></li> - * <li><b>gadgetId</b></li> - * <li><b>gadgetId</b></li> + * <li><b>gadgetIds</b></li> + * <li><b>hostId</b></li> * </ul> * TODO: Add constants for these. * TODO: Where does this go? @@ -50,21 +53,42 @@ public class GadgetManager { public static final String GADGET_PICK_ACTION = "android.gadget.action.PICK_GADGET"; public static final String EXTRA_GADGET_ID = "gadgetId"; + public static final String EXTRA_GADGET_IDS = "gadgetIds"; + public static final String EXTRA_HOST_ID = "hostId"; /** * Sent when it is time to update your gadget. + * + * <p>This may be sent in response to a new instance for this gadget provider having + * been instantiated, the requested {@link GadgetInfo#updatePeriodMillis update interval} + * having lapsed, or the system booting. */ public static final String GADGET_UPDATE_ACTION = "android.gadget.action.GADGET_UPDATE"; /** - * Sent when the gadget is added to a host for the first time. TODO: Maybe we don't want this. + * Sent when it is time to configure your gadget. This action is not sent as a broadcast + * to the gadget provider, but as a startActivity to the activity specified in the + * {@link GadgetInfo GadgetInfo meta-data}. + * + * <p>The {@link #EXTRA_GADGET_ID} extra contains the gadget ID. + */ + public static final String GADGET_CONFIGURE_ACTION = "android.gadget.action.GADGET_CONFIGURE"; + + /** + * Sent when the gadget is added to a host for the first time. This broadcast is sent at + * boot time if there is a gadget host installed with an instance for this provider. + */ + public static final String GADGET_ENABLED_ACTION = "android.gadget.action.GADGET_ENABLED"; + + /** + * Sent when an instances of a gadget is deleted from the host. */ - public static final String GADGET_ENABLE_ACTION = "android.gadget.action.GADGET_ENABLE"; + public static final String GADGET_DELETED_ACTION = "android.gadget.action.GADGET_DELETED"; /** - * Sent when the gadget is removed from the last host. TODO: Maybe we don't want this. + * Sent when the gadget is removed from the last host. */ - public static final String GADGET_DISABLE_ACTION = "android.gadget.action.GADGET_DISABLE"; + public static final String GADGET_DISABLED_ACTION = "android.gadget.action.GADGET_DISABLED"; /** * Field for the manifest meta-data tag. @@ -106,18 +130,12 @@ public class GadgetManager { * <p> * This method will only work when called from the uid that owns the gadget provider. * - * @param gadgetId The gadget instance for which to set the RemoteViews. + * @param gadgetIds The gadget instances for which to set the RemoteViews. * @param views The RemoteViews object to show. */ - public void updateGadget(int gadgetId, RemoteViews views) { - } - - /** - * Return a list of the gadget providers that are currently installed. - */ - public List<GadgetInfo> getInstalledProviders() { + public void updateGadget(int[] gadgetIds, RemoteViews views) { try { - return sService.getInstalledProviders(); + sService.updateGadgetIds(gadgetIds, views); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -125,14 +143,19 @@ public class GadgetManager { } /** - * Get the available info about the gadget. If the gadgetId has not been bound yet, - * this method will return null. + * Call this with the new RemoteViews for your gadget whenever you need to. * - * TODO: throws GadgetNotFoundException ??? if not valid + * <p> + * This method will only work when called from the uid that owns the gadget provider. + * + * @param provider The {@link ComponentName} for the {@link + * android.content.BroadcastReceiver BroadcastReceiver} provider + * for your gadget. + * @param views The RemoteViews object to show. */ - public GadgetInfo getGadgetInfo(int gadgetId) { + public void updateGadget(ComponentName provider, RemoteViews views) { try { - return sService.getGadgetInfo(gadgetId); + sService.updateGadgetProvider(provider, views); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -140,13 +163,11 @@ public class GadgetManager { } /** - * Get a gadgetId for a host in the calling process. - * - * @return a gadgetId + * Return a list of the gadget providers that are currently installed. */ - public int allocateGadgetId(String hostPackage) { + public List<GadgetInfo> getInstalledProviders() { try { - return sService.allocateGadgetId(hostPackage); + return sService.getInstalledProviders(); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -154,11 +175,14 @@ public class GadgetManager { } /** - * Delete the gadgetId. Same as removeGadget on GadgetHost. + * Get the available info about the gadget. If the gadgetId has not been bound yet, + * this method will return null. + * + * TODO: throws GadgetNotFoundException ??? if not valid */ - public void deleteGadgetId(int gadgetId) { + public GadgetInfo getGadgetInfo(int gadgetId) { try { - sService.deleteGadgetId(gadgetId); + return sService.getGadgetInfo(gadgetId); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); diff --git a/core/java/android/gadget/GadgetProvider.java b/core/java/android/gadget/GadgetProvider.java new file mode 100755 index 0000000..1ddfe3f --- /dev/null +++ b/core/java/android/gadget/GadgetProvider.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gadget; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +/** + * A conveience class to aid in implementing a gadget provider. + * Everything you can do with GadgetProvider, you can do with a regular {@link BroadcastReceiver}. + * GadgetProvider merely parses the relevant fields out of the Intent that is received in + * {@link #onReceive(Context,Intent) onReceive(Context,Intent)}, and calls hook methods + * with the received extras. + * + * <p>Extend this class and override one or more of the {@link #onUpdate}, {@link #onDeleted}, + * {@link #onEnabled} or {@link #onDisabled} methods to implement your own gadget functionality. + * + * <h3>Sample Code</h3> + * For an example of how to write a gadget provider, see the + * <a href="{@toroot}reference/android/gadget/package-descr.html#providers">android.gadget + * package overview</a>. + */ +public class GadgetProvider extends BroadcastReceiver { + /** + * Constructor to initialize GadgetProvider. + */ + public GadgetProvider() { + } + + /** + * Implements {@link BroadcastReceiver#onReceive} to dispatch calls to the various + * other methods on GadgetProvider. + * + * @param context The Context in which the receiver is running. + * @param intent The Intent being received. + */ + // BEGIN_INCLUDE(onReceive) + public void onReceive(Context context, Intent intent) { + // Protect against rogue update broadcasts (not really a security issue, + // just filter bad broacasts out so subclasses are less likely to crash). + String action = intent.getAction(); + if (GadgetManager.GADGET_UPDATE_ACTION.equals(action)) { + Bundle extras = intent.getExtras(); + if (extras != null) { + int[] gadgetIds = extras.getIntArray(GadgetManager.EXTRA_GADGET_IDS); + if (gadgetIds != null && gadgetIds.length > 0) { + this.onUpdate(context, GadgetManager.getInstance(context), gadgetIds); + } + } + } + else if (GadgetManager.GADGET_DELETED_ACTION.equals(action)) { + Bundle extras = intent.getExtras(); + if (extras != null) { + int[] gadgetIds = extras.getIntArray(GadgetManager.EXTRA_GADGET_IDS); + if (gadgetIds != null && gadgetIds.length > 0) { + this.onDeleted(context, gadgetIds); + } + } + } + else if (GadgetManager.GADGET_ENABLED_ACTION.equals(action)) { + this.onEnabled(context); + } + else if (GadgetManager.GADGET_DISABLED_ACTION.equals(action)) { + this.onDisabled(context); + } + } + // END_INCLUDE(onReceive) + + /** + * Called in response to the {@link GadgetManager#GADGET_UPDATE_ACTION} broadcast when + * this gadget provider is being asked to provide {@link android.widget.RemoteViews RemoteViews} + * for a set of gadgets. Override this method to implement your own gadget functionality. + * + * {@more} + * <p class="note">If you want this method called, you must declare in an intent-filter in + * your AndroidManifest.xml file that you accept the GADGET_UPDATE_ACTION intent action. + * For example: + * <font color=red>TODO: SAMPLE CODE GOES HERE</font> + * </p> + * + * @param context The {@link android.content.Context Context} in which this receiver is + * running. + * @param gadgetManager A {@link GadgetManager} object you can call {@link + * GadgetManager#updateGadgets} on. + * @param gadgetIds The gadgetsIds for which an update is needed. Note that this + * may be all of the gadget instances for this provider, or just + * a subset of them. + * + * @see GadgetManager#GADGET_UPDATE_ACTION + */ + public void onUpdate(Context context, GadgetManager gadgetManager, int[] gadgetIds) { + } + + /** + * Called in response to the {@link GadgetManager#GADGET_DELETED_ACTION} broadcast when + * one or more gadget instances have been deleted. Override this method to implement + * your own gadget functionality. + * + * {@more} + * <p class="note">If you want this method called, you must declare in an intent-filter in + * your AndroidManifest.xml file that you accept the GADGET_DELETED_ACTION intent action. + * For example: + * <font color=red>TODO: SAMPLE CODE GOES HERE</font> + * </p> + * + * @param context The {@link android.content.Context Context} in which this receiver is + * running. + * @param gadgetIds The gadgetsIds that have been deleted from their host. + * + * @see GadgetManager#GADGET_DELETED_ACTION + */ + public void onDeleted(Context context, int[] gadgetIds) { + } + + /** + * Called in response to the {@link GadgetManager#GADGET_ENABLED_ACTION} broadcast when + * the a gadget for this provider is instantiated. Override this method to implement your + * own gadget functionality. + * + * {@more} + * When the last gadget for this provider is deleted, + * {@link GadgetManager#GADGET_DISABLED_ACTION} is sent and {@link #onDisabled} + * is called. If after that, a gadget for this provider is created again, onEnabled() will + * be called again. + * + * <p class="note">If you want this method called, you must declare in an intent-filter in + * your AndroidManifest.xml file that you accept the GADGET_ENABLED_ACTION intent action. + * For example: + * <font color=red>TODO: SAMPLE CODE GOES HERE</font> + * </p> + * + * @param context The {@link android.content.Context Context} in which this receiver is + * running. + * + * @see GadgetManager#GADGET_ENABLED_ACTION + */ + public void onEnabled(Context context) { + } + + /** + * Called in response to the {@link GadgetManager#GADGET_DISABLED_ACTION} broadcast, which + * is sent when the last gadget instance for this provider is deleted. Override this method + * to implement your own gadget functionality. + * + * {@more} + * <p class="note">If you want this method called, you must declare in an intent-filter in + * your AndroidManifest.xml file that you accept the GADGET_DISABLED_ACTION intent action. + * For example: + * <font color=red>TODO: SAMPLE CODE GOES HERE</font> + * </p> + * + * @param context The {@link android.content.Context Context} in which this receiver is + * running. + * + * @see GadgetManager#GADGET_DISABLED_ACTION + */ + public void onDisabled(Context context) { + } +} diff --git a/core/java/android/gadget/package.html b/core/java/android/gadget/package.html index 280ccfb..4b8b9d9 100644 --- a/core/java/android/gadget/package.html +++ b/core/java/android/gadget/package.html @@ -1,4 +1,51 @@ <body> -@hide +{@hide} +<p>Android allows applications to publish views to be embedded in other applications. These +views are called gadgets, and are published by "gadget providers." The component that can +contain gadgets is called a "gadget host." See the links below for more information. +</p> +<h3><a href="{@toroot}reference/android/gadget/package-descr.html#providers">Gadget Providers</a></h3> +<ul> + <li><a href="{@toroot}reference/android/gadget/package-descr.html#provider_manifest">Declaring a gadget in the AndroidManifest</a></li> + <li><a href="{@toroot}reference/android/gadget/package-descr.html#provider_meta_data">Adding the {@link android.gadget.GadgetInfo GadgetInfo} meta-data</a></li> + <li><a href="{@toroot}reference/android/gadget/package-descr.html#provider_GadgetProvider">Using the {@link android.gadget.GadgetProvider GadgetProvider} class</a></li> + <li><a href="{@toroot}reference/android/gadget/package-descr.html#provider_configuration">Gadget Configuration UI</a></li> + <li><a href="{@toroot}reference/android/gadget/package-descr.html#provider_broadcasts">Gadget Broadcast Intents</a></li> +</ul> +<h3><a href="{@toroot}reference/android/gadget/package-descr.html#">Gadget Hosts</a></h3> +<ul> + <li><a href="{@toroot}reference/android/gadget/package-descr.html#">asdf</a></li> +</ul> +{@more} +<h2><a name="providers"></a>Gadget Providers</h2> +<p>Any application can publish gadgets. All an application needs to do to publish a gadget is +to have a {@link android.content.BroadcastReceiver} that receives the {@link +android.gadget.GadgetManager#GADGET_UPDATE_ACTION GadgetManager.GADGET_UPDATE_ACTION} intent, +and provide some meta-data about the gadget. + +<h3><a name="provider_manifest"></a>Declaring a gadget in the AndroidManifest</h3> + +<h3><a name="provider_meta_data"></a>Adding the {@link android.gadget.GadgetInfo GadgetInfo} meta-data</h3> + +<h3><a name="provider_GadgetProvider"></a>Using the {@link android.gadget.GadgetProvider GadgetProvider} class</h3> + +<h3><a name="provider_configuration"></a>Gadget Configuration UI</h3> + +<h3><a name="providers_broadcasts"></a>Gadget Broadcast Intents</h3> + +<p>{@link GadgetProvider} is just a convenience class. If you would like to receive the +gadget broadcasts directly, you can. By way of example, the implementation of +{@link GadgetProvider.onReceive} is quite simple:</p> + +{@sample frameworks/base/core/java/android/gadget/GadgetProvider.java onReceive} + + +<h2>Gadget Hosts</h3> +<p>Gadget hosts are the containers in which gadgets can be placed. Most of the look and feel +details are left up to the gadget hosts. For example, the home screen has one way of viewing +gadgets, but the lock screen could also contain gadgets, and it would have a different way of +adding, removing and otherwise managing gadgets.</p> +<p>For more information on implementing your own gadget host, see the +{@link android.gadget.GadgetHost GadgetHost} class.</p> </body> diff --git a/core/java/android/hardware/GeomagneticField.java b/core/java/android/hardware/GeomagneticField.java new file mode 100644 index 0000000..b4c04b1 --- /dev/null +++ b/core/java/android/hardware/GeomagneticField.java @@ -0,0 +1,409 @@ +/* + * 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. + */ + +package android.hardware; + +import java.util.GregorianCalendar; + +/** + * This class is used to estimated estimate magnetic field at a given point on + * Earth, and in particular, to compute the magnetic declination from true + * north. + * + * <p>This uses the World Magnetic Model produced by the United States National + * Geospatial-Intelligence Agency. More details about the model can be found at + * <a href="http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml">http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml</a>. + * This class currently uses WMM-2005 which is valid until 2010, but should + * produce acceptable results for several years after that. + */ +public class GeomagneticField { + // The magnetic field at a given point, in nonoteslas in geodetic + // coordinates. + private float mX; + private float mY; + private float mZ; + + // Geocentric coordinates -- set by computeGeocentricCoordinates. + private float mGcLatitudeRad; + private float mGcLongitudeRad; + private float mGcRadiusKm; + + // Constants from WGS84 (the coordinate system used by GPS) + static private final float EARTH_SEMI_MAJOR_AXIS_KM = 6378.137f; + static private final float EARTH_SEMI_MINOR_AXIS_KM = 6356.7523f; + static private final float EARTH_REFERENCE_RADIUS_KM = 6371.2f; + + // These coefficients and the formulae used below are from: + // NOAA Technical Report: The US/UK World Magnetic Model for 2005-2010 + static private final float[][] G_COEFF = new float[][] { + { 0f }, + { -29556.8f, -1671.7f }, + { -2340.6f, 3046.9f, 1657.0f }, + { 1335.4f, -2305.1f, 1246.7f, 674.0f }, + { 919.8f, 798.1f, 211.3f, -379.4f, 100.0f }, + { -227.4f, 354.6f, 208.7f, -136.5f, -168.3f, -14.1f }, + { 73.2f, 69.7f, 76.7f, -151.2f, -14.9f, 14.6f, -86.3f }, + { 80.1f, -74.5f, -1.4f, 38.5f, 12.4f, 9.5f, 5.7f, 1.8f }, + { 24.9f, 7.7f, -11.6f, -6.9f, -18.2f, 10.0f, 9.2f, -11.6f, -5.2f }, + { 5.6f, 9.9f, 3.5f, -7.0f, 5.1f, -10.8f, -1.3f, 8.8f, -6.7f, -9.1f }, + { -2.3f, -6.3f, 1.6f, -2.6f, 0.0f, 3.1f, 0.4f, 2.1f, 3.9f, -0.1f, -2.3f }, + { 2.8f, -1.6f, -1.7f, 1.7f, -0.1f, 0.1f, -0.7f, 0.7f, 1.8f, 0.0f, 1.1f, 4.1f }, + { -2.4f, -0.4f, 0.2f, 0.8f, -0.3f, 1.1f, -0.5f, 0.4f, -0.3f, -0.3f, -0.1f, + -0.3f, -0.1f } }; + + static private final float[][] H_COEFF = new float[][] { + { 0f }, + { 0.0f, 5079.8f }, + { 0.0f, -2594.7f, -516.7f }, + { 0.0f, -199.9f, 269.3f, -524.2f }, + { 0.0f, 281.5f, -226.0f, 145.8f, -304.7f }, + { 0.0f, 42.4f, 179.8f, -123.0f, -19.5f, 103.6f }, + { 0.0f, -20.3f, 54.7f, 63.6f, -63.4f, -0.1f, 50.4f }, + { 0.0f, -61.5f, -22.4f, 7.2f, 25.4f, 11.0f, -26.4f, -5.1f }, + { 0.0f, 11.2f, -21.0f, 9.6f, -19.8f, 16.1f, 7.7f, -12.9f, -0.2f }, + { 0.0f, -20.1f, 12.9f, 12.6f, -6.7f, -8.1f, 8.0f, 2.9f, -7.9f, 6.0f }, + { 0.0f, 2.4f, 0.2f, 4.4f, 4.8f, -6.5f, -1.1f, -3.4f, -0.8f, -2.3f, -7.9f }, + { 0.0f, 0.3f, 1.2f, -0.8f, -2.5f, 0.9f, -0.6f, -2.7f, -0.9f, -1.3f, -2.0f, -1.2f }, + { 0.0f, -0.4f, 0.3f, 2.4f, -2.6f, 0.6f, 0.3f, 0.0f, 0.0f, 0.3f, -0.9f, -0.4f, + 0.8f } }; + + static private final float[][] DELTA_G = new float[][] { + { 0f }, + { 8.0f, 10.6f }, + { -15.1f, -7.8f, -0.8f }, + { 0.4f, -2.6f, -1.2f, -6.5f }, + { -2.5f, 2.8f, -7.0f, 6.2f, -3.8f }, + { -2.8f, 0.7f, -3.2f, -1.1f, 0.1f, -0.8f }, + { -0.7f, 0.4f, -0.3f, 2.3f, -2.1f, -0.6f, 1.4f }, + { 0.2f, -0.1f, -0.3f, 1.1f, 0.6f, 0.5f, -0.4f, 0.6f }, + { 0.1f, 0.3f, -0.4f, 0.3f, -0.3f, 0.2f, 0.4f, -0.7f, 0.4f }, + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } }; + + static private final float[][] DELTA_H = new float[][] { + { 0f }, + { 0.0f, -20.9f }, + { 0.0f, -23.2f, -14.6f }, + { 0.0f, 5.0f, -7.0f, -0.6f }, + { 0.0f, 2.2f, 1.6f, 5.8f, 0.1f }, + { 0.0f, 0.0f, 1.7f, 2.1f, 4.8f, -1.1f }, + { 0.0f, -0.6f, -1.9f, -0.4f, -0.5f, -0.3f, 0.7f }, + { 0.0f, 0.6f, 0.4f, 0.2f, 0.3f, -0.8f, -0.2f, 0.1f }, + { 0.0f, -0.2f, 0.1f, 0.3f, 0.4f, 0.1f, -0.2f, 0.4f, 0.4f }, + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } }; + + static private final long BASE_TIME = + new GregorianCalendar(2005, 1, 1).getTimeInMillis(); + + // The ratio between the Gauss-normalized associated Legendre functions and + // the Schmid quasi-normalized ones. Compute these once staticly since they + // don't depend on input variables at all. + static private final float[][] SCHMIDT_QUASI_NORM_FACTORS = + computeSchmidtQuasiNormFactors(G_COEFF.length); + + /** + * Estimate the magnetic field at a given point and time. + * + * @param gdLatitudeDeg + * Latitude in WGS84 geodetic coordinates -- positive is east. + * @param gdLongitudeDeg + * Longitude in WGS84 geodetic coordinates -- positive is north. + * @param altitudeMeters + * Altitude in WGS84 geodetic coordinates, in meters. + * @param timeMillis + * Time at which to evaluate the declination, in milliseconds + * since January 1, 1970. (approximate is fine -- the declination + * changes very slowly). + */ + public GeomagneticField(float gdLatitudeDeg, + float gdLongitudeDeg, + float altitudeMeters, + long timeMillis) { + final int MAX_N = G_COEFF.length; // Maximum degree of the coefficients. + + // We don't handle the north and south poles correctly -- pretend that + // we're not quite at them to avoid crashing. + gdLatitudeDeg = Math.min(90.0f - 1e-5f, + Math.max(-90.0f + 1e-5f, gdLatitudeDeg)); + computeGeocentricCoordinates(gdLatitudeDeg, + gdLongitudeDeg, + altitudeMeters); + + assert G_COEFF.length == H_COEFF.length; + + // Note: LegendreTable computes associated Legendre functions for + // cos(theta). We want the associated Legendre functions for + // sin(latitude), which is the same as cos(PI/2 - latitude), except the + // derivate will be negated. + LegendreTable legendre = + new LegendreTable(MAX_N - 1, + (float) (Math.PI / 2.0 - mGcLatitudeRad)); + + // Compute a table of (EARTH_REFERENCE_RADIUS_KM / radius)^n for i in + // 0..MAX_N-2 (this is much faster than calling Math.pow MAX_N+1 times). + float[] relativeRadiusPower = new float[MAX_N + 2]; + relativeRadiusPower[0] = 1.0f; + relativeRadiusPower[1] = EARTH_REFERENCE_RADIUS_KM / mGcRadiusKm; + for (int i = 2; i < relativeRadiusPower.length; ++i) { + relativeRadiusPower[i] = relativeRadiusPower[i - 1] * + relativeRadiusPower[1]; + } + + // Compute tables of sin(lon * m) and cos(lon * m) for m = 0..MAX_N -- + // this is much faster than calling Math.sin and Math.com MAX_N+1 times. + float[] sinMLon = new float[MAX_N]; + float[] cosMLon = new float[MAX_N]; + sinMLon[0] = 0.0f; + cosMLon[0] = 1.0f; + sinMLon[1] = (float) Math.sin(mGcLongitudeRad); + cosMLon[1] = (float) Math.cos(mGcLongitudeRad); + + for (int m = 2; m < MAX_N; ++m) { + // Standard expansions for sin((m-x)*theta + x*theta) and + // cos((m-x)*theta + x*theta). + int x = m >> 1; + sinMLon[m] = sinMLon[m-x] * cosMLon[x] + cosMLon[m-x] * sinMLon[x]; + cosMLon[m] = cosMLon[m-x] * cosMLon[x] - sinMLon[m-x] * sinMLon[x]; + } + + float inverseCosLatitude = 1.0f / (float) Math.cos(mGcLatitudeRad); + float yearsSinceBase = + (timeMillis - BASE_TIME) / (365f * 24f * 60f * 60f * 1000f); + + // We now compute the magnetic field strength given the geocentric + // location. The magnetic field is the derivative of the potential + // function defined by the model. See NOAA Technical Report: The US/UK + // World Magnetic Model for 2005-2010 for the derivation. + float gcX = 0.0f; // Geocentric northwards component. + float gcY = 0.0f; // Geocentric eastwards component. + float gcZ = 0.0f; // Geocentric downwards component. + + for (int n = 1; n < MAX_N; n++) { + for (int m = 0; m <= n; m++) { + // Adjust the coefficients for the current date. + float g = G_COEFF[n][m] + yearsSinceBase * DELTA_G[n][m]; + float h = H_COEFF[n][m] + yearsSinceBase * DELTA_H[n][m]; + + // Negative derivative with respect to latitude, divided by + // radius. This looks like the negation of the version in the + // NOAA Techincal report because that report used + // P_n^m(sin(theta)) and we use P_n^m(cos(90 - theta)), so the + // derivative with respect to theta is negated. + gcX += relativeRadiusPower[n+2] + * (g * cosMLon[m] + h * sinMLon[m]) + * legendre.mPDeriv[n][m] + * SCHMIDT_QUASI_NORM_FACTORS[n][m]; + + // Negative derivative with respect to longitude, divided by + // radius. + gcY += relativeRadiusPower[n+2] * m + * (g * sinMLon[m] - h * cosMLon[m]) + * legendre.mP[n][m] + * SCHMIDT_QUASI_NORM_FACTORS[n][m] + * inverseCosLatitude; + + // Negative derivative with respect to radius. + gcZ -= (n + 1) * relativeRadiusPower[n+2] + * (g * cosMLon[m] + h * sinMLon[m]) + * legendre.mP[n][m] + * SCHMIDT_QUASI_NORM_FACTORS[n][m]; + } + } + + // Convert back to geodetic coordinates. This is basically just a + // rotation around the Y-axis by the difference in latitudes between the + // geocentric frame and the geodetic frame. + double latDiffRad = Math.toRadians(gdLatitudeDeg) - mGcLatitudeRad; + mX = (float) (gcX * Math.cos(latDiffRad) + + gcZ * Math.sin(latDiffRad)); + mY = gcY; + mZ = (float) (- gcX * Math.sin(latDiffRad) + + gcZ * Math.cos(latDiffRad)); + } + + /** + * @return The X (northward) component of the magnetic field in nanoteslas. + */ + public float getX() { + return mX; + } + + /** + * @return The Y (eastward) component of the magnetic field in nanoteslas. + */ + public float getY() { + return mY; + } + + /** + * @return The Z (downward) component of the magnetic field in nanoteslas. + */ + public float getZ() { + return mZ; + } + + /** + * @return The declination of the horizontal component of the magnetic + * field from true north, in degrees (i.e. positive means the + * magnetic field is rotated east that much from true north). + */ + public float getDeclination() { + return (float) Math.toDegrees(Math.atan2(mY, mX)); + } + + /** + * @return The inclination of the magnetic field in degrees -- positive + * means the magnetic field is rotated downwards. + */ + public float getInclination() { + return (float) Math.toDegrees(Math.atan2(mZ, + getHorizontalStrength())); + } + + /** + * @return Horizontal component of the field strength in nonoteslas. + */ + public float getHorizontalStrength() { + return (float) Math.sqrt(mX * mX + mY * mY); + } + + /** + * @return Total field strength in nanoteslas. + */ + public float getFieldStrength() { + return (float) Math.sqrt(mX * mX + mY * mY + mZ * mZ); + } + + /** + * @param gdLatitudeDeg + * Latitude in WGS84 geodetic coordinates. + * @param gdLongitudeDeg + * Longitude in WGS84 geodetic coordinates. + * @param altitudeMeters + * Altitude above sea level in WGS84 geodetic coordinates. + * @return Geocentric latitude (i.e. angle between closest point on the + * equator and this point, at the center of the earth. + */ + private void computeGeocentricCoordinates(float gdLatitudeDeg, + float gdLongitudeDeg, + float altitudeMeters) { + float altitudeKm = altitudeMeters / 1000.0f; + float a2 = EARTH_SEMI_MAJOR_AXIS_KM * EARTH_SEMI_MAJOR_AXIS_KM; + float b2 = EARTH_SEMI_MINOR_AXIS_KM * EARTH_SEMI_MINOR_AXIS_KM; + double gdLatRad = Math.toRadians(gdLatitudeDeg); + float clat = (float) Math.cos(gdLatRad); + float slat = (float) Math.sin(gdLatRad); + float tlat = slat / clat; + float latRad = + (float) Math.sqrt(a2 * clat * clat + b2 * slat * slat); + + mGcLatitudeRad = (float) Math.atan(tlat * (latRad * altitudeKm + b2) + / (latRad * altitudeKm + a2)); + + mGcLongitudeRad = (float) Math.toRadians(gdLongitudeDeg); + + float radSq = altitudeKm * altitudeKm + + 2 * altitudeKm * (float) Math.sqrt(a2 * clat * clat + + b2 * slat * slat) + + (a2 * a2 * clat * clat + b2 * b2 * slat * slat) + / (a2 * clat * clat + b2 * slat * slat); + mGcRadiusKm = (float) Math.sqrt(radSq); + } + + + /** + * Utility class to compute a table of Gauss-normalized associated Legendre + * functions P_n^m(cos(theta)) + */ + static private class LegendreTable { + // These are the Gauss-normalized associated Legendre functions -- that + // is, they are normal Legendre functions multiplied by + // (n-m)!/(2n-1)!! (where (2n-1)!! = 1*3*5*...*2n-1) + public final float[][] mP; + + // Derivative of mP, with respect to theta. + public final float[][] mPDeriv; + + /** + * @param maxN + * The maximum n- and m-values to support + * @param thetaRad + * Returned functions will be Gauss-normalized + * P_n^m(cos(thetaRad)), with thetaRad in radians. + */ + public LegendreTable(int maxN, float thetaRad) { + // Compute the table of Gauss-normalized associated Legendre + // functions using standard recursion relations. Also compute the + // table of derivatives using the derivative of the recursion + // relations. + float cos = (float) Math.cos(thetaRad); + float sin = (float) Math.sin(thetaRad); + + mP = new float[maxN + 1][]; + mPDeriv = new float[maxN + 1][]; + mP[0] = new float[] { 1.0f }; + mPDeriv[0] = new float[] { 0.0f }; + for (int n = 1; n <= maxN; n++) { + mP[n] = new float[n + 1]; + mPDeriv[n] = new float[n + 1]; + for (int m = 0; m <= n; m++) { + if (n == m) { + mP[n][m] = sin * mP[n - 1][m - 1]; + mPDeriv[n][m] = cos * mP[n - 1][m - 1] + + sin * mPDeriv[n - 1][m - 1]; + } else if (n == 1 || m == n - 1) { + mP[n][m] = cos * mP[n - 1][m]; + mPDeriv[n][m] = -sin * mP[n - 1][m] + + cos * mPDeriv[n - 1][m]; + } else { + assert n > 1 && m < n - 1; + float k = ((n - 1) * (n - 1) - m * m) + / (float) ((2 * n - 1) * (2 * n - 3)); + mP[n][m] = cos * mP[n - 1][m] - k * mP[n - 2][m]; + mPDeriv[n][m] = -sin * mP[n - 1][m] + + cos * mPDeriv[n - 1][m] - k * mPDeriv[n - 2][m]; + } + } + } + } + } + + /** + * Compute the ration between the Gauss-normalized associated Legendre + * functions and the Schmidt quasi-normalized version. This is equivalent to + * sqrt((m==0?1:2)*(n-m)!/(n+m!))*(2n-1)!!/(n-m)! + */ + private static float[][] computeSchmidtQuasiNormFactors(int maxN) { + float[][] schmidtQuasiNorm = new float[maxN + 1][]; + schmidtQuasiNorm[0] = new float[] { 1.0f }; + for (int n = 1; n <= maxN; n++) { + schmidtQuasiNorm[n] = new float[n + 1]; + schmidtQuasiNorm[n][0] = + schmidtQuasiNorm[n - 1][0] * (2 * n - 1) / (float) n; + for (int m = 1; m <= n; m++) { + schmidtQuasiNorm[n][m] = schmidtQuasiNorm[n][m - 1] + * (float) Math.sqrt((n - m + 1) * (m == 1 ? 2 : 1) + / (float) (n + m)); + } + } + return schmidtQuasiNorm; + } +}
\ No newline at end of file diff --git a/core/java/android/hardware/ISensorService.aidl b/core/java/android/hardware/ISensorService.aidl index 8aad9b4..04af2ae 100644 --- a/core/java/android/hardware/ISensorService.aidl +++ b/core/java/android/hardware/ISensorService.aidl @@ -25,5 +25,5 @@ import android.os.ParcelFileDescriptor; interface ISensorService { ParcelFileDescriptor getDataChanel(); - boolean enableSensor(IBinder listener, int sensor, int enable); + boolean enableSensor(IBinder listener, String name, int sensor, int enable); } diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index f02094e..3981f27 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -741,7 +741,7 @@ public class SensorManager extends IRotationWatcher.Stub */ @Deprecated public void unregisterListener(SensorListener listener) { - unregisterListener(listener, SENSOR_ALL); + unregisterListener(listener, SENSOR_ALL | SENSOR_ORIENTATION_RAW); } /** @@ -829,9 +829,11 @@ public class SensorManager extends IRotationWatcher.Stub } } + String name = sensor.getName(); + int handle = sensor.getHandle(); if (l == null) { l = new ListenerDelegate(listener, sensor, handler); - result = mSensorService.enableSensor(l, sensor.getHandle(), delay); + result = mSensorService.enableSensor(l, name, handle, delay); if (result) { sListeners.add(l); sListeners.notify(); @@ -840,7 +842,7 @@ public class SensorManager extends IRotationWatcher.Stub sSensorThread.startLocked(mSensorService); } } else { - result = mSensorService.enableSensor(l, sensor.getHandle(), delay); + result = mSensorService.enableSensor(l, name, handle, delay); if (result) { l.addSensor(sensor); } @@ -861,8 +863,9 @@ public class SensorManager extends IRotationWatcher.Stub ListenerDelegate l = sListeners.get(i); if (l.getListener() == listener) { // disable these sensors + String name = sensor.getName(); int handle = sensor.getHandle(); - mSensorService.enableSensor(l, handle, SENSOR_DISABLE); + mSensorService.enableSensor(l, name, handle, SENSOR_DISABLE); // if we have no more sensors enabled on this listener, // take it off the list. if (l.removeSensor(sensor) == 0) { @@ -886,7 +889,9 @@ public class SensorManager extends IRotationWatcher.Stub if (l.getListener() == listener) { // disable all sensors for this listener for (Sensor sensor : l.getSensors()) { - mSensorService.enableSensor(l, sensor.getHandle(), SENSOR_DISABLE); + String name = sensor.getName(); + int handle = sensor.getHandle(); + mSensorService.enableSensor(l, name, handle, SENSOR_DISABLE); } sListeners.remove(i); break; diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java index 7d02f65..eedcc35 100644 --- a/core/java/android/inputmethodservice/AbstractInputMethodService.java +++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java @@ -24,6 +24,9 @@ import android.view.MotionEvent; import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodSession; +import java.io.FileDescriptor; +import java.io.PrintWriter; + /** * AbstractInputMethodService provides a abstract base class for input methods. * Normal input method implementations will not derive from this directly, @@ -156,6 +159,13 @@ public abstract class AbstractInputMethodService extends Service */ public abstract AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface(); + /** + * Implement this to handle {@link android.os.Binder#dump Binder.dump()} + * calls on your input method. + */ + protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { + } + @Override final public IBinder onBind(Intent intent) { if (mInputMethod == null) { diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java index e59f38b..d9f10a9 100644 --- a/core/java/android/inputmethodservice/ExtractEditText.java +++ b/core/java/android/inputmethodservice/ExtractEditText.java @@ -2,6 +2,7 @@ package android.inputmethodservice; import android.content.Context; import android.util.AttributeSet; +import android.view.inputmethod.ExtractedText; import android.widget.EditText; /*** @@ -9,6 +10,9 @@ import android.widget.EditText; * extracted text in a full-screen input method. */ public class ExtractEditText extends EditText { + private InputMethodService mIME; + private int mSettingExtractedText; + public ExtractEditText(Context context) { super(context, null); } @@ -20,4 +24,101 @@ public class ExtractEditText extends EditText { public ExtractEditText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } + + void setIME(InputMethodService ime) { + mIME = ime; + } + + /** + * Start making changes that will not be reported to the client. That + * is, {@link #onSelectionChanged(int, int)} will not result in sending + * the new selection to the client + */ + public void startInternalChanges() { + mSettingExtractedText += 1; + } + + /** + * Finish making changes that will not be reported to the client. That + * is, {@link #onSelectionChanged(int, int)} will not result in sending + * the new selection to the client + */ + public void finishInternalChanges() { + mSettingExtractedText -= 1; + } + + /** + * Implement just to keep track of when we are setting text from the + * client (vs. seeing changes in ourself from the user). + */ + @Override public void setExtractedText(ExtractedText text) { + try { + mSettingExtractedText++; + super.setExtractedText(text); + } finally { + mSettingExtractedText--; + } + } + + /** + * Report to the underlying text editor about selection changes. + */ + @Override protected void onSelectionChanged(int selStart, int selEnd) { + if (mSettingExtractedText == 0 && mIME != null && selStart >= 0 && selEnd >= 0) { + mIME.onExtractedSelectionChanged(selStart, selEnd); + } + } + + /** + * Redirect clicks to the IME for handling there. First allows any + * on click handler to run, though. + */ + @Override public boolean performClick() { + if (!super.performClick() && mIME != null) { + mIME.onExtractedTextClicked(); + return true; + } + return false; + } + + @Override public boolean onTextContextMenuItem(int id) { + if (mIME != null) { + if (mIME.onExtractTextContextMenuItem(id)) { + return true; + } + } + return super.onTextContextMenuItem(id); + } + + /** + * We are always considered to be an input method target. + */ + public boolean isInputMethodTarget() { + return true; + } + + /** + * Pretend like the window this view is in always has focus, so its + * highlight and cursor will be displayed. + */ + @Override public boolean hasWindowFocus() { + return true; + } + + /** + * Pretend like this view always has focus, so its + * highlight and cursor will be displayed. + */ + @Override public boolean isFocused() { + return true; + } + + /** + * Pretend like this view always has focus, so its + * highlight and cursor will be displayed. + */ + @Override public boolean hasFocus() { + return true; + } + } diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index 9abc23b..a2c75b5 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -8,6 +8,8 @@ import com.android.internal.view.IInputMethodSession; import com.android.internal.view.InputConnectionWrapper; import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Binder; import android.os.IBinder; import android.os.Message; import android.os.RemoteException; @@ -18,6 +20,11 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodSession; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + /** * Implements the internal IInputMethod interface to convert incoming calls * on to it back to calls on the public InputMethod interface, scheduling @@ -28,6 +35,7 @@ class IInputMethodWrapper extends IInputMethod.Stub private static final String TAG = "InputMethodWrapper"; private static final boolean DEBUG = false; + private static final int DO_DUMP = 1; private static final int DO_ATTACH_TOKEN = 10; private static final int DO_SET_INPUT_CONTEXT = 20; private static final int DO_UNSET_INPUT_CONTEXT = 30; @@ -39,9 +47,14 @@ class IInputMethodWrapper extends IInputMethod.Stub private static final int DO_SHOW_SOFT_INPUT = 60; private static final int DO_HIDE_SOFT_INPUT = 70; + final AbstractInputMethodService mTarget; final HandlerCaller mCaller; final InputMethod mInputMethod; + static class Notifier { + boolean notified; + } + // NOTE: we should have a cache of these. static class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback { final Context mContext; @@ -64,7 +77,9 @@ class IInputMethodWrapper extends IInputMethod.Stub } } - public IInputMethodWrapper(Context context, InputMethod inputMethod) { + public IInputMethodWrapper(AbstractInputMethodService context, + InputMethod inputMethod) { + mTarget = context; mCaller = new HandlerCaller(context, this); mInputMethod = inputMethod; } @@ -75,6 +90,20 @@ class IInputMethodWrapper extends IInputMethod.Stub public void executeMessage(Message msg) { switch (msg.what) { + case DO_DUMP: { + HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj; + try { + mTarget.dump((FileDescriptor)args.arg1, + (PrintWriter)args.arg2, (String[])args.arg3); + } catch (RuntimeException e) { + ((PrintWriter)args.arg2).println("Exception: " + e); + } + synchronized (args.arg4) { + ((CountDownLatch)args.arg4).countDown(); + } + return; + } + case DO_ATTACH_TOKEN: { mInputMethod.attachToken((IBinder)msg.obj); return; @@ -86,12 +115,22 @@ class IInputMethodWrapper extends IInputMethod.Stub case DO_UNSET_INPUT_CONTEXT: mInputMethod.unbindInput(); return; - case DO_START_INPUT: - mInputMethod.startInput((EditorInfo)msg.obj); + case DO_START_INPUT: { + HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj; + IInputContext inputContext = (IInputContext)args.arg1; + InputConnection ic = inputContext != null + ? new InputConnectionWrapper(inputContext) : null; + mInputMethod.startInput(ic, (EditorInfo)args.arg2); return; - case DO_RESTART_INPUT: - mInputMethod.restartInput((EditorInfo)msg.obj); + } + case DO_RESTART_INPUT: { + HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj; + IInputContext inputContext = (IInputContext)args.arg1; + InputConnection ic = inputContext != null + ? new InputConnectionWrapper(inputContext) : null; + mInputMethod.restartInput(ic, (EditorInfo)args.arg2); return; + } case DO_CREATE_SESSION: { mInputMethod.createSession(new InputMethodSessionCallbackWrapper( mCaller.mContext, (IInputMethodCallback)msg.obj)); @@ -105,8 +144,7 @@ class IInputMethodWrapper extends IInputMethod.Stub mInputMethod.revokeSession((InputMethodSession)msg.obj); return; case DO_SHOW_SOFT_INPUT: - mInputMethod.showSoftInput( - msg.arg1 != 0 ? InputMethod.SHOW_EXPLICIT : 0); + mInputMethod.showSoftInput(msg.arg1); return; case DO_HIDE_SOFT_INPUT: mInputMethod.hideSoftInput(); @@ -115,6 +153,28 @@ class IInputMethodWrapper extends IInputMethod.Stub Log.w(TAG, "Unhandled message code: " + msg.what); } + @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { + if (mTarget.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + + fout.println("Permission Denial: can't dump InputMethodManager from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + CountDownLatch latch = new CountDownLatch(1); + mCaller.executeOrSendMessage(mCaller.obtainMessageOOOO(DO_DUMP, + fd, fout, args, latch)); + try { + if (!latch.await(5, TimeUnit.SECONDS)) { + fout.println("Timeout waiting for dump"); + } + } catch (InterruptedException e) { + fout.println("Interrupted waiting for dump"); + } + } + public void attachToken(IBinder token) { mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_ATTACH_TOKEN, token)); } @@ -130,12 +190,14 @@ class IInputMethodWrapper extends IInputMethod.Stub mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT)); } - public void startInput(EditorInfo attribute) { - mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_START_INPUT, attribute)); + public void startInput(IInputContext inputContext, EditorInfo attribute) { + mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_INPUT, + inputContext, attribute)); } - public void restartInput(EditorInfo attribute) { - mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RESTART_INPUT, attribute)); + public void restartInput(IInputContext inputContext, EditorInfo attribute) { + mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_RESTART_INPUT, + inputContext, attribute)); } public void createSession(IInputMethodCallback callback) { @@ -163,9 +225,9 @@ class IInputMethodWrapper extends IInputMethod.Stub } } - public void showSoftInput(boolean explicit) { + public void showSoftInput(int flags) { mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_SHOW_SOFT_INPUT, - explicit ? 1 : 0)); + flags)); } public void hideSoftInput() { diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 3a9b26a..ea5f741 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -26,7 +26,13 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.IBinder; +import android.provider.Settings; +import android.text.Layout; +import android.text.Spannable; +import android.text.method.MovementMethod; import android.util.Log; +import android.util.PrintWriterPrinter; +import android.util.Printer; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -45,12 +51,31 @@ import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.EditorInfo; import android.widget.FrameLayout; +import java.io.FileDescriptor; +import java.io.PrintWriter; + /** * InputMethodService provides a standard implementation of an InputMethod, * which final implementations can derive from and customize. See the * base class {@link AbstractInputMethodService} and the {@link InputMethod} * interface for more information on the basics of writing input methods. * + * <p>In addition to the normal Service lifecycle methods, this class + * introduces some new specific callbacks that most subclasses will want + * to make use of:</p> + * <ul> + * <li> {@link #onInitializeInterface()} for user-interface initialization, + * in particular to deal with configuration changes while the service is + * running. + * <li> {@link #onBindInput} to find out about switching to a new client. + * <li> {@link #onStartInput} to deal with an input session starting with + * the client. + * <li> {@link #onCreateInputView()}, {@link #onCreateCandidatesView()}, + * and {@link #onCreateExtractTextView()} for non-demand generation of the UI. + * <li> {@link #onStartInputView(EditorInfo, boolean)} to deal with input + * starting within the input area of the IME. + * </ul> + * * <p>An input method has significant discretion in how it goes about its * work: the {@link android.inputmethodservice.InputMethodService} provides * a basic framework for standard UI elements (input view, candidates view, @@ -184,6 +209,7 @@ public class InputMethodService extends AbstractInputMethodService { LayoutInflater mInflater; View mRootView; SoftInputWindow mWindow; + boolean mInitialized; boolean mWindowCreated; boolean mWindowAdded; boolean mWindowVisible; @@ -196,11 +222,16 @@ public class InputMethodService extends AbstractInputMethodService { InputBinding mInputBinding; InputConnection mInputConnection; boolean mInputStarted; + boolean mInputViewStarted; + boolean mCandidatesViewStarted; + InputConnection mStartedInputConnection; EditorInfo mInputEditorInfo; boolean mShowInputRequested; boolean mLastShowInputRequested; - boolean mShowCandidatesRequested; + int mCandidatesVisibility; + + boolean mShowInputForced; boolean mFullscreenApplied; boolean mIsFullscreen; @@ -262,6 +293,7 @@ public class InputMethodService extends AbstractInputMethodService { mInputConnection = binding.getConnection(); if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding + " ic=" + mInputConnection); + initialize(); onBindInput(); } @@ -277,14 +309,14 @@ public class InputMethodService extends AbstractInputMethodService { mInputConnection = null; } - public void startInput(EditorInfo attribute) { + public void startInput(InputConnection ic, EditorInfo attribute) { if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute); - doStartInput(attribute, false); + doStartInput(ic, attribute, false); } - public void restartInput(EditorInfo attribute) { + public void restartInput(InputConnection ic, EditorInfo attribute) { if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute); - doStartInput(attribute, true); + doStartInput(ic, attribute, true); } /** @@ -293,6 +325,7 @@ public class InputMethodService extends AbstractInputMethodService { public void hideSoftInput() { if (DEBUG) Log.v(TAG, "hideSoftInput()"); mShowInputRequested = false; + mShowInputForced = false; hideWindow(); } @@ -316,8 +349,7 @@ public class InputMethodService extends AbstractInputMethodService { return; } if (DEBUG) Log.v(TAG, "finishInput() in " + this); - onFinishInput(); - mInputStarted = false; + doFinishInput(); } /** @@ -444,17 +476,37 @@ public class InputMethodService extends AbstractInputMethodService { mWindow.getWindow().setLayout(FILL_PARENT, WRAP_CONTENT); } + /** + * This is a hook that subclasses can use to perform initialization of + * their interface. It is called for you prior to any of your UI objects + * being created, both after the service is first created and after a + * configuration change happens. + */ + public void onInitializeInterface() { + } + + void initialize() { + if (!mInitialized) { + mInitialized = true; + onInitializeInterface(); + } + } + void initViews() { - mWindowVisible = false; + mInitialized = false; mWindowCreated = false; mShowInputRequested = false; - mShowCandidatesRequested = false; + mShowInputForced = false; mRootView = mInflater.inflate( com.android.internal.R.layout.input_method, null); mWindow.setContentView(mRootView); mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer); - + if (Settings.System.getInt(getContentResolver(), + Settings.System.FANCY_IME_ANIMATIONS, 0) != 0) { + mWindow.getWindow().setWindowAnimations( + com.android.internal.R.style.Animation_InputMethodFancy); + } mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea); mExtractView = null; mExtractEditText = null; @@ -466,7 +518,8 @@ public class InputMethodService extends AbstractInputMethodService { mIsInputViewShown = false; mExtractFrame.setVisibility(View.GONE); - mCandidatesFrame.setVisibility(View.INVISIBLE); + mCandidatesVisibility = getCandidatesHiddenVisibility(); + mCandidatesFrame.setVisibility(mCandidatesVisibility); mInputFrame.setVisibility(View.GONE); } @@ -486,19 +539,36 @@ public class InputMethodService extends AbstractInputMethodService { * regenerating the input method UI as a result of the configuration * change, so you can rely on your {@link #onCreateInputView} and * other methods being called as appropriate due to a configuration change. + * + * <p>When a configuration change does happen, + * {@link #onInitializeInterface()} is guaranteed to be called the next + * time prior to any of the other input or UI creation callbacks. The + * following will be called immediately depending if appropriate for current + * state: {@link #onStartInput} if input is active, and + * {@link #onCreateInputView} and {@link #onStartInputView} and related + * appropriate functions if the UI is displayed. */ @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); boolean visible = mWindowVisible; boolean showingInput = mShowInputRequested; - boolean showingCandidates = mShowCandidatesRequested; + boolean showingForced = mShowInputForced; + boolean showingCandidates = mCandidatesVisibility == View.VISIBLE; initViews(); + mInputViewStarted = false; + mCandidatesViewStarted = false; + if (mInputStarted) { + doStartInput(getCurrentInputConnection(), + getCurrentInputEditorInfo(), true); + } if (visible) { - if (showingCandidates) { - setCandidatesViewShown(true); - } - if (showingInput) { + if (showingForced) { + // If we are showing the full soft keyboard, then go through + // this path to take care of current decisions about fullscreen + // etc. + onShowRequested(InputMethod.SHOW_FORCED|InputMethod.SHOW_EXPLICIT); + } else if (showingInput) { // If we are showing the full soft keyboard, then go through // this path to take care of current decisions about fullscreen // etc. @@ -507,6 +577,9 @@ public class InputMethodService extends AbstractInputMethodService { // Otherwise just put it back for its candidates. showWindow(false); } + if (showingCandidates) { + setCandidatesViewShown(true); + } } } @@ -568,6 +641,10 @@ public class InputMethodService extends AbstractInputMethodService { * the input method, or null if there is none. */ public InputConnection getCurrentInputConnection() { + InputConnection ic = mStartedInputConnection; + if (ic != null) { + return ic; + } return mInputConnection; } @@ -594,6 +671,7 @@ public class InputMethodService extends AbstractInputMethodService { changed = true; mIsFullscreen = isFullscreen; mFullscreenApplied = true; + initialize(); Drawable bg = onCreateBackgroundDrawable(); if (bg == null) { // We need to give the window a real drawable, so that it @@ -708,6 +786,7 @@ public class InputMethodService extends AbstractInputMethodService { mIsInputViewShown = isShown; mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE); if (mInputView == null) { + initialize(); View v = onCreateInputView(); if (v != null) { setInputView(v); @@ -717,12 +796,19 @@ public class InputMethodService extends AbstractInputMethodService { } /** + * Returns true if we have been asked to show our input view. + */ + public boolean isShowInputRequested() { + return mShowInputRequested; + } + + /** * Return whether the soft input view is <em>currently</em> shown to the * user. This is the state that was last determined and * applied by {@link #updateInputViewShown()}. */ public boolean isInputViewShown() { - return mIsInputViewShown; + return mIsInputViewShown && mWindowVisible; } /** @@ -744,9 +830,10 @@ public class InputMethodService extends AbstractInputMethodService { * it is hidden. */ public void setCandidatesViewShown(boolean shown) { - if (mShowCandidatesRequested != shown) { - mCandidatesFrame.setVisibility(shown ? View.VISIBLE : View.INVISIBLE); - mShowCandidatesRequested = shown; + int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility(); + if (mCandidatesVisibility != vis) { + mCandidatesFrame.setVisibility(vis); + mCandidatesVisibility = vis; } if (!mShowInputRequested && mWindowVisible != shown) { // If we are being asked to show the candidates view while the app @@ -760,10 +847,24 @@ public class InputMethodService extends AbstractInputMethodService { } } + /** + * Returns the visibility mode (either {@link View#INVISIBLE View.INVISIBLE} + * or {@link View#GONE View.GONE}) of the candidates view when it is not + * shown. The default implementation returns GONE when in fullscreen mode, + * otherwise VISIBLE. Be careful if you change this to return GONE in + * other situations -- if showing or hiding the candidates view causes + * your window to resize, this can cause temporary drawing artifacts as + * the resize takes place. + */ + public int getCandidatesHiddenVisibility() { + return isFullscreenMode() ? View.GONE : View.INVISIBLE; + } + public void setStatusIcon(int iconResId) { mStatusIcon = iconResId; - if (mInputConnection != null && mWindowVisible) { - mInputConnection.showStatusIcon(getPackageName(), iconResId); + InputConnection ic = getCurrentInputConnection(); + if (ic != null && mWindowVisible) { + ic.showStatusIcon(getPackageName(), iconResId); } } @@ -783,11 +884,12 @@ public class InputMethodService extends AbstractInputMethodService { mExtractFrame.removeAllViews(); mExtractFrame.addView(view, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); + ViewGroup.LayoutParams.FILL_PARENT)); mExtractView = view; if (view != null) { mExtractEditText = (ExtractEditText)view.findViewById( com.android.internal.R.id.inputExtractEditText); + mExtractEditText.setIME(this); startExtractingText(); } else { mExtractEditText = null; @@ -890,6 +992,72 @@ public class InputMethodService extends AbstractInputMethodService { } /** + * Called when the input view is being hidden from the user. This will + * be called either prior to hiding the window, or prior to switching to + * another target for editing. + * + * <p>The default + * implementation uses the InputConnection to clear any active composing + * text; you can override this (not calling the base class implementation) + * to perform whatever behavior you would like. + * + * @boolean finishingInput If true, {@link #onFinishInput} will be + * called immediately after. + */ + public void onFinishInputView(boolean finishingInput) { + if (!finishingInput) { + InputConnection ic = getCurrentInputConnection(); + if (ic != null) { + ic.finishComposingText(); + } + } + } + + /** + * Called when only the candidates view has been shown for showing + * processing as the user enters text through a hard keyboard. + * This will always be called after {@link #onStartInput}, + * allowing you to do your general setup there and just view-specific + * setup here. You are guaranteed that {@link #onCreateCandidatesView()} + * will have been called some time before this function is called. + * + * <p>Note that this will <em>not</em> be called when the input method + * is running in full editing mode, and thus receiving + * {@link #onStartInputView} to initiate that operation. This is only + * for the case when candidates are being shown while the input method + * editor is hidden but wants to show its candidates UI as text is + * entered through some other mechanism. + * + * @param info Description of the type of text being edited. + * @param restarting Set to true if we are restarting input on the + * same text field as before. + */ + public void onStartCandidatesView(EditorInfo info, boolean restarting) { + } + + /** + * Called when the candidates view is being hidden from the user. This will + * be called either prior to hiding the window, or prior to switching to + * another target for editing. + * + * <p>The default + * implementation uses the InputConnection to clear any active composing + * text; you can override this (not calling the base class implementation) + * to perform whatever behavior you would like. + * + * @boolean finishingInput If true, {@link #onFinishInput} will be + * called immediately after. + */ + public void onFinishCandidatesView(boolean finishingInput) { + if (!finishingInput) { + InputConnection ic = getCurrentInputConnection(); + if (ic != null) { + ic.finishComposingText(); + } + } + } + + /** * The system has decided that it may be time to show your input method. * This is called due to a corresponding call to your * {@link InputMethod#showSoftInput(int) InputMethod.showSoftInput(int)} @@ -905,10 +1073,22 @@ public class InputMethodService extends AbstractInputMethodService { if (!onEvaluateInputViewShown()) { return; } - if ((flags&InputMethod.SHOW_EXPLICIT) == 0 && onEvaluateFullscreenMode()) { - // Don't show if this is not explicit requested by the user and - // the input method is fullscreen. That would be too disruptive. - return; + if ((flags&InputMethod.SHOW_EXPLICIT) == 0) { + if (onEvaluateFullscreenMode()) { + // Don't show if this is not explicitly requested by the user and + // the input method is fullscreen. That would be too disruptive. + return; + } + Configuration config = getResources().getConfiguration(); + if (config.keyboard != Configuration.KEYBOARD_NOKEYS) { + // And if the device has a hard keyboard, even if it is + // currently hidden, don't show the input method implicitly. + // These kinds of devices don't need it that much. + return; + } + } + if ((flags&InputMethod.SHOW_FORCED) != 0) { + mShowInputForced = true; } showWindow(true); } @@ -922,6 +1102,7 @@ public class InputMethodService extends AbstractInputMethodService { + " mInputStarted=" + mInputStarted); boolean doShowInput = false; boolean wasVisible = mWindowVisible; + boolean wasCreated = mWindowCreated; mWindowVisible = true; if (!mShowInputRequested) { if (mInputStarted) { @@ -935,41 +1116,66 @@ public class InputMethodService extends AbstractInputMethodService { } if (DEBUG) Log.v(TAG, "showWindow: updating UI"); + initialize(); updateFullscreenMode(); updateInputViewShown(); if (!mWindowAdded || !mWindowCreated) { mWindowAdded = true; mWindowCreated = true; + initialize(); + if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView"); View v = onCreateCandidatesView(); if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v); if (v != null) { setCandidatesView(v); } } - if (doShowInput) { - if (mInputStarted) { - if (DEBUG) Log.v(TAG, "showWindow: starting input view"); + if (mShowInputRequested) { + if (!mInputViewStarted) { + if (DEBUG) Log.v(TAG, "CALL: onStartInputView"); + mInputViewStarted = true; onStartInputView(mInputEditorInfo, false); } + } else if (!mCandidatesViewStarted) { + if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView"); + mCandidatesViewStarted = true; + onStartCandidatesView(mInputEditorInfo, false); + } + + if (doShowInput) { startExtractingText(); } if (!wasVisible) { if (DEBUG) Log.v(TAG, "showWindow: showing!"); mWindow.show(); - if (mInputConnection != null) { - mInputConnection.showStatusIcon(getPackageName(), mStatusIcon); + } + + if (!wasVisible || !wasCreated) { + InputConnection ic = getCurrentInputConnection(); + if (ic != null) { + ic.showStatusIcon(getPackageName(), mStatusIcon); } } } public void hideWindow() { + if (mInputViewStarted) { + if (DEBUG) Log.v(TAG, "CALL: onFinishInputView"); + onFinishInputView(false); + } else if (mCandidatesViewStarted) { + if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView"); + onFinishCandidatesView(false); + } + mInputViewStarted = false; + mCandidatesViewStarted = false; if (mWindowVisible) { mWindow.hide(); mWindowVisible = false; - if (mInputConnection != null) { - mInputConnection.hideStatusIcon(); + InputConnection ic = getCurrentInputConnection(); + if (ic != null) { + ic.hideStatusIcon(); } } } @@ -1008,18 +1214,45 @@ public class InputMethodService extends AbstractInputMethodService { public void onStartInput(EditorInfo attribute, boolean restarting) { } - void doStartInput(EditorInfo attribute, boolean restarting) { - if (mInputStarted && !restarting) { + void doFinishInput() { + if (mInputViewStarted) { + if (DEBUG) Log.v(TAG, "CALL: onFinishInputView"); + onFinishInputView(true); + } else if (mCandidatesViewStarted) { + if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView"); + onFinishCandidatesView(true); + } + mInputViewStarted = false; + mCandidatesViewStarted = false; + if (mInputStarted) { + if (DEBUG) Log.v(TAG, "CALL: onFinishInput"); onFinishInput(); } + mInputStarted = false; + mStartedInputConnection = null; + } + + void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) { + if (!restarting) { + doFinishInput(); + } mInputStarted = true; + mStartedInputConnection = ic; mInputEditorInfo = attribute; + initialize(); + if (DEBUG) Log.v(TAG, "CALL: onStartInput"); onStartInput(attribute, restarting); if (mWindowVisible) { if (mShowInputRequested) { + if (DEBUG) Log.v(TAG, "CALL: onStartInputView"); + mInputViewStarted = true; onStartInputView(mInputEditorInfo, restarting); + startExtractingText(); + } else if (mCandidatesVisibility == View.VISIBLE) { + if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView"); + mCandidatesViewStarted = true; + onStartCandidatesView(mInputEditorInfo, restarting); } - startExtractingText(); } } @@ -1029,8 +1262,17 @@ public class InputMethodService extends AbstractInputMethodService { * {@link #onStartInput(EditorInfo, boolean)} to perform input in a * new editor, or the input method may be left idle. This method is * <em>not</em> called when input restarts in the same editor. + * + * <p>The default + * implementation uses the InputConnection to clear any active composing + * text; you can override this (not calling the base class implementation) + * to perform whatever behavior you would like. */ public void onFinishInput() { + InputConnection ic = getCurrentInputConnection(); + if (ic != null) { + ic.finishComposingText(); + } } /** @@ -1073,9 +1315,12 @@ public class InputMethodService extends AbstractInputMethodService { public void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) { - if (mExtractEditText != null && mExtractedText != null) { + final ExtractEditText eet = mExtractEditText; + if (eet != null && mExtractedText != null) { final int off = mExtractedText.startOffset; - mExtractEditText.setSelection(newSelStart-off, newSelEnd-off); + eet.startInternalChanges(); + eet.setSelection(newSelStart-off, newSelEnd-off); + eet.finishInternalChanges(); } } @@ -1100,6 +1345,18 @@ public class InputMethodService extends AbstractInputMethodService { .hideSoftInputFromInputMethod(mToken, flags); } + /** + * Override this to intercept key down events before they are processed by the + * application. If you return true, the application will not itself + * process the event. If you return true, the normal application processing + * will occur as if the IME had not seen the event at all. + * + * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK + * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown. In + * additional, in fullscreen mode only, it will consume DPAD movement + * events to move the cursor in the extracted text view, not allowing + * them to perform navigation in the underlying application. + */ public boolean onKeyDown(int keyCode, KeyEvent event) { if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { @@ -1108,25 +1365,54 @@ public class InputMethodService extends AbstractInputMethodService { // consume the back key. dismissSoftInput(0); return true; - } - if (mShowCandidatesRequested) { - // If the candidates are shown, we just want to make sure - // they are now hidden but otherwise let the app execute - // the back. - // XXX this needs better interaction with the soft input - // implementation. - //setCandidatesViewShown(false); + } else if (mWindowVisible) { + if (mCandidatesVisibility == View.VISIBLE) { + // If we are showing candidates even if no input area, then + // hide them. + setCandidatesViewShown(false); + return true; + } else { + // If we have the window visible for some other reason -- + // most likely to show candidates -- then just get rid + // of it. This really shouldn't happen, but just in case... + hideWindow(); + return true; + } } } - return false; + + return doMovementKey(keyCode, event, MOVEMENT_DOWN); } + /** + * Override this to intercept special key multiple events before they are + * processed by the + * application. If you return true, the application will not itself + * process the event. If you return true, the normal application processing + * will occur as if the IME had not seen the event at all. + * + * <p>The default implementation always returns false, except when + * in fullscreen mode, where it will consume DPAD movement + * events to move the cursor in the extracted text view, not allowing + * them to perform navigation in the underlying application. + */ public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) { - return false; + return doMovementKey(keyCode, event, count); } + /** + * Override this to intercept key up events before they are processed by the + * application. If you return true, the application will not itself + * process the event. If you return true, the normal application processing + * will occur as if the IME had not seen the event at all. + * + * <p>The default implementation always returns false, except when + * in fullscreen mode, where it will consume DPAD movement + * events to move the cursor in the extracted text view, not allowing + * them to perform navigation in the underlying application. + */ public boolean onKeyUp(int keyCode, KeyEvent event) { - return false; + return doMovementKey(keyCode, event, MOVEMENT_UP); } public boolean onTrackballEvent(MotionEvent event) { @@ -1136,21 +1422,176 @@ public class InputMethodService extends AbstractInputMethodService { public void onAppPrivateCommand(String action, Bundle data) { } + static final int MOVEMENT_DOWN = -1; + static final int MOVEMENT_UP = -2; + + boolean doMovementKey(int keyCode, KeyEvent event, int count) { + final ExtractEditText eet = mExtractEditText; + if (isFullscreenMode() && isInputViewShown() && eet != null) { + // If we are in fullscreen mode, the cursor will move around + // the extract edit text, but should NOT cause focus to move + // to other fields. + MovementMethod movement = eet.getMovementMethod(); + Layout layout = eet.getLayout(); + if (movement != null && layout != null) { + // We want our own movement method to handle the key, so the + // cursor will properly move in our own word wrapping. + if (count == MOVEMENT_DOWN) { + if (movement.onKeyDown(eet, + (Spannable)eet.getText(), keyCode, event)) { + return true; + } + } else if (count == MOVEMENT_UP) { + if (movement.onKeyUp(eet, + (Spannable)eet.getText(), keyCode, event)) { + return true; + } + } else { + KeyEvent down = new KeyEvent(event, KeyEvent.ACTION_DOWN); + if (movement.onKeyDown(eet, + (Spannable)eet.getText(), keyCode, down)) { + KeyEvent up = new KeyEvent(event, KeyEvent.ACTION_UP); + movement.onKeyUp(eet, + (Spannable)eet.getText(), keyCode, up); + while (--count > 0) { + movement.onKeyDown(eet, + (Spannable)eet.getText(), keyCode, down); + movement.onKeyUp(eet, + (Spannable)eet.getText(), keyCode, up); + } + } + } + } + // Regardless of whether the movement method handled the key, + // we never allow DPAD navigation to the application. + switch (keyCode) { + case KeyEvent.KEYCODE_DPAD_LEFT: + case KeyEvent.KEYCODE_DPAD_RIGHT: + case KeyEvent.KEYCODE_DPAD_UP: + case KeyEvent.KEYCODE_DPAD_DOWN: + return true; + } + } + + return false; + } + + /** + * This is called when the user has moved the cursor in the extracted + * text view, when running in fullsreen mode. The default implementation + * performs the corresponding selection change on the underlying text + * editor. + */ + public void onExtractedSelectionChanged(int start, int end) { + InputConnection conn = getCurrentInputConnection(); + if (conn != null) { + conn.setSelection(start, end); + } + } + + /** + * This is called when the user has clicked on the extracted text view, + * when running in fullscreen mode. The default implementation hides + * the candidates view when this happens. Re-implement this to provide + * whatever behavior you want. + */ + public void onExtractedTextClicked() { + setCandidatesViewShown(false); + } + + /** + * This is called when the user has selected a context menu item from the + * extracted text view, when running in fullscreen mode. The default + * implementation sends this action to the current InputConnection's + * {@link InputConnection#performContextMenuAction(int)}, for it + * to be processed in underlying "real" editor. Re-implement this to + * provide whatever behavior you want. + */ + public boolean onExtractTextContextMenuItem(int id) { + InputConnection ic = getCurrentInputConnection(); + if (ic != null) { + ic.performContextMenuAction(id); + } + return true; + } + void startExtractingText() { - if (mExtractEditText != null && getCurrentInputStarted() + final ExtractEditText eet = mExtractEditText; + if (eet != null && getCurrentInputStarted() && isFullscreenMode()) { mExtractedToken++; ExtractedTextRequest req = new ExtractedTextRequest(); req.token = mExtractedToken; + req.flags = InputConnection.GET_TEXT_WITH_STYLES; req.hintMaxLines = 10; req.hintMaxChars = 10000; - mExtractedText = mInputConnection.getExtractedText(req, - InputConnection.EXTRACTED_TEXT_MONITOR); - if (mExtractedText != null) { - mExtractEditText.setExtractedText(mExtractedText); + mExtractedText = getCurrentInputConnection().getExtractedText(req, + InputConnection.GET_EXTRACTED_TEXT_MONITOR); + try { + eet.startInternalChanges(); + int inputType = getCurrentInputEditorInfo().inputType; + if ((inputType&EditorInfo.TYPE_MASK_CLASS) + == EditorInfo.TYPE_CLASS_TEXT) { + if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) { + inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; + } + } + eet.setInputType(inputType); + eet.setHint(mInputEditorInfo.hintText); + if (mExtractedText != null) { + eet.setExtractedText(mExtractedText); + } + } finally { + eet.finishInternalChanges(); } - mExtractEditText.setInputType(getCurrentInputEditorInfo().inputType); - mExtractEditText.setHint(mInputEditorInfo.hintText); } } + + /** + * Performs a dump of the InputMethodService's internal state. Override + * to add your own information to the dump. + */ + @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { + final Printer p = new PrintWriterPrinter(fout); + p.println("Input method service state for " + this + ":"); + p.println(" mWindowCreated=" + mWindowCreated + + " mWindowAdded=" + mWindowAdded + + " mWindowVisible=" + mWindowVisible); + p.println(" Configuration=" + getResources().getConfiguration()); + p.println(" mToken=" + mToken); + p.println(" mInputBinding=" + mInputBinding); + p.println(" mInputConnection=" + mInputConnection); + p.println(" mStartedInputConnection=" + mStartedInputConnection); + p.println(" mInputStarted=" + mInputStarted + + " mInputViewStarted=" + mInputViewStarted + + " mCandidatesViewStarted=" + mCandidatesViewStarted); + + if (mInputEditorInfo != null) { + p.println(" mInputEditorInfo:"); + mInputEditorInfo.dump(p, " "); + } else { + p.println(" mInputEditorInfo: null"); + } + + p.println(" mShowInputRequested=" + mShowInputRequested + + " mLastShowInputRequested=" + mLastShowInputRequested + + " mShowInputForced=" + mShowInputForced); + p.println(" mCandidatesVisibility=" + mCandidatesVisibility + + " mFullscreenApplied=" + mFullscreenApplied + + " mIsFullscreen=" + mIsFullscreen); + + if (mExtractedText != null) { + p.println(" mExtractedText:"); + p.println(" text=" + mExtractedText.text.length() + " chars" + + " startOffset=" + mExtractedText.startOffset); + p.println(" selectionStart=" + mExtractedText.selectionStart + + " selectionEnd=" + mExtractedText.selectionEnd + + " flags=0x" + Integer.toHexString(mExtractedText.flags)); + } else { + p.println(" mExtractedText: null"); + } + p.println(" mExtractedToken=" + mExtractedToken); + p.println(" mIsInputViewShown=" + mIsInputViewShown + + " mStatusIcon=" + mStatusIcon); + } } diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java index cfd3188..228acbe 100755 --- a/core/java/android/inputmethodservice/Keyboard.java +++ b/core/java/android/inputmethodservice/Keyboard.java @@ -177,13 +177,13 @@ public class Keyboard { parent.mDisplayWidth, parent.mDefaultWidth); defaultHeight = getDimensionOrFraction(a, com.android.internal.R.styleable.Keyboard_keyHeight, - parent.mDisplayWidth, parent.mDefaultHeight); - defaultHorizontalGap = getDimensionOrFraction(a, + parent.mDisplayHeight, parent.mDefaultHeight); + defaultHorizontalGap = getDimensionOrFraction(a, com.android.internal.R.styleable.Keyboard_horizontalGap, parent.mDisplayWidth, parent.mDefaultHorizontalGap); verticalGap = getDimensionOrFraction(a, com.android.internal.R.styleable.Keyboard_verticalGap, - parent.mDisplayWidth, parent.mDefaultVerticalGap); + parent.mDisplayHeight, parent.mDefaultVerticalGap); a.recycle(); a = res.obtainAttributes(Xml.asAttributeSet(parser), com.android.internal.R.styleable.Keyboard_Row); @@ -540,7 +540,6 @@ public class Keyboard { row.defaultHorizontalGap = mDefaultHorizontalGap; row.verticalGap = mDefaultVerticalGap; row.rowEdgeFlags = EDGE_TOP | EDGE_BOTTOM; - final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns; for (int i = 0; i < characters.length(); i++) { char c = characters.charAt(i); diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java index 2f3b54b..b2c74f2 100755 --- a/core/java/android/inputmethodservice/KeyboardView.java +++ b/core/java/android/inputmethodservice/KeyboardView.java @@ -149,6 +149,7 @@ public class KeyboardView extends View implements View.OnClickListener { private int mMiniKeyboardOffsetY; private Map<Key,View> mMiniKeyboardCache; private int[] mWindowOffset; + private Key[] mKeys; /** Listener for {@link OnKeyboardActionListener}. */ private OnKeyboardActionListener mKeyboardActionListener; @@ -163,7 +164,7 @@ public class KeyboardView extends View implements View.OnClickListener { private boolean mPreviewCentered = false; private boolean mShowPreview = true; - private boolean mShowTouchPoints = false; + private boolean mShowTouchPoints = true; private int mPopupPreviewX; private int mPopupPreviewY; @@ -334,7 +335,7 @@ public class KeyboardView extends View implements View.OnClickListener { } private void initGestureDetector() { - mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() { + mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() { @Override public boolean onFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) { @@ -386,6 +387,8 @@ public class KeyboardView extends View implements View.OnClickListener { showPreview(NOT_A_KEY); } mKeyboard = keyboard; + List<Key> keys = mKeyboard.getKeys(); + mKeys = keys.toArray(new Key[keys.size()]); requestLayout(); invalidate(); computeProximityThreshold(keyboard); @@ -521,16 +524,16 @@ public class KeyboardView extends View implements View.OnClickListener { */ private void computeProximityThreshold(Keyboard keyboard) { if (keyboard == null) return; - List<Key> keys = keyboard.getKeys(); + final Key[] keys = mKeys; if (keys == null) return; - int length = keys.size(); + int length = keys.length; int dimensionSum = 0; for (int i = 0; i < length; i++) { - Key key = keys.get(i); - dimensionSum += key.width + key.gap + key.height; + Key key = keys[i]; + dimensionSum += Math.min(key.width, key.height) + key.gap; } if (dimensionSum < 0 || length == 0) return; - mProximityThreshold = dimensionSum / (length * 2); + mProximityThreshold = (int) (dimensionSum * 1.5f / length); mProximityThreshold *= mProximityThreshold; // Square it } @@ -545,7 +548,7 @@ public class KeyboardView extends View implements View.OnClickListener { final Rect padding = mPadding; final int kbdPaddingLeft = mPaddingLeft; final int kbdPaddingTop = mPaddingTop; - final List<Key> keys = mKeyboard.getKeys(); + final Key[] keys = mKeys; final Key invalidKey = mInvalidatedKey; //canvas.translate(0, mKeyboardPaddingTop); paint.setAlpha(255); @@ -565,9 +568,9 @@ public class KeyboardView extends View implements View.OnClickListener { drawSingleKey = true; } } - final int keyCount = keys.size(); + final int keyCount = keys.length; for (int i = 0; i < keyCount; i++) { - final Key key = keys.get(i); + final Key key = keys[i]; if (drawSingleKey && invalidKey != key) { continue; } @@ -638,15 +641,15 @@ public class KeyboardView extends View implements View.OnClickListener { } private int getKeyIndices(int x, int y, int[] allKeys) { - final List<Key> keys = mKeyboard.getKeys(); + final Key[] keys = mKeys; final boolean shifted = mKeyboard.isShifted(); int primaryIndex = NOT_A_KEY; int closestKey = NOT_A_KEY; int closestKeyDist = mProximityThreshold + 1; java.util.Arrays.fill(mDistances, Integer.MAX_VALUE); - final int keyCount = keys.size(); + final int keyCount = keys.length; for (int i = 0; i < keyCount; i++) { - final Key key = keys.get(i); + final Key key = keys[i]; int dist = 0; boolean isInside = key.isInside(x,y); if (((mProximityCorrectOn @@ -694,7 +697,7 @@ public class KeyboardView extends View implements View.OnClickListener { private void detectAndSendKey(int x, int y, long eventTime) { int index = mCurrentKey; if (index != NOT_A_KEY) { - final Key key = mKeyboard.getKeys().get(index); + final Key key = mKeys[index]; if (key.text != null) { for (int i = 0; i < key.text.length(); i++) { mKeyboardActionListener.onKey(key.text.charAt(i), key.codes); @@ -743,14 +746,14 @@ public class KeyboardView extends View implements View.OnClickListener { mCurrentKeyIndex = keyIndex; // Release the old key and press the new key - final List<Key> keys = mKeyboard.getKeys(); + final Key[] keys = mKeys; if (oldKeyIndex != mCurrentKeyIndex) { - if (oldKeyIndex != NOT_A_KEY && keys.size() > oldKeyIndex) { - keys.get(oldKeyIndex).onReleased(mCurrentKeyIndex == NOT_A_KEY); + if (oldKeyIndex != NOT_A_KEY && keys.length > oldKeyIndex) { + keys[oldKeyIndex].onReleased(mCurrentKeyIndex == NOT_A_KEY); invalidateKey(oldKeyIndex); } - if (mCurrentKeyIndex != NOT_A_KEY && keys.size() > mCurrentKeyIndex) { - keys.get(mCurrentKeyIndex).onPressed(); + if (mCurrentKeyIndex != NOT_A_KEY && keys.length > mCurrentKeyIndex) { + keys[mCurrentKeyIndex].onPressed(); invalidateKey(mCurrentKeyIndex); } } @@ -764,7 +767,7 @@ public class KeyboardView extends View implements View.OnClickListener { } } if (keyIndex != NOT_A_KEY) { - Key key = keys.get(keyIndex); + Key key = keys[keyIndex]; if (key.icon != null) { mPreviewText.setCompoundDrawables(null, null, null, key.iconPreview != null ? key.iconPreview : key.icon); @@ -774,8 +777,10 @@ public class KeyboardView extends View implements View.OnClickListener { mPreviewText.setText(getPreviewText(key)); if (key.label.length() > 1 && key.codes.length < 2) { mPreviewText.setTextSize(mLabelTextSize); + mPreviewText.setTypeface(Typeface.DEFAULT_BOLD); } else { mPreviewText.setTextSize(mPreviewTextSizeLarge); + mPreviewText.setTypeface(Typeface.DEFAULT); } } mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), @@ -788,8 +793,6 @@ public class KeyboardView extends View implements View.OnClickListener { lp.width = popupWidth; lp.height = popupHeight; } - previewPopup.setWidth(popupWidth); - previewPopup.setHeight(popupHeight); if (!mPreviewCentered) { mPopupPreviewX = key.x - mPreviewText.getPaddingLeft() + mPaddingLeft; mPopupPreviewY = key.y - popupHeight + mPreviewOffset; @@ -809,25 +812,27 @@ public class KeyboardView extends View implements View.OnClickListener { mPreviewText.getBackground().setState( key.popupResId != 0 ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET); if (previewPopup.isShowing()) { - previewPopup.update(mPopupParent, mPopupPreviewX + mOffsetInWindow[0], + previewPopup.update(mPopupPreviewX + mOffsetInWindow[0], mPopupPreviewY + mOffsetInWindow[1], popupWidth, popupHeight); } else { + previewPopup.setWidth(popupWidth); + previewPopup.setHeight(popupHeight); previewPopup.showAtLocation(mPopupParent, Gravity.NO_GRAVITY, mPopupPreviewX + mOffsetInWindow[0], mPopupPreviewY + mOffsetInWindow[1]); } - mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_PREVIEW, keyIndex, 0), + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_PREVIEW, keyIndex, 0), ViewConfiguration.getTapTimeout()); } } } private void invalidateKey(int keyIndex) { - if (keyIndex < 0 || keyIndex >= mKeyboard.getKeys().size()) { + if (keyIndex < 0 || keyIndex >= mKeys.length) { return; } - final Key key = mKeyboard.getKeys().get(keyIndex); + final Key key = mKeys[keyIndex]; mInvalidatedKey = key; invalidate(key.x + mPaddingLeft, key.y + mPaddingTop, key.x + key.width + mPaddingLeft, key.y + key.height + mPaddingTop); @@ -838,11 +843,11 @@ public class KeyboardView extends View implements View.OnClickListener { if (mPopupLayout == 0) { return false; } - if (mCurrentKey < 0 || mCurrentKey >= mKeyboard.getKeys().size()) { + if (mCurrentKey < 0 || mCurrentKey >= mKeys.length) { return false; } - Key popupKey = mKeyboard.getKeys().get(mCurrentKey); + Key popupKey = mKeys[mCurrentKey]; boolean result = onLongPress(popupKey); if (result) { mAbortKey = true; @@ -968,8 +973,8 @@ public class KeyboardView extends View implements View.OnClickListener { mLastMoveTime = mDownTime; checkMultiTap(eventTime, keyIndex); mKeyboardActionListener.onPress(keyIndex != NOT_A_KEY ? - mKeyboard.getKeys().get(keyIndex).codes[0] : 0); - if (mCurrentKey >= 0 && mKeyboard.getKeys().get(mCurrentKey).repeatable) { + mKeys[keyIndex].codes[0] : 0); + if (mCurrentKey >= 0 && mKeys[mCurrentKey].repeatable) { mRepeatKeyIndex = mCurrentKey; repeatKey(); Message msg = mHandler.obtainMessage(MSG_REPEAT); @@ -1054,7 +1059,7 @@ public class KeyboardView extends View implements View.OnClickListener { } private boolean repeatKey() { - Key key = mKeyboard.getKeys().get(mRepeatKeyIndex); + Key key = mKeys[mRepeatKeyIndex]; detectAndSendKey(key.x, key.y, mLastTapTime); return true; } @@ -1113,7 +1118,7 @@ public class KeyboardView extends View implements View.OnClickListener { private void checkMultiTap(long eventTime, int keyIndex) { if (keyIndex == NOT_A_KEY) return; - Key key = mKeyboard.getKeys().get(keyIndex); + Key key = mKeys[keyIndex]; if (key.codes.length > 1) { mInMultiTap = true; if (eventTime < mLastTapTime + MULTITAP_INTERVAL diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java index 9ff1665..da67c6d 100644 --- a/core/java/android/inputmethodservice/SoftInputWindow.java +++ b/core/java/android/inputmethodservice/SoftInputWindow.java @@ -18,6 +18,7 @@ package android.inputmethodservice; import android.app.Dialog; import android.content.Context; +import android.content.pm.ActivityInfo; import android.os.IBinder; import android.view.Gravity; import android.view.WindowManager; @@ -139,6 +140,8 @@ class SoftInputWindow extends Dialog { lp.gravity = Gravity.BOTTOM; lp.width = -1; + // Let the input method window's orientation follow sensor based rotation + lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER; getWindow().setAttributes(lp); getWindow().setFlags( diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 213813a..1429bc1 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -16,6 +16,8 @@ package android.net; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.os.RemoteException; /** @@ -100,6 +102,18 @@ public class ConnectivityManager */ public static final String EXTRA_EXTRA_INFO = "extraInfo"; + /** + * Broadcast Action: The setting for background data usage has changed + * values. Use {@link #getBackgroundDataSetting()} to get the current value. + * <p> + * If an application uses the network in the background, it should listen + * for this broadcast and stop using the background data if the value is + * false. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED = + "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"; + public static final int TYPE_MOBILE = 0; public static final int TYPE_WIFI = 1; @@ -224,6 +238,43 @@ public class ConnectivityManager } /** + * Returns the value of the setting for background data usage. If false, + * applications should not use the network if the application is not in the + * foreground. Developers should respect this setting, and check the value + * of this before performing any background data operations. + * <p> + * All applications that have background services that use the network + * should listen to {@link #ACTION_BACKGROUND_DATA_SETTING_CHANGED}. + * + * @return Whether background data usage is allowed. + */ + public boolean getBackgroundDataSetting() { + try { + return mService.getBackgroundDataSetting(); + } catch (RemoteException e) { + // Err on the side of safety + return false; + } + } + + /** + * Sets the value of the setting for background data usage. + * + * @param allowBackgroundData Whether an application should use data while + * it is in the background. + * + * @attr ref android.Manifest.permission#CHANGE_BACKGROUND_DATA_SETTING + * @see #getBackgroundDataSetting() + * @hide + */ + public void setBackgroundDataSetting(boolean allowBackgroundData) { + try { + mService.setBackgroundDataSetting(allowBackgroundData); + } catch (RemoteException e) { + } + } + + /** * Don't allow use of default constructor. */ @SuppressWarnings({"UnusedDeclaration"}) diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index e1d921f..de68598 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -44,4 +44,8 @@ interface IConnectivityManager int stopUsingNetworkFeature(int networkType, in String feature); boolean requestRouteToHost(int networkType, int hostAddress); + + boolean getBackgroundDataSetting(); + + void setBackgroundDataSetting(boolean allowBackgroundData); } diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 32a26e4..c23df21 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -2235,12 +2235,13 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { } /** - * Creates a new Uri by encoding and appending a path segment to a base Uri. + * Creates a new Uri by appending an already-encoded path segment to a + * base Uri. * * @param baseUri Uri to append path segment to - * @param pathSegment to encode and append - * @return a new Uri based on baseUri with the given segment encoded and - * appended to the path + * @param pathSegment encoded path segment to append + * @return a new Uri based on baseUri with the given segment appended to + * the path * @throws NullPointerException if baseUri is null */ public static Uri withAppendedPath(Uri baseUri, String pathSegment) { diff --git a/core/java/android/net/http/AndroidHttpClient.java b/core/java/android/net/http/AndroidHttpClient.java index 01442ae..4fb1499 100644 --- a/core/java/android/net/http/AndroidHttpClient.java +++ b/core/java/android/net/http/AndroidHttpClient.java @@ -26,7 +26,6 @@ import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpResponse; import org.apache.http.entity.AbstractHttpEntity; import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.client.CookieStore; import org.apache.http.client.HttpClient; import org.apache.http.client.ResponseHandler; import org.apache.http.client.ClientProtocolException; @@ -56,7 +55,6 @@ import java.io.OutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import java.net.URI; -import java.util.concurrent.atomic.AtomicInteger; import android.util.Log; import android.content.ContentResolver; @@ -347,6 +345,13 @@ public final class AndroidHttpClient implements HttpClient { } /** + * Returns true if auth logging is turned on for this configuration. + */ + private boolean isAuthLoggable() { + return Log.isLoggable(tag + "-auth", level); + } + + /** * Prints a message using this configuration. */ private void println(String message) { @@ -392,7 +397,8 @@ public final class AndroidHttpClient implements HttpClient { if (configuration != null && configuration.isLoggable() && request instanceof HttpUriRequest) { - configuration.println(toCurl((HttpUriRequest) request)); + configuration.println(toCurl((HttpUriRequest) request, + configuration.isAuthLoggable())); } } } @@ -400,12 +406,17 @@ public final class AndroidHttpClient implements HttpClient { /** * Generates a cURL command equivalent to the given request. */ - private static String toCurl(HttpUriRequest request) throws IOException { + private static String toCurl(HttpUriRequest request, boolean logAuthToken) throws IOException { StringBuilder builder = new StringBuilder(); builder.append("curl "); for (Header header: request.getAllHeaders()) { + if (!logAuthToken + && (header.getName().equals("Authorization") || + header.getName().equals("Cookie"))) { + continue; + } builder.append("--header \""); builder.append(header.toString().trim()); builder.append("\" "); diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java index 65e6117..c4ee5b0 100644 --- a/core/java/android/net/http/RequestHandle.java +++ b/core/java/android/net/http/RequestHandle.java @@ -55,7 +55,7 @@ public class RequestHandle { private final static String AUTHORIZATION_HEADER = "Authorization"; private final static String PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization"; - private final static int MAX_REDIRECT_COUNT = 16; + public final static int MAX_REDIRECT_COUNT = 16; /** * Creates a new request session. @@ -106,6 +106,14 @@ public class RequestHandle { return mRedirectCount >= MAX_REDIRECT_COUNT; } + public int getRedirectCount() { + return mRedirectCount; + } + + public void setRedirectCount(int count) { + mRedirectCount = count; + } + /** * Create and queue a redirect request. * diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index ed7c366..017b14d 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -15,19 +15,26 @@ import android.util.SparseArray; public abstract class BatteryStats { /** - * A constant indicating a partial wake lock. + * A constant indicating a partial wake lock timer. */ public static final int WAKE_TYPE_PARTIAL = 0; /** - * A constant indicating a full wake lock. + * A constant indicating a full wake lock timer. */ public static final int WAKE_TYPE_FULL = 1; /** - * A constant indicating a window wake lock. + * A constant indicating a window wake lock timer. */ public static final int WAKE_TYPE_WINDOW = 2; + + /** + * A constant indicating a sensor timer. + * + * {@hide} + */ + public static final int SENSOR = 3; /** * Include all of the data in the stats, including previously saved data. @@ -48,6 +55,21 @@ public abstract class BatteryStats { * Include only the run since the last time the device was unplugged in the stats. */ public static final int STATS_UNPLUGGED = 3; + + /** + * Bump the version on this if the checkin format changes. + */ + private static final int BATTERY_STATS_CHECKIN_VERSION = 1; + + // TODO: Update this list if you add/change any stats above. + private static final String[] STAT_NAMES = { "total", "last", "current", "unplugged" }; + + private static final String APK_DATA = "apk"; + private static final String PROCESS_DATA = "process"; + private static final String SENSOR_DATA = "sensor"; + private static final String WAKELOCK_DATA = "wakelock"; + private static final String NETWORK_DATA = "network"; + private static final String BATTERY_DATA = "battery"; private final StringBuilder mFormatBuilder = new StringBuilder(8); private final Formatter mFormatter = new Formatter(mFormatBuilder); @@ -115,8 +137,28 @@ public abstract class BatteryStats { * @return a Map from Strings to Uid.Pkg objects. */ public abstract Map<String, ? extends Pkg> getPackageStats(); + + /** + * {@hide} + */ + public abstract int getUid(); + + /** + * {@hide} + */ + public abstract long getTcpBytesReceived(int which); + + /** + * {@hide} + */ + public abstract long getTcpBytesSent(int which); public static abstract class Sensor { + /** + * {@hide} + */ + public abstract String getName(); + public abstract Timer getSensorTime(); } @@ -200,6 +242,22 @@ public abstract class BatteryStats { * Returns the number of times the device has been started. */ public abstract int getStartCount(); + + /** + * Returns the time in milliseconds that the screen has been on while the device was + * running on battery. + * + * {@hide} + */ + public abstract long getBatteryScreenOnTime(); + + /** + * Returns the time in milliseconds that the screen has been on while the device was + * plugged in. + * + * {@hide} + */ + public abstract long getPluggedScreenOnTime(); /** * Returns a SparseArray containing the statistics for each uid. @@ -318,11 +376,14 @@ public abstract class BatteryStats { * @param linePrefix a String to be prepended to each line of output. * @return the line prefix */ - private final String printWakeLock(StringBuilder sb, Timer timer, long now, + private static final String printWakeLock(StringBuilder sb, Timer timer, long now, String name, int which, String linePrefix) { + if (timer != null) { // Convert from microseconds to milliseconds with rounding - long totalTimeMillis = (timer.getTotalTime(now, which) + 500) / 1000; + long totalTimeMicros = timer.getTotalTime(now, which); + long totalTimeMillis = (totalTimeMicros + 500) / 1000; + int count = timer.getCount(which); if (totalTimeMillis != 0) { sb.append(linePrefix); @@ -337,6 +398,184 @@ public abstract class BatteryStats { } return linePrefix; } + + /** + * Checkin version of wakelock printer. Prints simple comma-separated list. + * + * @param sb a StringBuilder object. + * @param timer a Timer object contining the wakelock times. + * @param now the current time in microseconds. + * @param name the name of the wakelock. + * @param which which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT. + * @param linePrefix a String to be prepended to each line of output. + * @return the line prefix + */ + private static final String printWakeLockCheckin(StringBuilder sb, Timer timer, long now, + String name, int which, String linePrefix) { + long totalTimeMicros = 0; + int count = 0; + if (timer != null) { + totalTimeMicros = timer.getTotalTime(now, which); + count = timer.getCount(which); + } + sb.append(linePrefix); + sb.append((totalTimeMicros + 500) / 1000); // microseconds to milliseconds with rounding + sb.append(','); + sb.append(name); + sb.append(','); + sb.append(count); + return ","; + } + + /** + * Dump a comma-separated line of values for terse checkin mode. + * + * @param pw the PageWriter to dump log to + * @param category category of data (e.g. "total", "last", "unplugged", "current" ) + * @param type type of data (e.g. "wakelock", "sensor", "process", "apk" , "process", "network") + * @param args type-dependent data arguments + */ + private static final void dumpLine(PrintWriter pw, int uid, String category, String type, + Object... args ) { + pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); + pw.print(uid); pw.print(','); + pw.print(category); pw.print(','); + pw.print(type); + + for (Object arg : args) { + pw.print(','); + pw.print(arg); + } + pw.print('\n'); + } + + /** + * Checkin server version of dump to produce more compact, computer-readable log. + * + * NOTE: all times are expressed in 'ms'. + * @param fd + * @param pw + * @param which + */ + private final void dumpCheckinLocked(FileDescriptor fd, PrintWriter pw, int which) { + long uSecTime = SystemClock.elapsedRealtime() * 1000; + final long uSecNow = getBatteryUptime(uSecTime); + + StringBuilder sb = new StringBuilder(128); + long batteryUptime = computeBatteryUptime(uSecNow, which); + long batteryRealtime = computeBatteryRealtime(getBatteryRealtime(uSecTime), which); + long elapsedRealtime = computeRealtime(uSecTime, which); + long uptime = computeUptime(SystemClock.uptimeMillis() * 1000, which); + + String category = STAT_NAMES[which]; + + // Dump "battery" stat + dumpLine(pw, 0 /* uid */, category, BATTERY_DATA, + which == STATS_TOTAL ? getStartCount() : "N/A", + batteryUptime / 1000, + formatRatioLocked(batteryUptime, elapsedRealtime), + batteryRealtime / 1000, + formatRatioLocked(batteryRealtime, elapsedRealtime), + uptime / 1000, + elapsedRealtime / 1000); + + SparseArray<? extends Uid> uidStats = getUidStats(); + final int NU = uidStats.size(); + for (int iu = 0; iu < NU; iu++) { + final int uid = uidStats.keyAt(iu); + Uid u = uidStats.valueAt(iu); + // Dump Network stats per uid, if any + long rx = u.getTcpBytesReceived(which); + long tx = u.getTcpBytesSent(which); + if (rx > 0 || tx > 0) dumpLine(pw, uid, category, NETWORK_DATA, rx, tx); + + Map<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats(); + if (wakelocks.size() > 0) { + for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> ent + : wakelocks.entrySet()) { + Uid.Wakelock wl = ent.getValue(); + String linePrefix = ""; + sb.setLength(0); + linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_FULL), uSecNow, + "full", which, linePrefix); + linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_PARTIAL), uSecNow, + "partial", which, linePrefix); + linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_WINDOW), uSecNow, + "window", which, linePrefix); + + // Only log if we had at lease one wakelock... + if (sb.length() > 0) { + dumpLine(pw, uid, category, WAKELOCK_DATA, ent.getKey(), sb.toString()); + } + } + } + + Map<Integer, ? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats(); + if (sensors.size() > 0) { + for (Map.Entry<Integer, ? extends BatteryStats.Uid.Sensor> ent + : sensors.entrySet()) { + Uid.Sensor se = ent.getValue(); + int sensorNumber = ent.getKey(); + Timer timer = se.getSensorTime(); + if (timer != null) { + // Convert from microseconds to milliseconds with rounding + long totalTime = (timer.getTotalTime(uSecNow, which) + 500) / 1000; + int count = timer.getCount(which); + if (totalTime != 0) { + dumpLine(pw, uid, category, SENSOR_DATA, sensorNumber, totalTime, count); + } + } + } + } + + Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); + if (processStats.size() > 0) { + for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent + : processStats.entrySet()) { + Uid.Proc ps = ent.getValue(); + + long userTime = ps.getUserTime(which); + long systemTime = ps.getSystemTime(which); + int starts = ps.getStarts(which); + + if (userTime != 0 || systemTime != 0 || starts != 0) { + dumpLine(pw, uid, category, PROCESS_DATA, + ent.getKey(), // proc + userTime * 10, // cpu time in ms + systemTime * 10, // user time in ms + starts); // process starts + } + } + } + + Map<String, ? extends BatteryStats.Uid.Pkg> packageStats = u.getPackageStats(); + if (packageStats.size() > 0) { + for (Map.Entry<String, ? extends BatteryStats.Uid.Pkg> ent + : packageStats.entrySet()) { + + Uid.Pkg ps = ent.getValue(); + int wakeups = ps.getWakeups(which); + Map<String, ? extends Uid.Pkg.Serv> serviceStats = ps.getServiceStats(); + for (Map.Entry<String, ? extends BatteryStats.Uid.Pkg.Serv> sent + : serviceStats.entrySet()) { + BatteryStats.Uid.Pkg.Serv ss = sent.getValue(); + long startTime = ss.getStartTime(uSecNow, which); + int starts = ss.getStarts(which); + int launches = ss.getLaunches(which); + if (startTime != 0 || starts != 0 || launches != 0) { + dumpLine(pw, uid, category, APK_DATA, + wakeups, // wakeup alarms + ent.getKey(), // Apk + sent.getKey(), // service + startTime / 1000, // time spent started, in ms + starts, + launches); + } + } + } + } + } + } @SuppressWarnings("unused") private final void dumpLocked(FileDescriptor fd, PrintWriter pw, String prefix, int which) { @@ -344,13 +583,22 @@ public abstract class BatteryStats { final long uSecNow = getBatteryUptime(uSecTime); StringBuilder sb = new StringBuilder(128); - if (which == STATS_TOTAL) { - pw.println(prefix + "Current and Historic Battery Usage Statistics:"); - pw.println(prefix + " System starts: " + getStartCount()); - } else if (which == STATS_LAST) { - pw.println(prefix + "Last Battery Usage Statistics:"); - } else { - pw.println(prefix + "Current Battery Usage Statistics:"); + switch (which) { + case STATS_TOTAL: + pw.println(prefix + "Current and Historic Battery Usage Statistics:"); + pw.println(prefix + " System starts: " + getStartCount()); + break; + case STATS_LAST: + pw.println(prefix + "Last Battery Usage Statistics:"); + break; + case STATS_UNPLUGGED: + pw.println(prefix + "Last Unplugged Battery Usage Statistics:"); + break; + case STATS_CURRENT: + pw.println(prefix + "Current Battery Usage Statistics:"); + break; + default: + throw new IllegalArgumentException("which = " + which); } long batteryUptime = computeBatteryUptime(uSecNow, which); long batteryRealtime = computeBatteryRealtime(getBatteryRealtime(uSecTime), which); @@ -359,7 +607,7 @@ public abstract class BatteryStats { pw.println(prefix + " On battery: " + formatTimeMs(batteryUptime / 1000) + "(" - + formatRatioLocked(batteryUptime, batteryRealtime) + + formatRatioLocked(batteryUptime, elapsedRealtime) + ") uptime, " + formatTimeMs(batteryRealtime / 1000) + "(" + formatRatioLocked(batteryRealtime, elapsedRealtime) @@ -380,6 +628,9 @@ public abstract class BatteryStats { Uid u = uidStats.valueAt(iu); pw.println(prefix + " #" + uid + ":"); boolean uidActivity = false; + + pw.println(prefix + " Network: " + u.getTcpBytesReceived(which) + " bytes received, " + + u.getTcpBytesSent(which) + " bytes sent"); Map<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats(); if (wakelocks.size() > 0) { @@ -512,12 +763,30 @@ public abstract class BatteryStats { */ @SuppressWarnings("unused") public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { + boolean isCheckin = false; + if (args != null) { + for (String arg : args) { + if ("-c".equals(arg)) { + isCheckin = true; + break; + } + } + } synchronized (this) { - dumpLocked(fd, pw, "", STATS_TOTAL); - pw.println(""); - dumpLocked(fd, pw, "", STATS_LAST); - pw.println(""); - dumpLocked(fd, pw, "", STATS_CURRENT); + if (isCheckin) { + dumpCheckinLocked(fd, pw, STATS_TOTAL); + dumpCheckinLocked(fd, pw, STATS_LAST); + dumpCheckinLocked(fd, pw, STATS_UNPLUGGED); + dumpCheckinLocked(fd, pw, STATS_CURRENT); + } else { + dumpLocked(fd, pw, "", STATS_TOTAL); + pw.println(""); + dumpLocked(fd, pw, "", STATS_LAST); + pw.println(""); + dumpLocked(fd, pw, "", STATS_UNPLUGGED); + pw.println(""); + dumpLocked(fd, pw, "", STATS_CURRENT); + } } } } diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 528e6bd..df10c6a 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -33,7 +33,7 @@ import java.lang.reflect.Modifier; * the standard support creating a local implementation of such an object. * * <p>Most developers will not implement this class directly, instead using the - * <a href="{@docRoot}reference/aidl.html">aidl</a> tool to describe the desired + * <a href="{@docRoot}guide/developing/tools/aidl.html">aidl</a> tool to describe the desired * interface, having it generate the appropriate Binder subclass. You can, * however, derive directly from Binder to implement your own custom RPC * protocol or simply instantiate a raw Binder object directly to use as a @@ -194,18 +194,15 @@ public class Binder implements IBinder { return true; } else if (code == DUMP_TRANSACTION) { ParcelFileDescriptor fd = data.readFileDescriptor(); - FileOutputStream fout = fd != null - ? new FileOutputStream(fd.getFileDescriptor()) : null; - PrintWriter pw = fout != null ? new PrintWriter(fout) : null; - if (pw != null) { - String[] args = data.readStringArray(); - dump(fd.getFileDescriptor(), pw, args); - pw.flush(); - } + String[] args = data.readStringArray(); if (fd != null) { try { - fd.close(); - } catch (IOException e) { + dump(fd.getFileDescriptor(), args); + } finally { + try { + fd.close(); + } catch (IOException e) { + } } } return true; @@ -214,6 +211,20 @@ public class Binder implements IBinder { } /** + * Implemented to call the more convenient version + * {@link #dump(FileDescriptor, PrintWriter, String[])}. + */ + public void dump(FileDescriptor fd, String[] args) { + FileOutputStream fout = new FileOutputStream(fd); + PrintWriter pw = new PrintWriter(fout); + try { + dump(fd, pw, args); + } finally { + pw.flush(); + } + } + + /** * Print the object's state into the given stream. * * @param fd The raw file descriptor that the dump is being sent to. @@ -302,6 +313,17 @@ final class BinderProxy implements IBinder { throws RemoteException; public native boolean unlinkToDeath(DeathRecipient recipient, int flags); + public void dump(FileDescriptor fd, String[] args) throws RemoteException { + Parcel data = Parcel.obtain(); + data.writeFileDescriptor(fd); + data.writeStringArray(args); + try { + transact(DUMP_TRANSACTION, data, null, 0); + } finally { + data.recycle(); + } + } + BinderProxy() { mSelf = new WeakReference(this); } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index cdf907b..467c17f 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -26,6 +26,9 @@ public class Build { /** Either a changelist number, or a label like "M4-rc20". */ public static final String ID = getString("ro.build.id"); + /** A build ID string meant for displaying to the user */ + public static final String DISPLAY = getString("ro.build.display.id"); + /** The name of the overall product. */ public static final String PRODUCT = getString("ro.product.name"); diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 5f7f91f..950bb09 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -17,6 +17,7 @@ package android.os; import java.io.FileOutputStream; +import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; @@ -28,12 +29,13 @@ import dalvik.bytecode.Opcodes; import dalvik.system.VMDebug; -/** Provides various debugging functions for Android applications, including +/** + * Provides various debugging functions for Android applications, including * tracing and allocation counts. * <p><strong>Logging Trace Files</strong></p> * <p>Debug can create log files that give details about an application, such as * a call stack and start/stop times for any running methods. See <a -href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Program</a> for +href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for * information about reading trace files. To start logging trace files, call one * of the startMethodTracing() methods. To stop tracing, call * {@link #stopMethodTracing()}. @@ -285,7 +287,7 @@ public final class Debug /** * Start method tracing with default log name and buffer size. See <a -href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Program</a> for +href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for * information about reading these files. Call stopMethodTracing() to stop * tracing. */ @@ -297,7 +299,7 @@ href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Progra * Start method tracing, specifying the trace log file name. The trace * file will be put under "/sdcard" unless an absolute path is given. * See <a - href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Program</a> for + href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for * information about reading trace files. * * @param traceName Name for the trace log file to create. @@ -313,7 +315,7 @@ href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Progra * Start method tracing, specifying the trace log file name and the * buffer size. The trace files will be put under "/sdcard" unless an * absolute path is given. See <a - href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Program</a> for + href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for * information about reading trace files. * @param traceName Name for the trace log file to create. * If no name argument is given, this value defaults to "/sdcard/dmtrace.trace". @@ -330,7 +332,7 @@ href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Progra * Start method tracing, specifying the trace log file name and the * buffer size. The trace files will be put under "/sdcard" unless an * absolute path is given. See <a - href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Program</a> for + href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for * information about reading trace files. * * <p> @@ -581,6 +583,18 @@ href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Progra } /** + * Dump "hprof" data to the specified file. This will cause a GC. + * + * @param fileName Full pathname of output file (e.g. "/sdcard/dump.hprof"). + * @throws UnsupportedOperationException if the VM was built without + * HPROF support. + * @throws IOException if an error occurs while opening or writing files. + */ + public static void dumpHprofData(String fileName) throws IOException { + VMDebug.dumpHprofData(fileName); + } + + /** * Returns the number of sent transactions from this process. * @return The number of sent transactions or -1 if it could not read t. */ diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index e37b551..f761e8e 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -75,6 +75,18 @@ public class Environment { public static final String MEDIA_UNMOUNTED = "unmounted"; /** + * getExternalStorageState() returns MEDIA_CHECKING if the media is present + * and being disk-checked + */ + public static final String MEDIA_CHECKING = "checking"; + + /** + * getExternalStorageState() returns MEDIA_NOFS if the media is present + * but is blank or is using an unsupported filesystem + */ + public static final String MEDIA_NOFS = "nofs"; + + /** * getExternalStorageState() returns MEDIA_MOUNTED if the media is present * and mounted at its mount point with read/write access. */ diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java index 3ec0e9b..5c40c9a0 100644 --- a/core/java/android/os/IBinder.java +++ b/core/java/android/os/IBinder.java @@ -16,6 +16,9 @@ package android.os; +import java.io.FileDescriptor; +import java.io.PrintWriter; + /** * Base interface for a remotable object, the core part of a lightweight * remote procedure call mechanism designed for high performance when @@ -145,6 +148,14 @@ public interface IBinder { public IInterface queryLocalInterface(String descriptor); /** + * Print the object's state into the given stream. + * + * @param fd The raw file descriptor that the dump is being sent to. + * @param args additional arguments to the dump request. + */ + public void dump(FileDescriptor fd, String[] args) throws RemoteException; + + /** * Perform a generic operation with the object. * * @param code The action to perform. This should diff --git a/core/java/android/os/ICheckinService.aidl b/core/java/android/os/ICheckinService.aidl index 70ad28e..11becc4 100644 --- a/core/java/android/os/ICheckinService.aidl +++ b/core/java/android/os/ICheckinService.aidl @@ -26,6 +26,9 @@ import android.os.IParentalControlCallback; * {@hide} */ interface ICheckinService { + /** Synchronously attempt a checkin with the server, return true on success. */ + boolean checkin(); + /** Direct submission of crash data; returns after writing the crash. */ void reportCrashSync(in byte[] crashData); diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/IMountService.aidl index 0397446..88dae85 100644 --- a/core/java/android/os/IMountService.aidl +++ b/core/java/android/os/IMountService.aidl @@ -48,4 +48,19 @@ interface IMountService * Safely unmount external storage at given mount point. */ void unmountMedia(String mountPoint); + + /** + * Format external storage given a mount point + */ + void formatMedia(String mountPoint); + + /** + * Returns true if media notification sounds are enabled. + */ + boolean getPlayNotificationSounds(); + + /** + * Sets whether or not media notification sounds are played. + */ + void setPlayNotificationSounds(boolean value); } diff --git a/core/java/android/os/INetStatService.aidl b/core/java/android/os/INetStatService.aidl index fb840d8..a8f3de0 100644 --- a/core/java/android/os/INetStatService.aidl +++ b/core/java/android/os/INetStatService.aidl @@ -17,14 +17,19 @@ package android.os; /** - * Retrieves packet and byte counts for the phone data interface. + * Retrieves packet and byte counts for the phone data interface, + * and for all interfaces. * Used for the data activity icon and the phone status in Settings. * * {@hide} */ interface INetStatService { - int getTxPackets(); - int getRxPackets(); - int getTxBytes(); - int getRxBytes(); + long getMobileTxPackets(); + long getMobileRxPackets(); + long getMobileTxBytes(); + long getMobileRxBytes(); + long getTotalTxPackets(); + long getTotalRxPackets(); + long getTotalTxBytes(); + long getTotalRxBytes(); } diff --git a/core/java/android/os/NetStat.java b/core/java/android/os/NetStat.java index 7312236..733137a 100644 --- a/core/java/android/os/NetStat.java +++ b/core/java/android/os/NetStat.java @@ -16,36 +16,197 @@ package android.os; +import android.util.Log; + +import java.io.File; +import java.io.RandomAccessFile; +import java.io.IOException; + /** @hide */ public class NetStat{ /** - * Get total number of tx packets sent through ppp0 + * Get total number of tx packets sent through rmnet0 or ppp0 * - * @return number of Tx packets through ppp0 + * @return number of Tx packets through rmnet0 or ppp0 */ - - public native static int netStatGetTxPkts(); + public static long getMobileTxPkts() { + return getMobileStat("tx_packets"); + } /** - * Get total number of rx packets received through ppp0 + * Get total number of rx packets received through rmnet0 or ppp0 * - * @return number of Rx packets through ppp0 + * @return number of Rx packets through rmnet0 or ppp0 */ - public native static int netStatGetRxPkts(); + public static long getMobileRxPkts() { + return getMobileStat("rx_packets"); + } /** - * Get total number of tx bytes received through ppp0 + * Get total number of tx bytes received through rmnet0 or ppp0 + * + * @return number of Tx bytes through rmnet0 or ppp0 + */ + public static long getMobileTxBytes() { + return getMobileStat("tx_bytes"); + } + + /** + * Get total number of rx bytes received through rmnet0 or ppp0 + * + * @return number of Rx bytes through rmnet0 or ppp0 + */ + public static long getMobileRxBytes() { + return getMobileStat("rx_bytes"); + } + + /** + * Get the total number of packets sent through all network interfaces. + * + * @return the number of packets sent through all network interfaces + */ + public static long getTotalTxPkts() { + return getTotalStat("tx_packets"); + } + + /** + * Get the total number of packets received through all network interfaces. + * + * @return the number of packets received through all network interfaces + */ + public static long getTotalRxPkts() { + return getTotalStat("rx_packets"); + } + + /** + * Get the total number of bytes sent through all network interfaces. + * + * @return the number of bytes sent through all network interfaces + */ + public static long getTotalTxBytes() { + return getTotalStat("tx_bytes"); + } + + /** + * Get the total number of bytes received through all network interfaces. + * + * @return the number of bytes received through all network interfaces + */ + public static long getTotalRxBytes() { + return getTotalStat("rx_bytes"); + } + + /** + * Gets network bytes sent for this UID. + * The statistics are across all interfaces. + * The statistics come from /proc/uid_stat. * - * @return number of Tx bytes through ppp0 + * {@see android.os.Process#myUid()}. + * + * @param uid + * @return byte count */ - public native static int netStatGetTxBytes(); + public static long getUidTxBytes(int uid) { + return getNumberFromFilePath("/proc/uid_stat/" + uid + "/tcp_snd"); + } /** - * Get total number of rx bytes received through ppp0 + * Gets network bytes received for this UID. + * The statistics are across all interfaces. + * The statistics come from /proc/uid_stat. * - * @return number of Rx bytes through ppp0 + * {@see android.os.Process#myUid()}. + * + * @param uid + * @return byte count */ - public native static int netStatGetRxBytes(); + public static long getUidRxBytes(int uid) { + return getNumberFromFilePath("/proc/uid_stat/" + uid + "/tcp_rcv"); + } + + private static String TAG = "netstat"; + private static final byte[] buf = new byte[16]; + + private static long getTotalStat(String whatStat) { + File netdir = new File("/sys/class/net"); + + File[] nets = netdir.listFiles(); + if (nets == null) { + return 0; + } + long total = 0; + StringBuffer strbuf = new StringBuffer(); + for (File net : nets) { + strbuf.append(net.getPath()).append(File.separator).append("statistics") + .append(File.separator).append(whatStat); + total += getNumberFromFilePath(strbuf.toString()); + strbuf.setLength(0); + } + return total; + } + + private static long getMobileStat(String whatStat) { + String filename = "/sys/class/net/rmnet0/statistics/" + whatStat; + RandomAccessFile raf = getFile(filename); + if (raf == null) { + filename = "/sys/class/net/ppp0/statistics/" + whatStat; + raf = getFile(filename); + } + if (raf == null) { + return 0L; + } + return getNumberFromFile(raf, filename); + } + + // File will have format <number><newline> + private static long getNumberFromFilePath(String filename) { + RandomAccessFile raf = getFile(filename); + if (raf == null) { + return 0L; + } + return getNumberFromFile(raf, filename); + } + + private static synchronized long getNumberFromFile(RandomAccessFile raf, String filename) { + try { + raf.read(buf); + raf.close(); + } catch (IOException e) { + Log.w(TAG, "Exception getting TCP bytes from " + filename, e); + return 0L; + } finally { + if (raf != null) { + try { + raf.close(); + } catch (IOException e) { + Log.w(TAG, "Exception closing " + filename, e); + } + } + } + + long num = 0L; + for (int i = 0; i < buf.length; i++) { + if (buf[i] < '0' || buf[i] > '9') { + break; + } + num *= 10; + num += buf[i] - '0'; + } + return num; + } + + private static RandomAccessFile getFile(String filename) { + File f = new File(filename); + if (!f.canRead()) { + return null; + } + try { + return new RandomAccessFile(f, "r"); + } catch (IOException e) { + Log.w(TAG, "Exception opening TCP statistics file " + filename, e); + return null; + } + } } diff --git a/core/java/android/package.html b/core/java/android/package.html index b6d2999..1f1be2d 100644 --- a/core/java/android/package.html +++ b/core/java/android/package.html @@ -5,6 +5,6 @@ Contains the resource classes used by standard Android applications. This package contains the resource classes that Android defines to be used in Android applications. Third party developers can use many of them also for their applications. To learn more about how to use these classes, and what a -resource is, see <a href="{@docRoot}devel/resources-i18n.html">Resources</a>. +resource is, see <a href="{@docRoot}guide/topics/resources/index.html">Resources and Assets</a>. </BODY> </HTML> diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java index 95970ea..837ce91 100644 --- a/core/java/android/preference/PreferenceActivity.java +++ b/core/java/android/preference/PreferenceActivity.java @@ -103,8 +103,6 @@ public abstract class PreferenceActivity extends ListActivity implements protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - requestWindowFeature(Window.FEATURE_NO_TITLE); - setContentView(com.android.internal.R.layout.preference_list_content); mPreferenceManager = onCreatePreferenceManager(); @@ -214,6 +212,11 @@ public abstract class PreferenceActivity extends ListActivity implements public void setPreferenceScreen(PreferenceScreen preferenceScreen) { if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) { postBindPreferences(); + CharSequence title = getPreferenceScreen().getTitle(); + // Set the title of the activity + if (title != null) { + setTitle(title); + } } } diff --git a/core/java/android/preference/PreferenceGroup.java b/core/java/android/preference/PreferenceGroup.java index 4258b41..d008fd6 100644 --- a/core/java/android/preference/PreferenceGroup.java +++ b/core/java/android/preference/PreferenceGroup.java @@ -25,6 +25,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.os.Bundle; import android.os.Parcelable; +import android.text.TextUtils; import android.util.AttributeSet; /** @@ -223,6 +224,9 @@ public abstract class PreferenceGroup extends Preference implements GenericInfla * @return The {@link Preference} with the key, or null. */ public Preference findPreference(CharSequence key) { + if (TextUtils.equals(getKey(), key)) { + return this; + } final int preferenceCount = getPreferenceCount(); for (int i = 0; i < preferenceCount; i++) { final Preference preference = getPreference(i); diff --git a/core/java/android/provider/Checkin.java b/core/java/android/provider/Checkin.java index ef5eded..5767c65 100644 --- a/core/java/android/provider/Checkin.java +++ b/core/java/android/provider/Checkin.java @@ -30,10 +30,13 @@ import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; /** - * Contract class for {@link android.server.checkin.CheckinProvider}. + * Contract class for the checkin provider, used to store events and + * statistics that will be uploaded to a checkin server eventually. * Describes the exposed database schema, and offers methods to add * events and statistics to be uploaded. * + * TODO: Move this to vendor/google when we have a home for it. + * * @hide */ public final class Checkin { @@ -56,6 +59,12 @@ public final class Checkin { /** Valid tag values. Extend as necessary for your needs. */ public enum Tag { + AUTOTEST_FAILURE, + AUTOTEST_SEQUENCE_BEGIN, + AUTOTEST_SUITE_BEGIN, + AUTOTEST_TEST_BEGIN, + AUTOTEST_TEST_FAILURE, + AUTOTEST_TEST_SUCCESS, BROWSER_BUG_REPORT, CARRIER_BUG_REPORT, CHECKIN_FAILURE, @@ -195,8 +204,11 @@ public final class Checkin { values.put(Events.TAG, tag.toString()); if (value != null) values.put(Events.VALUE, value); return resolver.insert(Events.CONTENT_URI, values); + } catch (IllegalArgumentException e) { // thrown when provider is unavailable. + Log.w(TAG, "Can't log event " + tag + ": " + e); + return null; } catch (SQLException e) { - Log.e(TAG, "Can't log event: " + tag, e); // Database errors are not fatal. + Log.e(TAG, "Can't log event " + tag, e); // Database errors are not fatal. return null; } } @@ -218,8 +230,11 @@ public final class Checkin { if (count != 0) values.put(Stats.COUNT, count); if (sum != 0.0) values.put(Stats.SUM, sum); return resolver.insert(Stats.CONTENT_URI, values); + } catch (IllegalArgumentException e) { // thrown when provider is unavailable. + Log.w(TAG, "Can't update stat " + tag + ": " + e); + return null; } catch (SQLException e) { - Log.e(TAG, "Can't update stat: " + tag, e); // Database errors are not fatal. + Log.e(TAG, "Can't update stat " + tag, e); // Database errors are not fatal. return null; } } @@ -285,4 +300,3 @@ public final class Checkin { } } } - diff --git a/core/java/android/provider/Contacts.java b/core/java/android/provider/Contacts.java index 085c095..3bffaec 100644 --- a/core/java/android/provider/Contacts.java +++ b/core/java/android/provider/Contacts.java @@ -932,27 +932,33 @@ public class Contacts { } /** - * This looks up the provider category defined in - * {@link android.provider.Im.ProviderCategories} from the predefined IM protocol id. + * This looks up the provider name defined in + * {@link android.provider.Im.ProviderNames} from the predefined IM protocol id. * This is used for interacting with the IM application. - * + * * @param protocol the protocol ID - * @return the provider category the IM app uses for the given protocol, or null if no + * @return the provider name the IM app uses for the given protocol, or null if no * provider is defined for the given protocol * @hide */ - public static String lookupProviderCategoryFromId(int protocol) { + public static String lookupProviderNameFromId(int protocol) { switch (protocol) { case PROTOCOL_GOOGLE_TALK: - return Im.ProviderCategories.GTALK; + return Im.ProviderNames.GTALK; case PROTOCOL_AIM: - return Im.ProviderCategories.AIM; + return Im.ProviderNames.AIM; case PROTOCOL_MSN: - return Im.ProviderCategories.MSN; + return Im.ProviderNames.MSN; case PROTOCOL_YAHOO: - return Im.ProviderCategories.YAHOO; + return Im.ProviderNames.YAHOO; case PROTOCOL_ICQ: - return Im.ProviderCategories.ICQ; + return Im.ProviderNames.ICQ; + case PROTOCOL_JABBER: + return Im.ProviderNames.JABBER; + case PROTOCOL_SKYPE: + return Im.ProviderNames.SKYPE; + case PROTOCOL_QQ: + return Im.ProviderNames.QQ; } return null; } diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index a5a30b9..4c58e0d 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -471,34 +471,40 @@ public final class Downloads implements BaseColumns { public static final int STATUS_UNHANDLED_REDIRECT = 493; /** - * This download couldn't be completed because there were - * too many redirects. + * This download couldn't be completed because of an + * unspecified unhandled HTTP code. */ - public static final int STATUS_TOO_MANY_REDIRECTS = 494; + public static final int STATUS_UNHANDLED_HTTP_CODE = 494; /** * This download couldn't be completed because of an - * unspecified unhandled HTTP code. + * error receiving or processing data at the HTTP level. */ - public static final int STATUS_UNHANDLED_HTTP_CODE = 495; + public static final int STATUS_HTTP_DATA_ERROR = 495; /** * This download couldn't be completed because of an - * error receiving or processing data at the HTTP level. + * HttpException while setting up the request. */ - public static final int STATUS_HTTP_DATA_ERROR = 496; + public static final int STATUS_HTTP_EXCEPTION = 496; /** - * This download is visible and shows in the notifications while - * in progress and after completion. + * This download couldn't be completed because there were + * too many redirects. */ - public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 0; + public static final int STATUS_TOO_MANY_REDIRECTS = 497; /** * This download is visible but only shows in the notifications * while it's in progress. */ - public static final int VISIBILITY_VISIBLE = 1; + public static final int VISIBILITY_VISIBLE = 0; + + /** + * This download is visible and shows in the notifications while + * in progress and after completion. + */ + public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 1; /** * This download doesn't show in the UI or in the notifications. diff --git a/core/java/android/provider/Gmail.java b/core/java/android/provider/Gmail.java index 325f19d..253dd4a 100644 --- a/core/java/android/provider/Gmail.java +++ b/core/java/android/provider/Gmail.java @@ -370,6 +370,25 @@ public final class Gmail { "maxAttachmentSize"; } + /** + * These flags can be included as Selection Arguments when + * querying the provider. + */ + public static class SelectionArguments { + private SelectionArguments() { + // forbid instantiation + } + + /** + * Specifies that you do NOT wish the returned cursor to + * become the Active Network Cursor. If you do not include + * this flag as a selectionArg, the new cursor will become the + * Active Network Cursor by default. + */ + public static final String DO_NOT_BECOME_ACTIVE_NETWORK_CURSOR = + "SELECTION_ARGUMENT_DO_NOT_BECOME_ACTIVE_NETWORK_CURSOR"; + } + // These are the projections that we need when getting cursors from the // content provider. private static String[] CONVERSATION_PROJECTION = { @@ -436,6 +455,28 @@ public final class Gmail { } /** + * Behavior for a new cursor: should it become the Active Network + * Cursor? This could potentially lead to bad behavior if someone + * else is using the Active Network Cursor, since theirs will stop + * being the Active Network Cursor. + */ + public static enum BecomeActiveNetworkCursor { + /** + * The new cursor should become the one and only Active + * Network Cursor. Any other cursor that might already be the + * Active Network Cursor will cease to be so. + */ + YES, + + /** + * The new cursor should not become the Active Network + * Cursor. Any other cursor that might already be the Active + * Network Cursor will continue to be so. + */ + NO + } + + /** * Wraps a Cursor in a ConversationCursor * * @param account the account the cursor is associated with @@ -450,6 +491,20 @@ public final class Gmail { } /** + * Creates an array of SelectionArguments suitable for passing to the provider's query. + * Currently this only handles one flag, but it could be expanded in the future. + */ + private static String[] getSelectionArguments( + BecomeActiveNetworkCursor becomeActiveNetworkCursor) { + if (BecomeActiveNetworkCursor.NO == becomeActiveNetworkCursor) { + return new String[] {SelectionArguments.DO_NOT_BECOME_ACTIVE_NETWORK_CURSOR}; + } else { + // Default behavior; no args required. + return null; + } + } + + /** * Asynchronously gets a cursor over all conversations matching a query. The * query is in Gmail's query syntax. When the operation is complete the handler's * onQueryComplete() method is called with the resulting Cursor. @@ -458,14 +513,17 @@ public final class Gmail { * @param handler An AsyncQueryHanlder that will be used to run the query * @param token The token to pass to startQuery, which will be passed back to onQueryComplete * @param query a query in Gmail's query syntax + * @param becomeActiveNetworkCursor whether or not the returned + * cursor should become the Active Network Cursor */ public void runQueryForConversations(String account, AsyncQueryHandler handler, int token, - String query) { + String query, BecomeActiveNetworkCursor becomeActiveNetworkCursor) { if (TextUtils.isEmpty(account)) { throw new IllegalArgumentException("account is empty"); } + String[] selectionArgs = getSelectionArguments(becomeActiveNetworkCursor); handler.startQuery(token, null, Uri.withAppendedPath(CONVERSATIONS_URI, account), - CONVERSATION_PROJECTION, query, null, null); + CONVERSATION_PROJECTION, query, selectionArgs, null); } /** @@ -474,11 +532,15 @@ public final class Gmail { * * @param account run the query on this account * @param query a query in Gmail's query syntax + * @param becomeActiveNetworkCursor whether or not the returned + * cursor should become the Active Network Cursor */ - public ConversationCursor getConversationCursorForQuery(String account, String query) { + public ConversationCursor getConversationCursorForQuery( + String account, String query, BecomeActiveNetworkCursor becomeActiveNetworkCursor) { + String[] selectionArgs = getSelectionArguments(becomeActiveNetworkCursor); Cursor cursor = mContentResolver.query( Uri.withAppendedPath(CONVERSATIONS_URI, account), CONVERSATION_PROJECTION, - query, null, null); + query, selectionArgs, null); return new ConversationCursor(this, account, cursor); } @@ -559,12 +621,10 @@ public final class Gmail { * server message id. * @param label the label to add or remove * @param add true to add the label, false to remove it - * @throws NonexistentLabelException thrown if the label does not exist */ public void addOrRemoveLabelOnConversation( String account, long conversationId, long maxServerMessageId, String label, - boolean add) - throws NonexistentLabelException { + boolean add) { if (TextUtils.isEmpty(account)) { throw new IllegalArgumentException("account is empty"); } @@ -599,7 +659,6 @@ public final class Gmail { * @param messageId the id of the message to whose labels should be changed * @param label the label to add or remove * @param add true to add the label, false to remove it - * @throws NonexistentLabelException thrown if the label does not exist */ public static void addOrRemoveLabelOnMessage(ContentResolver contentResolver, String account, long conversationId, long messageId, String label, boolean add) { @@ -1175,19 +1234,6 @@ public final class Gmail { } /** - * Thrown when an operation is requested with a label that does not exist. - * - * TODO: this is here because I wanted a checked exception. However, I don't - * think that that is appropriate. In fact, I don't think that we should - * throw an exception at all because the label might have been valid when - * the caller presented it to the user but removed as a result of a sync. - * Maybe we should kill this and eat the errors. - */ - public static class NonexistentLabelException extends Exception { - // TODO: Add label name? - } - - /** * A cursor over labels. */ public final class LabelCursor extends MailCursor { @@ -2021,10 +2067,8 @@ public final class Gmail { * * @param label the label to add or remove * @param add whether to add or remove the label - * @throws NonexistentLabelException thrown if the named label does not - * exist */ - public void addOrRemoveLabel(String label, boolean add) throws NonexistentLabelException { + public void addOrRemoveLabel(String label, boolean add) { addOrRemoveLabelOnMessage(mContentResolver, mAccount, getConversationId(), getMessageId(), label, add); } diff --git a/core/java/android/provider/Im.java b/core/java/android/provider/Im.java index 68b2acd..bea857f 100644 --- a/core/java/android/provider/Im.java +++ b/core/java/android/provider/Im.java @@ -78,21 +78,10 @@ public class Im { String MSN = "MSN"; String ICQ = "ICQ"; String AIM = "AIM"; - } - - /** - * The ProviderCategories definitions are used for the Intent category for the Intent - * - * Intent intent = new Intent(Intent.ACTION_SENDTO, - * Uri.fromParts("im", data, null)). - * addCategory(category); - */ - public interface ProviderCategories { - String GTALK = "com.android.im.category.GTALK"; - String AIM = "com.android.im.category.AIM"; - String MSN = "com.android.im.category.MSN"; - String YAHOO = "com.android.im.category.YAHOO"; - String ICQ = "com.android.im.category.ICQ"; + String XMPP = "XMPP"; + String JABBER = "JABBER"; + String SKYPE = "SKYPE"; + String QQ = "QQ"; } /** @@ -140,54 +129,6 @@ public class Im { return retVal; } - /** - * This returns the provider name given a provider category. - * - * @param providerCategory the provider category defined in {@link ProviderCategories}. - * @return the corresponding provider name defined in {@link ProviderNames}. - */ - public static String getProviderNameForCategory(String providerCategory) { - if (providerCategory != null) { - if (providerCategory.equalsIgnoreCase(ProviderCategories.GTALK)) { - return ProviderNames.GTALK; - } else if (providerCategory.equalsIgnoreCase(ProviderCategories.AIM)) { - return ProviderNames.AIM; - } else if (providerCategory.equalsIgnoreCase(ProviderCategories.MSN)) { - return ProviderNames.MSN; - } else if (providerCategory.equalsIgnoreCase(ProviderCategories.YAHOO)) { - return ProviderNames.YAHOO; - } else if (providerCategory.equalsIgnoreCase(ProviderCategories.ICQ)) { - return ProviderNames.ICQ; - } - } - - return null; - } - - /** - * This returns the provider category given a provider name. - * - * @param providername the provider name defined in {@link ProviderNames}. - * @return the provider category defined in {@link ProviderCategories}. - */ - public static String getProviderCategoryFromName(String providername) { - if (providername != null) { - if (providername.equalsIgnoreCase(Im.ProviderNames.GTALK)) { - return Im.ProviderCategories.GTALK; - } else if (providername.equalsIgnoreCase(Im.ProviderNames.AIM)) { - return Im.ProviderCategories.AIM; - } else if (providername.equalsIgnoreCase(Im.ProviderNames.MSN)) { - return Im.ProviderCategories.MSN; - } else if (providername.equalsIgnoreCase(Im.ProviderNames.YAHOO)) { - return Im.ProviderCategories.YAHOO; - } else if (providername.equalsIgnoreCase(Im.ProviderNames.ICQ)) { - return Im.ProviderCategories.ICQ; - } - } - - return null; - } - private static final String[] PROVIDER_PROJECTION = new String[] { _ID, NAME @@ -797,6 +738,13 @@ public class Im { String ETAG = "etag"; /** + * The OTR etag, computed by the server, stored on the client. There is one OTR etag + * per account roster. + * <P>Type: TEXT</P> + */ + String OTR_ETAG = "otr_etag"; + + /** * The account id for the etag. * <P> Type: INTEGER </P> */ @@ -837,12 +785,38 @@ public class Im { return retVal; } + public static final String getOtrEtag(ContentResolver resolver, long accountId) { + String retVal = null; + + Cursor c = resolver.query(CONTENT_URI, + CONTACT_OTR_ETAG_PROJECTION, + ACCOUNT + "=" + accountId, + null /* selection args */, + null /* sort order */); + + try { + if (c.moveToFirst()) { + retVal = c.getString(COLUMN_OTR_ETAG); + } + } finally { + c.close(); + } + + return retVal; + } + private static final String[] CONTACT_ETAG_PROJECTION = new String[] { Im.ContactsEtag.ETAG // 0 }; private static int COLUMN_ETAG = 0; + private static final String[] CONTACT_OTR_ETAG_PROJECTION = new String[] { + Im.ContactsEtag.OTR_ETAG // 0 + }; + + private static int COLUMN_OTR_ETAG = 0; + /** * The content:// style URL for this table */ @@ -1271,9 +1245,9 @@ public class Im { } /** - * Columns shared between the IM and contacts presence tables + * Common presence columns shared between the IM and contacts presence tables */ - interface CommonPresenceColumns { + public interface CommonPresenceColumns { /** * The priority, an integer, used by XMPP presence * <P>Type: INTEGER</P> diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 87a02e6..2897b4e 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -91,14 +91,14 @@ public final class MediaStore public static final String EXTRA_SCREEN_ORIENTATION = "android.intent.extra.screenOrientation"; /** - * The name of the Intent-extra used to control the orientation of a ViewImage. + * The name of an Intent-extra used to control the UI of a ViewImage. * This is a boolean property that overrides the activity's default fullscreen state. * @hide */ public static final String EXTRA_FULL_SCREEN = "android.intent.extra.fullScreen"; /** - * The name of the Intent-extra used to control the orientation of a ViewImage. + * The name of an Intent-extra used to control the UI of a ViewImage. * This is a boolean property that specifies whether or not to show action icons. * @hide */ @@ -117,25 +117,36 @@ public final class MediaStore */ public static final String INTENT_ACTION_STILL_IMAGE_CAMERA = "android.media.action.STILL_IMAGE_CAMERA"; - /** * The name of the Intent action used to launch a camera in video mode. */ public static final String INTENT_ACTION_VIDEO_CAMERA = "android.media.action.VIDEO_CAMERA"; /** - * Standard Intent action that can be sent to have the media application - * capture an image and return it. The image is returned as a Bitmap - * object in the extra field. - * @hide + * Standard Intent action that can be sent to have the camera application + * capture an image and return it. + * <p> + * The caller may pass an extra EXTRA_OUTPUT to control where this image will be written. + * If the EXTRA_OUTPUT is not present, then a small sized image is returned as a Bitmap + * object in the extra field. This is useful for applications that only need a small image. + * If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri + * value of EXTRA_OUTPUT. + * @see #EXTRA_OUTPUT + * @see #EXTRA_VIDEO_QUALITY */ public final static String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE"; /** - * Standard Intent action that can be sent to have the media application - * capture an video and return it. The caller may pass in an extra EXTRA_VIDEO_QUALITY - * control the video quality. - * @hide + * Standard Intent action that can be sent to have the camera application + * capture an video and return it. + * <p> + * The caller may pass in an extra EXTRA_VIDEO_QUALITY to control the video quality. + * <p> + * The caller may pass in an extra EXTRA_OUTPUT to control + * where the video is written. If EXTRA_OUTPUT is not present the video will be + * written to the standard location for videos, and the Uri of that location will be + * returned in the data field of the Uri. + * @see #EXTRA_OUTPUT */ public final static String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE"; @@ -143,14 +154,12 @@ public final class MediaStore * The name of the Intent-extra used to control the quality of a recorded video. This is an * integer property. Currently value 0 means low quality, suitable for MMS messages, and * value 1 means high quality. In the future other quality levels may be added. - * @hide */ public final static String EXTRA_VIDEO_QUALITY = "android.intent.extra.videoQuality"; /** - * The name of the Intent-extra used to indicate a Uri to be used to + * The name of the Intent-extra used to indicate a content resolver Uri to be used to * store the requested image or video. - * @hide */ public final static String EXTRA_OUTPUT = "output"; @@ -471,7 +480,7 @@ public final class MediaStore /** * The default sort order for this table */ - public static final String DEFAULT_SORT_ORDER = "name ASC"; + public static final String DEFAULT_SORT_ORDER = ImageColumns.BUCKET_DISPLAY_NAME; } public static class Thumbnails implements BaseColumns @@ -582,6 +591,14 @@ public final class MediaStore public static final String DURATION = "duration"; /** + * The position, in ms, playback was at when playback for this file + * was last stopped. + * <P>Type: INTEGER (long)</P> + * @hide + */ + public static final String BOOKMARK = "bookmark"; + + /** * The id of the artist who created the audio file, if any * <P>Type: INTEGER (long)</P> */ @@ -654,6 +671,13 @@ public final class MediaStore public static final String IS_MUSIC = "is_music"; /** + * Non-zero if the audio file is a podcast + * <P>Type: INTEGER (boolean)</P> + * @hide + */ + public static final String IS_PODCAST = "is_podcast"; + + /** * Non-zero id the audio file may be a ringtone * <P>Type: INTEGER (boolean)</P> */ @@ -1198,22 +1222,15 @@ public final class MediaStore } public static final class Video { - /** - * deprecated Replaced by DEFAULT_SORT_ORDER2 - * This variable is a mistake that is retained for backwards compatibility. - * (There is no "name" column in the Video table.) - */ - public static final String DEFAULT_SORT_ORDER = "name ASC"; /** - * The default sort order for this table - * @hide + * The default sort order for this table. */ - public static final String DEFAULT_SORT_ORDER2 = MediaColumns.DISPLAY_NAME; + public static final String DEFAULT_SORT_ORDER = MediaColumns.DISPLAY_NAME; public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) { - return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER2); + return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER); } public interface VideoColumns extends MediaColumns { @@ -1316,7 +1333,6 @@ public final class MediaStore * video should start playing at the next time it is opened. If the value is null or * out of the range 0..DURATION-1 then the video should start playing from the * beginning. - * @hide * <P>Type: INTEGER</P> */ public static final String BOOKMARK = "bookmark"; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index abbfd5b..054da1d 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -235,6 +235,35 @@ public final class Settings { "android.settings.LOCALE_SETTINGS"; /** + * Activity Action: Show settings to configure input methods, in particular + * allowing the user to enable input methods. + * <p> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_INPUT_METHOD_SETTINGS = + "android.settings.INPUT_METHOD_SETTINGS"; + + /** + * Activity Action: Show settings to manage the user input dictionary. + * <p> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_USER_DICTIONARY_SETTINGS = + "android.settings.USER_DICTIONARY_SETTINGS"; + + /** * Activity Action: Show settings to allow configuration of application-related settings. * <p> * In some cases, a matching Activity may not exist, so ensure you @@ -1233,6 +1262,13 @@ public final class Settings { public static final String TRANSITION_ANIMATION_SCALE = "transition_animation_scale"; /** + * Scaling factor for normal window animations. Setting to 0 will disable window + * animations. + * @hide + */ + public static final String FANCY_IME_ANIMATIONS = "fancy_ime_animations"; + + /** * Control whether the accelerometer will be used to change screen * orientation. If 0, it will not be used unless explicitly requested * by the application; if 1, it will be used by default unless explicitly @@ -2008,6 +2044,14 @@ public final class Settings { */ public static final String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS = "wifi_mobile_data_transition_wakelock_timeout_ms"; + + /** + * Whether background data usage is allowed by the user. See + * ConnectivityManager for more info. + * + * @hide pending API council + */ + public static final String BACKGROUND_DATA = "background_data"; } /** @@ -2130,6 +2174,11 @@ public final class Settings { * Event tags from the kernel event log to upload during checkin. */ public static final String CHECKIN_EVENTS = "checkin_events"; + + /** + * Event tags for list of services to upload during checkin. + */ + public static final String CHECKIN_DUMPSYS_LIST = "checkin_dumpsys_list"; /** * The interval (in seconds) between periodic checkin attempts. @@ -2704,8 +2753,27 @@ public final class Settings { * Speech encoding used with voice search on WIFI networks. To be factored out of this class. */ public static final String VOICE_SEARCH_ENCODING_WIFI = "voice_search_encoding_wifi"; - - + + /** + * Prefix for rules that launch automatic instrumentation test cycles. + * The format is the InstrumentationTestRunner (or compatible) package and class, + * followed optionally by space-separated key value pairs to pass to it. + */ + public static final String AUTOTEST_PREFIX = "autotest:"; + + /** + * Interval between synchronous checkins forced by the automatic test runner. + * If you set this to a value smaller than CHECKIN_INTERVAL, then the test runner's + * frequent checkins will prevent asynchronous background checkins from interfering + * with any performance measurements. + */ + public static final String AUTOTEST_CHECKIN_SECONDS = "autotest_checkin_seconds"; + + /** + * Interval between reboots forced by the automatic test runner. + */ + public static final String AUTOTEST_REBOOT_SECONDS = "autotest_reboot_seconds"; + /** * @deprecated * @hide @@ -2912,9 +2980,10 @@ public final class Settings { * * @param context A context. * @param cursor A cursor pointing to the row whose title should be - * returned. The cursor must contain at least the - * {@link #TITLE} and {@link #INTENT} columns. - * @return A title that is localized and can be displayed to the user. + * returned. The cursor must contain at least the {@link #TITLE} + * and {@link #INTENT} columns. + * @return A title that is localized and can be displayed to the user, + * or the empty string if one could not be found. */ public static CharSequence getTitle(Context context, Cursor cursor) { int titleColumn = cursor.getColumnIndex(TITLE); @@ -2943,7 +3012,7 @@ public final class Settings { PackageManager packageManager = context.getPackageManager(); ResolveInfo info = packageManager.resolveActivity(intent, 0); - return info.loadLabel(packageManager); + return info != null ? info.loadLabel(packageManager) : ""; } } diff --git a/core/java/android/provider/Sync.java b/core/java/android/provider/Sync.java index 94bf807..628852f 100644 --- a/core/java/android/provider/Sync.java +++ b/core/java/android/provider/Sync.java @@ -273,6 +273,7 @@ public final class Sync { public static final int ERROR_CONFLICT = 5; public static final int ERROR_TOO_MANY_DELETIONS = 6; public static final int ERROR_TOO_MANY_RETRIES = 7; + public static final int ERROR_INTERNAL = 8; // The MESG column will contain one of these or one of the Error types. public static final String MESG_SUCCESS = "success"; @@ -292,6 +293,7 @@ public final class Sync { case ERROR_CONFLICT: return "conflict detected"; case ERROR_TOO_MANY_DELETIONS: return "too many deletions"; case ERROR_TOO_MANY_RETRIES: return "too many retries"; + case ERROR_INTERNAL: return "internal error"; default: return "unknown error"; } } @@ -491,11 +493,6 @@ public final class Sync { /** controls whether or not the device listens for sync tickles */ public static final String SETTING_LISTEN_FOR_TICKLES = "listen_for_tickles"; - /** controls whether or not the device connect to Google in background for various - * stuff, including GTalk, checkin, Market and data sync ... - */ - public static final String SETTING_BACKGROUND_DATA = "background_data"; - /** controls whether or not the individual provider is synced when tickles are received */ public static final String SETTING_SYNC_PROVIDER_PREFIX = "sync_provider_"; @@ -572,18 +569,6 @@ public final class Sync { putBoolean(contentResolver, SETTING_LISTEN_FOR_TICKLES, flag); } - /** - * A convenience method to set whether or not the device should connect to Google - * in background. - * - * @param contentResolver the ContentResolver to use to access the settings table - * @param flag true if it should connect. - */ - static public void setBackgroundData(ContentResolver contentResolver, - boolean flag) { - putBoolean(contentResolver, SETTING_BACKGROUND_DATA, flag); - } - public static class QueryMap extends ContentQueryMap { private ContentResolver mContentResolver; @@ -632,24 +617,6 @@ public final class Sync { } /** - * Set whether or not the device should connect to Google in background - * - * @param flag true if it should - */ - public void setBackgroundData(boolean flag) { - Settings.setBackgroundData(mContentResolver, flag); - } - - /** - * Check if the device should connect to Google in background. - - * @return true if it should - */ - public boolean getBackgroundData() { - return getBoolean(SETTING_BACKGROUND_DATA, true); - } - - /** * Convenience function for retrieving a single settings value * as a boolean. * diff --git a/core/java/android/provider/UserDictionary.java b/core/java/android/provider/UserDictionary.java index 58e5731..5a7ef85 100644 --- a/core/java/android/provider/UserDictionary.java +++ b/core/java/android/provider/UserDictionary.java @@ -25,10 +25,13 @@ import android.net.Uri; import android.text.TextUtils; /** - * - * @hide Pending API council approval + * A provider of user defined words for input methods to use for predictive text input. + * Applications and input methods may add words into the dictionary. Words can have associated + * frequency information and locale information. */ public class UserDictionary { + + /** Authority string for this provider. */ public static final String AUTHORITY = "user_dictionary"; /** @@ -39,7 +42,6 @@ public class UserDictionary { /** * Contains the user defined words. - * @hide Pending API council approval */ public static class Words implements BaseColumns { /** @@ -67,14 +69,14 @@ public class UserDictionary { public static final String WORD = "word"; /** - * The frequency column. A value between 1 and 255. + * The frequency column. A value between 1 and 255. Higher values imply higher frequency. * <p>TYPE: INTEGER</p> */ public static final String FREQUENCY = "frequency"; /** * The locale that this word belongs to. Null if it pertains to all - * locales. Locale is a 5 letter string such as <pre>en_US</pre>. + * locales. Locale is as defined by the string returned by Locale.toString(). * <p>TYPE: TEXT</p> */ public static final String LOCALE = "locale"; @@ -85,8 +87,10 @@ public class UserDictionary { */ public static final String APP_ID = "appid"; + /** The locale type to specify that the word is common to all locales. */ public static final int LOCALE_TYPE_ALL = 0; + /** The locale type to specify that the word is for the current locale. */ public static final int LOCALE_TYPE_CURRENT = 1; /** @@ -94,7 +98,14 @@ public class UserDictionary { */ public static final String DEFAULT_SORT_ORDER = FREQUENCY + " DESC"; - + /** Adds a word to the dictionary, with the given frequency and the specified + * specified locale type. + * @param context the current application context + * @param word the word to add to the dictionary. This should not be null or + * empty. + * @param localeType the locale type for this word. It should be one of + * {@link #LOCALE_TYPE_ALL} or {@link #LOCALE_TYPE_CURRENT}. + */ public static void addWord(Context context, String word, int frequency, int localeType) { final ContentResolver resolver = context.getContentResolver(); diff --git a/core/java/android/provider/package.html b/core/java/android/provider/package.html index a553592..055b037 100644 --- a/core/java/android/provider/package.html +++ b/core/java/android/provider/package.html @@ -6,6 +6,6 @@ Android. as contact informations, calendar information, and media files. These classes provide simplified methods of adding or retrieving data from these content providers. For information about how to use a content provider, see <a -href="{@docRoot}devel/data.html">Reading and Writing Persistent Data</a>. +href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a>. </BODY> </HTML> diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java index 8486e4b..58f9491 100644 --- a/core/java/android/server/BluetoothA2dpService.java +++ b/core/java/android/server/BluetoothA2dpService.java @@ -55,6 +55,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; private static final String A2DP_SINK_ADDRESS = "a2dp_sink_address"; + private static final String BLUETOOTH_ENABLED = "bluetooth_enabled"; private final Context mContext; private final IntentFilter mIntentFilter; @@ -136,10 +137,28 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { BluetoothA2dp.STATE_DISCONNECTED)); } } + mAudioManager.setParameter(BLUETOOTH_ENABLED, "true"); } private synchronized void onBluetoothDisable() { - mAudioDevices = null; + if (mAudioDevices != null) { + for (String path : mAudioDevices.keySet()) { + switch (mAudioDevices.get(path).state) { + case BluetoothA2dp.STATE_CONNECTING: + case BluetoothA2dp.STATE_CONNECTED: + case BluetoothA2dp.STATE_PLAYING: + disconnectSinkNative(path); + updateState(path, BluetoothA2dp.STATE_DISCONNECTED); + break; + case BluetoothA2dp.STATE_DISCONNECTING: + updateState(path, BluetoothA2dp.STATE_DISCONNECTED); + break; + } + } + mAudioDevices = null; + } + mAudioManager.setBluetoothA2dpOn(false); + mAudioManager.setParameter(BLUETOOTH_ENABLED, "false"); } public synchronized int connectSink(String address) { @@ -289,6 +308,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { } } + updateState(path, BluetoothA2dp.STATE_CONNECTING); mAudioManager.setParameter(A2DP_SINK_ADDRESS, lookupAddress(path)); mAudioManager.setBluetoothA2dpOn(true); updateState(path, BluetoothA2dp.STATE_CONNECTED); @@ -309,6 +329,11 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private synchronized final String lookupAddress(String path) { if (mAudioDevices == null) return null; + SinkState sink = mAudioDevices.get(path); + if (sink == null) { + Log.w(TAG, "lookupAddress() called for unknown device " + path); + updateState(path, BluetoothA2dp.STATE_DISCONNECTED); + } String address = mAudioDevices.get(path).address; if (address == null) Log.e(TAG, "Can't find address for " + path); return address; @@ -349,6 +374,15 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { intent.putExtra(BluetoothA2dp.SINK_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothA2dp.SINK_STATE, state); mContext.sendBroadcast(intent, BLUETOOTH_PERM); + + if ((prevState == BluetoothA2dp.STATE_CONNECTED || + prevState == BluetoothA2dp.STATE_PLAYING) && + (state != BluetoothA2dp.STATE_CONNECTED && + state != BluetoothA2dp.STATE_PLAYING)) { + // disconnected + intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); + mContext.sendBroadcast(intent); + } } } diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java index 3ce34c3..d149761 100644 --- a/core/java/android/server/BluetoothDeviceService.java +++ b/core/java/android/server/BluetoothDeviceService.java @@ -91,8 +91,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { mIsDiscovering = false; mEventLoop = new BluetoothEventLoop(mContext, this); registerForAirplaneMode(); - - disableEsco(); // TODO: enable eSCO support once its fully supported } private native void initializeNativeDataNative(); @@ -130,10 +128,21 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } mEventLoop.stop(); disableNative(); + + // mark in progress bondings as cancelled + for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) { + mBondState.setBondState(address, BluetoothDevice.BOND_NOT_BONDED, + BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); + } + // update mode + Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION); + intent.putExtra(BluetoothIntent.SCAN_MODE, BluetoothDevice.SCAN_MODE_NONE); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); + mIsEnabled = false; - Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON, 0); + persistBluetoothOnSetting(false); mIsDiscovering = false; - Intent intent = new Intent(BluetoothIntent.DISABLED_ACTION); + intent = new Intent(BluetoothIntent.DISABLED_ACTION); mContext.sendBroadcast(intent, BLUETOOTH_PERM); return true; } @@ -202,18 +211,27 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { if (res) { mIsEnabled = true; - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.BLUETOOTH_ON, 1); + persistBluetoothOnSetting(true); mIsDiscovering = false; Intent intent = new Intent(BluetoothIntent.ENABLED_ACTION); mBondState.loadBondState(); mContext.sendBroadcast(intent, BLUETOOTH_PERM); mHandler.sendMessageDelayed(mHandler.obtainMessage(REGISTER_SDP_RECORDS), 3000); + + // Update mode + mEventLoop.onModeChanged(getModeNative()); } mEnableThread = null; } - }; + } + private void persistBluetoothOnSetting(boolean bluetoothOn) { + long origCallerIdentityToken = Binder.clearCallingIdentity(); + Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON, + bluetoothOn ? 1 : 0); + Binder.restoreCallingIdentity(origCallerIdentityToken); + } + private native int enableNative(); private native int disableNative(); @@ -288,10 +306,10 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { return state.intValue(); } - public synchronized String[] listBonds() { + private synchronized String[] listInState(int state) { ArrayList<String> result = new ArrayList<String>(mState.size()); for (Map.Entry<String, Integer> e : mState.entrySet()) { - if (e.getValue().intValue() == BluetoothDevice.BOND_BONDED) { + if (e.getValue().intValue() == state) { result.add(e.getKey()); } } @@ -353,18 +371,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } private native boolean setNameNative(String name); - public synchronized String getMajorClass() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getMajorClassNative(); - } - private native String getMajorClassNative(); - - public synchronized String getMinorClass() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getMinorClassNative(); - } - private native String getMinorClassNative(); - /** * Returns the user-friendly name of a remote device. This value is * retrned from our local cache, which is updated during device discovery. @@ -386,24 +392,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { /* pacakge */ native String getAdapterPathNative(); - /** - * Initiate a remote-device-discovery procedure. This procedure may be - * canceled by calling {@link #stopDiscovery}. Remote-device discoveries - * are returned as intents - * <p> - * Typically, when a remote device is found, your - * android.bluetooth.DiscoveryEventNotifier#notifyRemoteDeviceFound - * method will be invoked, and subsequently, your - * android.bluetooth.RemoteDeviceEventNotifier#notifyRemoteNameUpdated - * will tell you the user-friendly name of the remote device. However, - * it is possible that the name update may fail for various reasons, so you - * should display the device's Bluetooth address as soon as you get a - * notifyRemoteDeviceFound event, and update the name when you get the - * remote name. - * - * @return true if discovery has started, - * false otherwise. - */ public synchronized boolean startDiscovery(boolean resolveNames) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); @@ -411,12 +399,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } private native boolean startDiscoveryNative(boolean resolveNames); - /** - * Cancel a remote-device discovery. - * - * Note: you may safely call this method even when discovery has not been - * started. - */ public synchronized boolean cancelDiscovery() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); @@ -492,171 +474,23 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } private native boolean isConnectedNative(String address); - /** - * Detetermines whether this device is connectable (that is, whether remote - * devices can connect to it.) - * <p> - * Note: A Bluetooth adapter has separate connectable and discoverable - * states, and you could have any combination of those. Although - * any combination is possible (such as discoverable but not - * connectable), we restrict the possible combinations to one of - * three possibilities: discoverable and connectable, connectable - * but not discoverable, and neither connectable nor discoverable. - * - * @return true if this adapter is connectable - * false otherwise - * - * @see #isDiscoverable - * @see #getMode - * @see #setMode - */ - public synchronized boolean isConnectable() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return isConnectableNative(); - } - private native boolean isConnectableNative(); - - /** - * Detetermines whether this device is discoverable. - * - * Note: a Bluetooth adapter has separate connectable and discoverable - * states, and you could have any combination of those. Although - * any combination is possible (such as discoverable but not - * connectable), we restrict the possible combinations to one of - * three possibilities: discoverable and connectable, connectable - * but not discoverable, and neither connectable nor discoverable. - * - * @return true if this adapter is discoverable - * false otherwise - * - * @see #isConnectable - * @see #getMode - * @see #setMode - */ - public synchronized boolean isDiscoverable() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return isDiscoverableNative(); - } - private native boolean isDiscoverableNative(); - - /** - * Determines which one of three modes this adapter is in: discoverable and - * connectable, not discoverable but connectable, or neither. - * - * @return Mode enumeration containing the current mode. - * - * @see #setMode - */ - public synchronized int getMode() { + public synchronized int getScanMode() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - String mode = getModeNative(); - if (mode == null) { - return BluetoothDevice.MODE_UNKNOWN; - } - if (mode.equalsIgnoreCase("off")) { - return BluetoothDevice.MODE_OFF; - } - else if (mode.equalsIgnoreCase("connectable")) { - return BluetoothDevice.MODE_CONNECTABLE; - } - else if (mode.equalsIgnoreCase("discoverable")) { - return BluetoothDevice.MODE_DISCOVERABLE; - } - else { - return BluetoothDevice.MODE_UNKNOWN; - } + return bluezStringToScanMode(getModeNative()); } private native String getModeNative(); - /** - * Set the discoverability and connectability mode of this adapter. The - * possibilities are discoverable and connectable (MODE_DISCOVERABLE), - * connectable but not discoverable (MODE_CONNECTABLE), and neither - * (MODE_OFF). - * - * Note: MODE_OFF does not mean that the adapter is physically off. It - * may be neither discoverable nor connectable, but it could still - * initiate outgoing connections, or could participate in a - * connection initiated by a remote device before its mode was set - * to MODE_OFF. - * - * @param mode the new mode - * @see #getMode - */ - public synchronized boolean setMode(int mode) { + public synchronized boolean setScanMode(int mode) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); - switch (mode) { - case BluetoothDevice.MODE_OFF: - return setModeNative("off"); - case BluetoothDevice.MODE_CONNECTABLE: - return setModeNative("connectable"); - case BluetoothDevice.MODE_DISCOVERABLE: - return setModeNative("discoverable"); + String bluezMode = scanModeToBluezString(mode); + if (bluezMode != null) { + return setModeNative(bluezMode); } return false; } private native boolean setModeNative(String mode); - /** - * Retrieves the alias of a remote device. The alias is a local feature, - * and allows us to associate a name with a remote device that is different - * from that remote device's user-friendly name. The remote device knows - * nothing about this. The alias can be changed with - * {@link #setRemoteAlias}, and it may be removed with - * {@link #clearRemoteAlias} - * - * @param address Bluetooth address of remote device. - * - * @return The alias of the remote device. - */ - public synchronized String getRemoteAlias(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteAliasNative(address); - } - private native String getRemoteAliasNative(String address); - - /** - * Changes the alias of a remote device. The alias is a local feature, - * from that remote device's user-friendly name. The remote device knows - * nothing about this. The alias can be retrieved with - * {@link #getRemoteAlias}, and it may be removed with - * {@link #clearRemoteAlias}. - * - * @param address Bluetooth address of remote device - * @param alias Alias for the remote device - */ - public synchronized boolean setRemoteAlias(String address, String alias) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - if (alias == null || !BluetoothDevice.checkBluetoothAddress(address)) { - return false; - } - return setRemoteAliasNative(address, alias); - } - private native boolean setRemoteAliasNative(String address, String alias); - - /** - * Removes the alias of a remote device. The alias is a local feature, - * from that remote device's user-friendly name. The remote device knows - * nothing about this. The alias can be retrieved with - * {@link #getRemoteAlias}. - * - * @param address Bluetooth address of remote device - */ - public synchronized boolean clearRemoteAlias(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return false; - } - return clearRemoteAliasNative(address); - } - private native boolean clearRemoteAliasNative(String address); - public synchronized boolean disconnectRemoteDeviceAcl(String address) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); @@ -699,7 +533,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } mBondState.setBondState(address, BluetoothDevice.BOND_NOT_BONDED, - BluetoothDevice.UNBOND_REASON_CANCELLED); + BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); cancelBondingProcessNative(address); return true; } @@ -717,7 +551,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { public synchronized String[] listBonds() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mBondState.listBonds(); + return mBondState.listInState(BluetoothDevice.BOND_BONDED); } public synchronized int getBondState(String address) { @@ -919,69 +753,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { private native String lastUsedNative(String address); /** - * Gets the major device class of the specified device. - * Example: "computer" - * - * Note: This is simply a string desciption of the major class of the - * device-class information, which is returned as a 32-bit value - * during device discovery. - * - * @param address The Bluetooth address of the remote device. - * - * @return remote-device major class - * - * @see #getRemoteClass - */ - public synchronized String getRemoteMajorClass(String address) { - if (!BluetoothDevice.checkBluetoothAddress(address)) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return null; - } - return getRemoteMajorClassNative(address); - } - private native String getRemoteMajorClassNative(String address); - - /** - * Gets the minor device class of the specified device. - * Example: "laptop" - * - * Note: This is simply a string desciption of the minor class of the - * device-class information, which is returned as a 32-bit value - * during device discovery. - * - * @param address The Bluetooth address of the remote device. - * - * @return remote-device minor class - * - * @see #getRemoteClass - */ - public synchronized String getRemoteMinorClass(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteMinorClassNative(address); - } - private native String getRemoteMinorClassNative(String address); - - /** - * Gets the service classes of the specified device. - * Example: ["networking", "object transfer"] - * - * @return a String array with the descriptions of the service classes. - * - * @see #getRemoteClass - */ - public synchronized String[] getRemoteServiceClasses(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteServiceClassesNative(address); - } - private native String[] getRemoteServiceClassesNative(String address); - - /** * Gets the remote major, minor, and service classes encoded as a 32-bit * integer. * @@ -1188,16 +959,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { Settings.System.AIRPLANE_MODE_ON, 0) == 1; } - private static final String DISABLE_ESCO_PATH = "/sys/module/sco/parameters/disable_esco"; - private static void disableEsco() { - try { - FileWriter file = new FileWriter(DISABLE_ESCO_PATH); - file.write("Y"); - file.close(); - } catch (FileNotFoundException e) { - } catch (IOException e) {} - } - @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mIsEnabled) { @@ -1248,6 +1009,34 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { pw.println("\nmIsAirplaneSensitive = " + mIsAirplaneSensitive); } + /* package */ static int bluezStringToScanMode(String mode) { + if (mode == null) { + return BluetoothError.ERROR; + } + mode = mode.toLowerCase(); + if (mode.equals("off")) { + return BluetoothDevice.SCAN_MODE_NONE; + } else if (mode.equals("connectable")) { + return BluetoothDevice.SCAN_MODE_CONNECTABLE; + } else if (mode.equals("discoverable")) { + return BluetoothDevice.SCAN_MODE_CONNECTABLE_DISCOVERABLE; + } else { + return BluetoothError.ERROR; + } + } + + /* package */ static String scanModeToBluezString(int mode) { + switch (mode) { + case BluetoothDevice.SCAN_MODE_NONE: + return "off"; + case BluetoothDevice.SCAN_MODE_CONNECTABLE: + return "connectable"; + case BluetoothDevice.SCAN_MODE_CONNECTABLE_DISCOVERABLE: + return "discoverable"; + } + return null; + } + private static void log(String msg) { Log.d(TAG, msg); } diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 4f63f98..0f60fae 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -16,6 +16,7 @@ package android.server; +import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothError; @@ -114,9 +115,7 @@ class BluetoothEventLoop { public synchronized void stop() { if (mThread != null) { - mInterrupted = true; - try { mThread.join(); mThread = null; @@ -130,104 +129,86 @@ class BluetoothEventLoop { return mThread != null; } - public void onModeChanged(String mode) { - Intent intent = new Intent(BluetoothIntent.MODE_CHANGED_ACTION); - int intMode = BluetoothDevice.MODE_UNKNOWN; - if (mode.equalsIgnoreCase("off")) { - intMode = BluetoothDevice.MODE_OFF; - } - else if (mode.equalsIgnoreCase("connectable")) { - intMode = BluetoothDevice.MODE_CONNECTABLE; - } - else if (mode.equalsIgnoreCase("discoverable")) { - intMode = BluetoothDevice.MODE_DISCOVERABLE; + /*package*/ void onModeChanged(String bluezMode) { + int mode = BluetoothDeviceService.bluezStringToScanMode(bluezMode); + if (mode >= 0) { + Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION); + intent.putExtra(BluetoothIntent.SCAN_MODE, mode); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - intent.putExtra(BluetoothIntent.MODE, intMode); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onDiscoveryStarted() { + private void onDiscoveryStarted() { mBluetoothService.setIsDiscovering(true); Intent intent = new Intent(BluetoothIntent.DISCOVERY_STARTED_ACTION); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onDiscoveryCompleted() { + private void onDiscoveryCompleted() { mBluetoothService.setIsDiscovering(false); Intent intent = new Intent(BluetoothIntent.DISCOVERY_COMPLETED_ACTION); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onPairingRequest() { + private void onPairingRequest() { Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION); mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); } - public void onPairingCancel() { + private void onPairingCancel() { Intent intent = new Intent(BluetoothIntent.PAIRING_CANCEL_ACTION); mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); } - public void onRemoteDeviceFound(String address, int deviceClass, short rssi) { + private void onRemoteDeviceFound(String address, int deviceClass, short rssi) { Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); intent.putExtra(BluetoothIntent.CLASS, deviceClass); intent.putExtra(BluetoothIntent.RSSI, rssi); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onRemoteDeviceDisappeared(String address) { + private void onRemoteDeviceDisappeared(String address) { Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISAPPEARED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onRemoteClassUpdated(String address, int deviceClass) { + private void onRemoteClassUpdated(String address, int deviceClass) { Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CLASS_UPDATED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); intent.putExtra(BluetoothIntent.CLASS, deviceClass); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onRemoteDeviceConnected(String address) { + private void onRemoteDeviceConnected(String address) { Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onRemoteDeviceDisconnectRequested(String address) { + private void onRemoteDeviceDisconnectRequested(String address) { Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECT_REQUESTED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onRemoteDeviceDisconnected(String address) { + private void onRemoteDeviceDisconnected(String address) { Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECTED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onRemoteNameUpdated(String address, String name) { + private void onRemoteNameUpdated(String address, String name) { Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); intent.putExtra(BluetoothIntent.NAME, name); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onRemoteNameFailed(String address) { + private void onRemoteNameFailed(String address) { Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_FAILED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onRemoteNameChanged(String address, String name) { + private void onRemoteNameChanged(String address, String name) { Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); intent.putExtra(BluetoothIntent.NAME, name); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onRemoteAliasChanged(String address, String alias) { - Intent intent = new Intent(BluetoothIntent.REMOTE_ALIAS_CHANGED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - intent.putExtra(BluetoothIntent.ALIAS, alias); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - public void onRemoteAliasCleared(String address) { - Intent intent = new Intent(BluetoothIntent.REMOTE_ALIAS_CLEARED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } private void onCreateBondingResult(String address, int result) { address = address.toUpperCase(); @@ -239,23 +220,23 @@ class BluetoothEventLoop { } } - public void onBondingCreated(String address) { + private void onBondingCreated(String address) { mBluetoothService.getBondState().setBondState(address.toUpperCase(), BluetoothDevice.BOND_BONDED); } - public void onBondingRemoved(String address) { + private void onBondingRemoved(String address) { mBluetoothService.getBondState().setBondState(address.toUpperCase(), BluetoothDevice.BOND_NOT_BONDED, BluetoothDevice.UNBOND_REASON_REMOVED); } - public void onNameChanged(String name) { + private void onNameChanged(String name) { Intent intent = new Intent(BluetoothIntent.NAME_CHANGED_ACTION); intent.putExtra(BluetoothIntent.NAME, name); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onPasskeyAgentRequest(String address, int nativeData) { + private void onPasskeyAgentRequest(String address, int nativeData) { address = address.toUpperCase(); mPasskeyAgentRequestData.put(address, new Integer(nativeData)); @@ -284,14 +265,36 @@ class BluetoothEventLoop { mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); } - public void onPasskeyAgentCancel(String address) { + private void onPasskeyAgentCancel(String address) { address = address.toUpperCase(); mPasskeyAgentRequestData.remove(address); Intent intent = new Intent(BluetoothIntent.PAIRING_CANCEL_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_NOT_BONDED, - BluetoothDevice.UNBOND_REASON_CANCELLED); + BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); + } + + private boolean onAuthAgentAuthorize(String address, String service, String uuid) { + boolean authorized = false; + if (service.endsWith("service_audio")) { + BluetoothA2dp a2dp = new BluetoothA2dp(mContext); + authorized = a2dp.getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF; + if (authorized) { + Log.i(TAG, "Allowing incoming A2DP connection from " + address); + } else { + Log.i(TAG, "Rejecting incoming A2DP connection from " + address); + } + } else { + Log.i(TAG, "Rejecting incoming " + service + " connection from " + address); + } + return authorized; + } + + private void onAuthAgentCancel(String address, String service, String uuid) { + // We immediately response to DBUS Authorize() so this should not + // usually happen + log("onAuthAgentCancel(" + address + ", " + service + ", " + uuid + ")"); } private void onGetRemoteServiceChannelResult(String address, int channel) { diff --git a/core/java/android/server/checkin/CheckinProvider.java b/core/java/android/server/checkin/CheckinProvider.java deleted file mode 100644 index 86ece4a..0000000 --- a/core/java/android/server/checkin/CheckinProvider.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.server.checkin; - -import android.content.ContentProvider; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.pm.PackageManager; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.database.sqlite.SQLiteQueryBuilder; -import android.net.Uri; -import android.os.Environment; -import android.provider.BaseColumns; -import android.provider.Checkin; -import android.util.Log; - -import java.io.File; - -/** - * Content provider for the database used to store events and statistics - * while they wait to be uploaded by the checkin service. - */ -public class CheckinProvider extends ContentProvider { - /** Class identifier for logging. */ - private static final String TAG = "CheckinProvider"; - - /** Filename of database (in /data directory). */ - private static final String DATABASE_FILENAME = "checkin.db"; - - /** Version of database schema. */ - private static final int DATABASE_VERSION = 1; - - /** Maximum number of events recorded. */ - private static final int EVENT_LIMIT = 1000; - - /** Maximum size of individual event data. */ - private static final int EVENT_SIZE = 8192; - - /** Maximum number of crashes recorded. */ - private static final int CRASH_LIMIT = 25; - - /** Maximum size of individual crashes recorded. */ - private static final int CRASH_SIZE = 16384; - - /** Permission required for access to the 'properties' database. */ - private static final String PROPERTIES_PERMISSION = - "android.permission.ACCESS_CHECKIN_PROPERTIES"; - - /** Lock for stats read-modify-write update cycle (see {@link #insert}). */ - private final Object mStatsLock = new Object(); - - /** The underlying SQLite database. */ - private SQLiteOpenHelper mOpenHelper; - - private static class OpenHelper extends SQLiteOpenHelper { - public OpenHelper(Context context) { - super(context, DATABASE_FILENAME, null, DATABASE_VERSION); - - // The database used to live in /data/checkin.db. - File oldLocation = Environment.getDataDirectory(); - File old = new File(oldLocation, DATABASE_FILENAME); - File file = context.getDatabasePath(DATABASE_FILENAME); - - // Try to move the file to the new location. - // TODO: Remove this code before shipping. - if (old.exists() && !file.exists() && !old.renameTo(file)) { - Log.e(TAG, "Can't rename " + old + " to " + file); - } - if (old.exists() && !old.delete()) { - // Clean up the old data file in any case. - Log.e(TAG, "Can't remove " + old); - } - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + Checkin.Events.TABLE_NAME + " (" + - BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - Checkin.Events.TAG + " TEXT NOT NULL," + - Checkin.Events.VALUE + " TEXT DEFAULT \"\"," + - Checkin.Events.DATE + " INTEGER NOT NULL)"); - - db.execSQL("CREATE INDEX events_index ON " + - Checkin.Events.TABLE_NAME + " (" + - Checkin.Events.TAG + ")"); - - db.execSQL("CREATE TABLE " + Checkin.Stats.TABLE_NAME + " (" + - BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - Checkin.Stats.TAG + " TEXT UNIQUE," + - Checkin.Stats.COUNT + " INTEGER DEFAULT 0," + - Checkin.Stats.SUM + " REAL DEFAULT 0.0)"); - - db.execSQL("CREATE TABLE " + Checkin.Crashes.TABLE_NAME + " (" + - BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - Checkin.Crashes.DATA + " TEXT NOT NULL," + - Checkin.Crashes.LOGS + " TEXT)"); - - db.execSQL("CREATE TABLE " + Checkin.Properties.TABLE_NAME + " (" + - BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - Checkin.Properties.TAG + " TEXT UNIQUE ON CONFLICT REPLACE," - + Checkin.Properties.VALUE + " TEXT DEFAULT \"\")"); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int old, int version) { - db.execSQL("DROP TABLE IF EXISTS " + Checkin.Events.TABLE_NAME); - db.execSQL("DROP TABLE IF EXISTS " + Checkin.Stats.TABLE_NAME); - db.execSQL("DROP TABLE IF EXISTS " + Checkin.Crashes.TABLE_NAME); - db.execSQL("DROP TABLE IF EXISTS " + Checkin.Properties.TABLE_NAME); - onCreate(db); - } - } - - @Override public boolean onCreate() { - mOpenHelper = new OpenHelper(getContext()); - return true; - } - - @Override - public Cursor query(Uri uri, String[] select, - String where, String[] args, String sort) { - checkPermissions(uri); - SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - qb.setTables(uri.getPathSegments().get(0)); - if (uri.getPathSegments().size() == 2) { - qb.appendWhere("_id=" + ContentUris.parseId(uri)); - } else if (uri.getPathSegments().size() != 1) { - throw new IllegalArgumentException("Invalid query URI: " + uri); - } - - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); - Cursor cursor = qb.query(db, select, where, args, null, null, sort); - cursor.setNotificationUri(getContext().getContentResolver(), uri); - return cursor; - } - - @Override - public Uri insert(Uri uri, ContentValues values) { - checkPermissions(uri); - if (uri.getPathSegments().size() != 1) { - throw new IllegalArgumentException("Invalid insert URI: " + uri); - } - - long id; - String table = uri.getPathSegments().get(0); - if (Checkin.Events.TABLE_NAME.equals(table)) { - id = insertEvent(values); - } else if (Checkin.Stats.TABLE_NAME.equals(table)) { - id = insertStats(values); - } else if (Checkin.Crashes.TABLE_NAME.equals(table)) { - id = insertCrash(values); - } else { - id = mOpenHelper.getWritableDatabase().insert(table, null, values); - } - - if (id < 0) { - return null; - } else { - uri = ContentUris.withAppendedId(uri, id); - getContext().getContentResolver().notifyChange(uri, null); - return uri; - } - } - - /** - * Insert an entry into the events table. - * Trims old events from the table to keep the size bounded. - * @param values to insert - * @return the row ID of the new entry - */ - private long insertEvent(ContentValues values) { - String value = values.getAsString(Checkin.Events.VALUE); - if (value != null && value.length() > EVENT_SIZE) { - // Event values are readable text, so they can be truncated. - value = value.substring(0, EVENT_SIZE - 3) + "..."; - values.put(Checkin.Events.VALUE, value); - } - - if (!values.containsKey(Checkin.Events.DATE)) { - values.put(Checkin.Events.DATE, System.currentTimeMillis()); - } - - // TODO: Make this more efficient; don't do it on every insert. - // Also, consider keeping the most recent instance of every tag, - // and possibly update a counter when events are deleted. - - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - db.execSQL("DELETE FROM " + - Checkin.Events.TABLE_NAME + " WHERE " + - Checkin.Events._ID + " IN (SELECT " + - Checkin.Events._ID + " FROM " + - Checkin.Events.TABLE_NAME + " ORDER BY " + - Checkin.Events.DATE + " DESC LIMIT -1 OFFSET " + - (EVENT_LIMIT - 1) + ")"); - return db.insert(Checkin.Events.TABLE_NAME, null, values); - } - - /** - * Add an entry into the stats table. - * For statistics, instead of just inserting a row into the database, - * we add the count and sum values to the existing values (if any) - * for the specified tag. This must be done with a lock held, - * to avoid a race condition during the read-modify-write update. - * @param values to insert - * @return the row ID of the modified entry - */ - private long insertStats(ContentValues values) { - synchronized (mStatsLock) { - String tag = values.getAsString(Checkin.Stats.TAG); - if (tag == null) { - throw new IllegalArgumentException("Tag required:" + values); - } - - // Look for existing values with this tag. - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - Cursor cursor = db.query(false, - Checkin.Stats.TABLE_NAME, - new String[] { - Checkin.Stats._ID, - Checkin.Stats.COUNT, - Checkin.Stats.SUM - }, - Checkin.Stats.TAG + "=?", - new String[] { tag }, - null, null, null, null /* limit */); - - try { - if (cursor == null || !cursor.moveToNext()) { - // This is a new statistic, insert it directly. - return db.insert(Checkin.Stats.TABLE_NAME, null, values); - } else { - // Depend on SELECT column order to avoid getColumnIndex() - long id = cursor.getLong(0); - int count = cursor.getInt(1); - double sum = cursor.getDouble(2); - - Integer countAdd = values.getAsInteger(Checkin.Stats.COUNT); - if (countAdd != null) count += countAdd.intValue(); - - Double sumAdd = values.getAsDouble(Checkin.Stats.SUM); - if (sumAdd != null) sum += sumAdd.doubleValue(); - - if (count <= 0 && sum == 0.0) { - // Updated to nothing: delete the row! - cursor.deleteRow(); - getContext().getContentResolver().notifyChange( - ContentUris.withAppendedId(Checkin.Stats.CONTENT_URI, id), null); - return -1; - } else { - if (countAdd != null) cursor.updateInt(1, count); - if (sumAdd != null) cursor.updateDouble(2, sum); - cursor.commitUpdates(); - return id; - } - } - } finally { - // Always clean up the cursor. - if (cursor != null) cursor.close(); - } - } - } - - /** - * Add an entry into the crashes table. - * @param values to insert - * @return the row ID of the modified entry - */ - private long insertCrash(ContentValues values) { - try { - int crashSize = values.getAsString(Checkin.Crashes.DATA).length(); - if (crashSize > CRASH_SIZE) { - // The crash is too big. Don't report it, but do log a stat. - Checkin.updateStats(getContext().getContentResolver(), - Checkin.Stats.Tag.CRASHES_TRUNCATED, 1, 0.0); - throw new IllegalArgumentException("Too big: " + crashSize); - } - - // Count the number of crashes reported, even if they roll over. - Checkin.updateStats(getContext().getContentResolver(), - Checkin.Stats.Tag.CRASHES_REPORTED, 1, 0.0); - - // Trim the crashes database, if needed. - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - db.execSQL("DELETE FROM " + - Checkin.Crashes.TABLE_NAME + " WHERE " + - Checkin.Crashes._ID + " IN (SELECT " + - Checkin.Crashes._ID + " FROM " + - Checkin.Crashes.TABLE_NAME + " ORDER BY " + - Checkin.Crashes._ID + " DESC LIMIT -1 OFFSET " + - (CRASH_LIMIT - 1) + ")"); - - return db.insert(Checkin.Crashes.TABLE_NAME, null, values); - } catch (Throwable t) { - // To avoid an infinite crash-reporting loop, swallow the error. - Log.e("CheckinProvider", "Error inserting crash: " + t); - return -1; - } - } - - // TODO: optimize bulkInsert, especially for stats? - - @Override - public int update(Uri uri, ContentValues values, - String where, String[] args) { - checkPermissions(uri); - if (uri.getPathSegments().size() == 2) { - if (where != null && where.length() > 0) { - throw new UnsupportedOperationException( - "WHERE clause not supported for update: " + uri); - } - where = "_id=" + ContentUris.parseId(uri); - args = null; - } else if (uri.getPathSegments().size() != 1) { - throw new IllegalArgumentException("Invalid update URI: " + uri); - } - - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - int count = db.update(uri.getPathSegments().get(0), values, where, args); - getContext().getContentResolver().notifyChange(uri, null); - return count; - } - - @Override - public int delete(Uri uri, String where, String[] args) { - checkPermissions(uri); - if (uri.getPathSegments().size() == 2) { - if (where != null && where.length() > 0) { - throw new UnsupportedOperationException( - "WHERE clause not supported for delete: " + uri); - } - where = "_id=" + ContentUris.parseId(uri); - args = null; - } else if (uri.getPathSegments().size() != 1) { - throw new IllegalArgumentException("Invalid delete URI: " + uri); - } - - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - int count = db.delete(uri.getPathSegments().get(0), where, args); - getContext().getContentResolver().notifyChange(uri, null); - return count; - } - - @Override - public String getType(Uri uri) { - if (uri.getPathSegments().size() == 1) { - return "vnd.android.cursor.dir/" + uri.getPathSegments().get(0); - } else if (uri.getPathSegments().size() == 2) { - return "vnd.android.cursor.item/" + uri.getPathSegments().get(0); - } else { - throw new IllegalArgumentException("Invalid URI: " + uri); - } - } - - /** - * Make sure the caller has permission to the database. - * @param uri the caller is requesting access to - * @throws SecurityException if the caller is forbidden. - */ - private void checkPermissions(Uri uri) { - if (uri.getPathSegments().size() < 1) { - throw new IllegalArgumentException("Invalid query URI: " + uri); - } - - String table = uri.getPathSegments().get(0); - if (table.equals(Checkin.Properties.TABLE_NAME) && - getContext().checkCallingOrSelfPermission(PROPERTIES_PERMISSION) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Cannot access checkin properties"); - } - } -} diff --git a/core/java/android/server/checkin/FallbackCheckinService.java b/core/java/android/server/checkin/FallbackCheckinService.java deleted file mode 100644 index 65921af..0000000 --- a/core/java/android/server/checkin/FallbackCheckinService.java +++ /dev/null @@ -1,49 +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. - */ - -package android.server.checkin; - -import android.os.ICheckinService; -import android.os.RemoteException; -import android.os.IParentalControlCallback; -import com.google.android.net.ParentalControlState; - -/** - * @hide - */ -public final class FallbackCheckinService extends ICheckinService.Stub { - public FallbackCheckinService() { - } - - public void reportCrashSync(byte[] crashData) throws RemoteException { - } - - public void reportCrashAsync(byte[] crashData) throws RemoteException { - } - - public void masterClear() throws RemoteException { - } - - public void getParentalControlState(IParentalControlCallback p) throws RemoteException { - ParentalControlState state = new ParentalControlState(); - state.isEnabled = false; - p.onResult(state); - } - - public void getParentalControlState(IParentalControlCallback p, String requestingApp) - throws android.os.RemoteException { - } -} diff --git a/core/java/android/server/checkin/package.html b/core/java/android/server/checkin/package.html deleted file mode 100644 index 1c9bf9d..0000000 --- a/core/java/android/server/checkin/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body> - {@hide} -</body> -</html> diff --git a/core/java/android/server/search/SearchableInfo.java b/core/java/android/server/search/SearchableInfo.java index c8f395e..c18675e 100644 --- a/core/java/android/server/search/SearchableInfo.java +++ b/core/java/android/server/search/SearchableInfo.java @@ -86,6 +86,16 @@ public final class SearchableInfo implements Parcelable { private String mSuggestProviderPackage = null; private Context mCacheActivityContext = null; // use during setup only - don't hold memory! + // Flag values for Searchable_voiceSearchMode + private static int VOICE_SEARCH_SHOW_BUTTON = 1; + private static int VOICE_SEARCH_LAUNCH_WEB_SEARCH = 2; + private static int VOICE_SEARCH_LAUNCH_RECOGNIZER = 4; + private int mVoiceSearchMode = 0; + private int mVoiceLanguageModeId; // voiceLanguageModel + private int mVoicePromptTextId; // voicePromptText + private int mVoiceLanguageId; // voiceLanguage + private int mVoiceMaxResults; // voiceMaxResults + /** * Set the default searchable activity (when none is specified). */ @@ -420,7 +430,7 @@ public final class SearchableInfo implements Parcelable { mSearchInputType = a.getInt(com.android.internal.R.styleable.Searchable_inputType, InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_SEARCH | - InputType.TYPE_TEXT_VARIATION_SEARCH_STRING); + InputType.TYPE_TEXT_VARIATION_NORMAL); setSearchModeFlags(); if (DBG_INHIBIT_SUGGESTIONS == 0) { @@ -435,6 +445,18 @@ public final class SearchableInfo implements Parcelable { mSuggestIntentData = a.getString( com.android.internal.R.styleable.Searchable_searchSuggestIntentData); } + mVoiceSearchMode = + a.getInt(com.android.internal.R.styleable.Searchable_voiceSearchMode, 0); + // TODO this didn't work - came back zero from YouTube + mVoiceLanguageModeId = + a.getResourceId(com.android.internal.R.styleable.Searchable_voiceLanguageModel, 0); + mVoicePromptTextId = + a.getResourceId(com.android.internal.R.styleable.Searchable_voicePromptText, 0); + mVoiceLanguageId = + a.getResourceId(com.android.internal.R.styleable.Searchable_voiceLanguage, 0); + mVoiceMaxResults = + a.getInt(com.android.internal.R.styleable.Searchable_voiceMaxResults, 0); + a.recycle(); // get package info for suggestions provider (if any) @@ -462,11 +484,6 @@ public final class SearchableInfo implements Parcelable { * Convert searchmode to flags. */ private void setSearchModeFlags() { - // decompose searchMode attribute - // TODO How do I reconcile these hardcoded values with the flag bits defined in - // in attrs.xml? e.g. android.R.id.filterMode = 0x010200a4 instead of just "1" - /* mFilterMode = (0 != (mSearchMode & 1)); */ - /* mQuickStart = (0 != (mSearchMode & 2)); */ mBadgeLabel = (0 != (mSearchMode & 4)); mBadgeIcon = (0 != (mSearchMode & 8)) && (mIconId != 0); mQueryRewriteFromData = (0 != (mSearchMode & 0x10)); @@ -654,6 +671,59 @@ public final class SearchableInfo implements Parcelable { } /** + * @return true if android:voiceSearchMode="showVoiceSearchButton" + */ + public boolean getVoiceSearchEnabled() { + return 0 != (mVoiceSearchMode & VOICE_SEARCH_SHOW_BUTTON); + } + + /** + * @return true if android:voiceSearchMode="launchWebSearch" + */ + public boolean getVoiceSearchLaunchWebSearch() { + return 0 != (mVoiceSearchMode & VOICE_SEARCH_LAUNCH_WEB_SEARCH); + } + + /** + * @return true if android:voiceSearchMode="launchRecognizer" + */ + public boolean getVoiceSearchLaunchRecognizer() { + return 0 != (mVoiceSearchMode & VOICE_SEARCH_LAUNCH_RECOGNIZER); + } + + /** + * @return the resource Id of the language model string, if specified in the searchable + * activity's metadata, or 0 if not specified. + */ + public int getVoiceLanguageModeId() { + return mVoiceLanguageModeId; + } + + /** + * @return the resource Id of the voice prompt text string, if specified in the searchable + * activity's metadata, or 0 if not specified. + */ + public int getVoicePromptTextId() { + return mVoicePromptTextId; + } + + /** + * @return the resource Id of the spoken langauge, if specified in the searchable + * activity's metadata, or 0 if not specified. + */ + public int getVoiceLanguageId() { + return mVoiceLanguageId; + } + + /** + * @return the max results count, if specified in the searchable + * activity's metadata, or 0 if not specified. + */ + public int getVoiceMaxResults() { + return mVoiceMaxResults; + } + + /** * Return the resource Id of replacement text for the "Search" button. * * @return Returns the resource Id, or 0 if not specified by this package. @@ -727,6 +797,12 @@ public final class SearchableInfo implements Parcelable { } mSuggestProviderPackage = in.readString(); + + mVoiceSearchMode = in.readInt(); + mVoiceLanguageModeId = in.readInt(); + mVoicePromptTextId = in.readInt(); + mVoiceLanguageId = in.readInt(); + mVoiceMaxResults = in.readInt(); } public int describeContents() { @@ -764,5 +840,11 @@ public final class SearchableInfo implements Parcelable { } dest.writeString(mSuggestProviderPackage); + + dest.writeInt(mVoiceSearchMode); + dest.writeInt(mVoiceLanguageModeId); + dest.writeInt(mVoicePromptTextId); + dest.writeInt(mVoiceLanguageId); + dest.writeInt(mVoiceMaxResults); } } diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java index abbf8a7..987e763 100644 --- a/core/java/android/speech/RecognizerIntent.java +++ b/core/java/android/speech/RecognizerIntent.java @@ -22,8 +22,6 @@ import android.content.Intent; /** * Constants for supporting speech recognition through starting an {@link Intent} - * - * @hide {pending API council review} */ public class RecognizerIntent { private RecognizerIntent() { @@ -32,7 +30,8 @@ public class RecognizerIntent { /** * Starts an activity that will prompt the user for speech and sends it through a - * speech recognizer. + * speech recognizer. The results will be returned via activity results, or forwarded + * via a PendingIntent if one is provided. * * <p>Required extras: * <ul> @@ -41,9 +40,11 @@ public class RecognizerIntent { * * <p>Optional extras: * <ul> - * <li>{@link Intent#EXTRA_PROMPT} + * <li>{@link #EXTRA_PROMPT} * <li>{@link #EXTRA_LANGUAGE} * <li>{@link #EXTRA_MAX_RESULTS} + * <li>{@link #EXTRA_RESULTS_PENDINGINTENT} + * <li>{@link #EXTRA_RESULTS_PENDINGINTENT_BUNDLE} * </ul> * * <p> Result extras: @@ -57,6 +58,32 @@ public class RecognizerIntent { public static final String ACTION_RECOGNIZE_SPEECH = "android.speech.action.RECOGNIZE_SPEECH"; /** + * Starts an activity that will prompt the user for speech, sends it through a + * speech recognizer, and invokes and displays a web search result. + * + * <p>Required extras: + * <ul> + * <li>{@link #EXTRA_LANGUAGE_MODEL} + * </ul> + * + * <p>Optional extras: + * <ul> + * <li>{@link #EXTRA_PROMPT} + * <li>{@link #EXTRA_LANGUAGE} + * <li>{@link #EXTRA_MAX_RESULTS} + * </ul> + * + * <p> Result extras: + * <ul> + * <li>{@link #EXTRA_RESULTS} + * </ul> + * + * <p>NOTE: There may not be any applications installed to handle this action, so you should + * make sure to catch {@link ActivityNotFoundException}. + */ + public static final String ACTION_WEB_SEARCH = "android.speech.action.WEB_SEARCH"; + + /** * Informs the recognizer which speech model to prefer when performing * {@link #ACTION_RECOGNIZE_SPEECH}. The recognizer uses this * information to fine tune the results. This extra is required. Activities implementing @@ -65,27 +92,51 @@ public class RecognizerIntent { * @see #LANGUAGE_MODEL_FREE_FORM * @see #LANGUAGE_MODEL_WEB_SEARCH */ - public static final String EXTRA_LANGUAGE_MODEL = "language_model"; + public static final String EXTRA_LANGUAGE_MODEL = "android.speech.extra.LANGUAGE_MODEL"; - /** Free form speech recognition */ + /** + * Use a language model based on free-form speech recognition. This is a value to use for + * {@link #EXTRA_LANGUAGE_MODEL}. + * @see #EXTRA_LANGUAGE_MODEL + */ public static final String LANGUAGE_MODEL_FREE_FORM = "free_form"; - /** Use a language model based on web search terms */ + /** + * Use a language model based on web search terms. This is a value to use for + * {@link #EXTRA_LANGUAGE_MODEL}. + * @see #EXTRA_LANGUAGE_MODEL + */ public static final String LANGUAGE_MODEL_WEB_SEARCH = "web_search"; /** Optional text prompt to show to the user when asking them to speak. */ - public static final String EXTRA_PROMPT = "prompt"; + public static final String EXTRA_PROMPT = "android.speech.extra.PROMPT"; /** * Optional language override to inform the recognizer that it should expect speech in * a language different than the one set in the {@link java.util.Locale#getDefault()}. */ - public static final String EXTRA_LANGUAGE = "lang"; + public static final String EXTRA_LANGUAGE = "android.speech.extra.LANGUAGE"; /** * Optional limit on the maximum number of results to return. If omitted the recognizer * will choose how many results to return. Must be an integer. */ - public static final String EXTRA_MAX_RESULTS = "max_results"; + public static final String EXTRA_MAX_RESULTS = "android.speech.extra.MAX_RESULTS"; + + /** + * When the intent is {@link #ACTION_RECOGNIZE_SPEECH}, the speech input activity will + * return results to you via the activity results mechanism. Alternatively, if you use this + * extra to supply a PendingIntent, the results will be added to its bundle and the + * PendingIntent will be sent to its target. + */ + public static final String EXTRA_RESULTS_PENDINGINTENT = + "android.speech.extra.RESULTS_PENDINGINTENT"; + /** + * If you use {@link #EXTRA_RESULTS_PENDINGINTENT} to supply a forwarding intent, you can + * also use this extra to supply additional extras for the final intent. The search results + * will be added to this bundle, and the combined bundle will be sent to the target. + */ + public static final String EXTRA_RESULTS_PENDINGINTENT_BUNDLE = + "android.speech.extra.RESULTS_PENDINGINTENT_BUNDLE"; /** Result code returned when no matches are found for the given speech */ public static final int RESULT_NO_MATCH = Activity.RESULT_FIRST_USER; @@ -102,5 +153,5 @@ public class RecognizerIntent { * An ArrayList<String> of the potential results when performing * {@link #ACTION_RECOGNIZE_SPEECH}. Only present when {@link Activity#RESULT_OK} is returned. */ - public static final String EXTRA_RESULTS = "results"; + public static final String EXTRA_RESULTS = "android.speech.extra.RESULTS"; } diff --git a/core/java/android/text/Annotation.java b/core/java/android/text/Annotation.java index a3812a8..dbc290b 100644 --- a/core/java/android/text/Annotation.java +++ b/core/java/android/text/Annotation.java @@ -16,20 +16,40 @@ package android.text; +import android.os.Parcel; + /** * Annotations are simple key-value pairs that are preserved across * TextView save/restore cycles and can be used to keep application-specific * data that needs to be maintained for regions of text. */ -public class Annotation { - private String mKey; - private String mValue; +public class Annotation implements ParcelableSpan { + private final String mKey; + private final String mValue; public Annotation(String key, String value) { mKey = key; mValue = value; } + public Annotation(Parcel src) { + mKey = src.readString(); + mValue = src.readString(); + } + + public int getSpanTypeId() { + return TextUtils.ANNOTATION; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mKey); + dest.writeString(mValue); + } + public String getKey() { return mKey; } diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java index 90f5e4c..8495714 100644 --- a/core/java/android/text/Html.java +++ b/core/java/android/text/Html.java @@ -700,7 +700,41 @@ class HtmlToSpannedConverter implements ContentHandler { } public void characters(char ch[], int start, int length) throws SAXException { - mSpannableStringBuilder.append(CharBuffer.wrap(ch, start, length)); + StringBuilder sb = new StringBuilder(); + + /* + * Ignore whitespace that immediately follows other whitespace; + * newlines count as spaces. + */ + + for (int i = 0; i < length; i++) { + char c = ch[i + start]; + + if (c == ' ' || c == '\n') { + char pred; + int len = sb.length(); + + if (len == 0) { + len = mSpannableStringBuilder.length(); + + if (len == 0) { + pred = '\n'; + } else { + pred = mSpannableStringBuilder.charAt(len - 1); + } + } else { + pred = sb.charAt(len - 1); + } + + if (pred != ' ' && pred != '\n') { + sb.append(' '); + } + } else { + sb.append(c); + } + } + + mSpannableStringBuilder.append(sb); } public void ignorableWhitespace(char ch[], int start, int length) throws SAXException { diff --git a/core/java/android/text/InputType.java b/core/java/android/text/InputType.java index bd86834..a073cf4 100644 --- a/core/java/android/text/InputType.java +++ b/core/java/android/text/InputType.java @@ -116,14 +116,22 @@ public interface InputType { /** * Flag for {@link #TYPE_CLASS_TEXT}: multiple lines of text can be - * entered into the field. + * entered into the field. If this flag is not set, the text field + * will be constrained to a single line. */ public static final int TYPE_TEXT_FLAG_MULTI_LINE = 0x00020000; /** + * Flag for {@link #TYPE_CLASS_TEXT}: the regular text view associated + * with this should not be multi-line, but when a fullscreen input method + * is providing text it should use multiple lines if it can. + */ + public static final int TYPE_TEXT_FLAG_IME_MULTI_LINE = 0x00040000; + + /** * Flag for {@link #TYPE_CLASS_TEXT}: flags any text being used as a search string */ - public static final int TYPE_TEXT_FLAG_SEARCH = 0x00040000; + public static final int TYPE_TEXT_FLAG_SEARCH = 0x00080000; // ---------------------------------------------------------------------- @@ -149,30 +157,31 @@ public interface InputType { public static final int TYPE_TEXT_VARIATION_EMAIL_SUBJECT = 0x00000030; /** - * Variation of {@link #TYPE_CLASS_TEXT}: entering the content of - * an e-mail. + * Variation of {@link #TYPE_CLASS_TEXT}: entering a short, possibly informal + * message such as an instant message or a text message. */ - public static final int TYPE_TEXT_VARIATION_EMAIL_CONTENT = 0x00000040; + public static final int TYPE_TEXT_VARIATION_SHORT_MESSAGE = 0x00000040; /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering the content of a long, possibly + * formal message such as the body of an e-mail. + */ + public static final int TYPE_TEXT_VARIATION_LONG_MESSAGE = 0x00000050; + + /** * Variation of {@link #TYPE_CLASS_TEXT}: entering the name of a person. */ - public static final int TYPE_TEXT_VARIATION_PERSON_NAME = 0x00000050; + public static final int TYPE_TEXT_VARIATION_PERSON_NAME = 0x00000060; /** * Variation of {@link #TYPE_CLASS_TEXT}: entering a postal mailing address. */ - public static final int TYPE_TEXT_VARIATION_POSTAL_ADDRESS = 0x00000060; + public static final int TYPE_TEXT_VARIATION_POSTAL_ADDRESS = 0x00000070; /** * Variation of {@link #TYPE_CLASS_TEXT}: entering a password. */ - public static final int TYPE_TEXT_VARIATION_PASSWORD = 0x00000070; - - /** - * Variation of {@link #TYPE_CLASS_TEXT}: entering a simple text search (e.g. web search) - */ - public static final int TYPE_TEXT_VARIATION_SEARCH_STRING = 0x00000080; + public static final int TYPE_TEXT_VARIATION_PASSWORD = 0x00000080; /** * Variation of {@link #TYPE_CLASS_TEXT}: entering text inside of a web form. diff --git a/core/java/android/text/NoCopySpan.java b/core/java/android/text/NoCopySpan.java new file mode 100644 index 0000000..0855c0b --- /dev/null +++ b/core/java/android/text/NoCopySpan.java @@ -0,0 +1,31 @@ +/* + * 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. + */ + +package android.text; + +/** + * This interface should be added to a span object that should not be copied + * into a new Spenned when performing a slice or copy operation on the original + * Spanned it was placed in. + */ +public interface NoCopySpan { + /** + * Convenience equivalent for when you would just want a new Object() for + * a span but want it to be no-copy. Use this instead. + */ + public class Concrete implements NoCopySpan { + } +} diff --git a/core/java/android/text/ParcelableSpan.java b/core/java/android/text/ParcelableSpan.java new file mode 100644 index 0000000..224511a --- /dev/null +++ b/core/java/android/text/ParcelableSpan.java @@ -0,0 +1,31 @@ +/* + * 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. + */ + +package android.text; + +import android.os.Parcelable; + +/** + * A special kind of Parcelable for objects that will serve as text spans. + * This can only be used by code in the framework; it is not intended for + * applications to implement their own Parcelable spans. + */ +public interface ParcelableSpan extends Parcelable { + /** + * Return a special type identifier for this span class. + */ + public abstract int getSpanTypeId(); +} diff --git a/core/java/android/text/Selection.java b/core/java/android/text/Selection.java index 44469ec..bb98bce 100644 --- a/core/java/android/text/Selection.java +++ b/core/java/android/text/Selection.java @@ -72,7 +72,7 @@ public class Selection { if (ostart != start || oend != stop) { text.setSpan(SELECTION_START, start, start, - Spanned.SPAN_POINT_POINT); + Spanned.SPAN_POINT_POINT|Spanned.SPAN_INTERMEDIATE); text.setSpan(SELECTION_END, stop, stop, Spanned.SPAN_POINT_POINT); } @@ -417,8 +417,8 @@ public class Selection { } } - private static final class START { }; - private static final class END { }; + private static final class START implements NoCopySpan { }; + private static final class END implements NoCopySpan { }; /* * Public constants diff --git a/core/java/android/text/SpanWatcher.java b/core/java/android/text/SpanWatcher.java index f99882a..01e82c8 100644 --- a/core/java/android/text/SpanWatcher.java +++ b/core/java/android/text/SpanWatcher.java @@ -21,7 +21,7 @@ package android.text; * will be called to notify it that other markup objects have been * added, changed, or removed. */ -public interface SpanWatcher { +public interface SpanWatcher extends NoCopySpan { /** * This method is called to notify you that the specified object * has been attached to the specified range of the text. diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index 223ce2f..caaafa1 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -70,6 +70,10 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable, Object[] spans = sp.getSpans(start, end, Object.class); for (int i = 0; i < spans.length; i++) { + if (spans[i] instanceof NoCopySpan) { + continue; + } + int st = sp.getSpanStart(spans[i]) - start; int en = sp.getSpanEnd(spans[i]) - start; int fl = sp.getSpanFlags(spans[i]); diff --git a/core/java/android/text/Spanned.java b/core/java/android/text/Spanned.java index bd0a16b..154497d 100644 --- a/core/java/android/text/Spanned.java +++ b/core/java/android/text/Spanned.java @@ -106,6 +106,14 @@ extends CharSequence public static final int SPAN_COMPOSING = 0x100; /** + * This flag will be set for intermediate span changes, meaning there + * is guaranteed to be another change following it. Typically it is + * used for {@link Selection} which automatically uses this with the first + * offset it sets when updating the selection. + */ + public static final int SPAN_INTERMEDIATE = 0x200; + + /** * The bits numbered SPAN_USER_SHIFT and above are available * for callers to use to store scalar data associated with their * span object. diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index ceb9f4f..0fef40b 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -576,7 +576,28 @@ extends Layout if (fmbottom > fitbottom) fitbottom = fmbottom; - if (c == ' ' || c == '\t') { + /* + * From the Unicode Line Breaking Algorithm: + * (at least approximately) + * + * .,:; are class IS: breakpoints + * except when adjacent to digits + * / is class SY: a breakpoint + * except when followed by a digit. + * - is class HY: a breakpoint + * except when followed by a digit. + * + * Ideographs are class ID: breakpoints when adjacent. + */ + + if (c == ' ' || c == '\t' || + ((c == '.' || c == ',' || c == ':' || c == ';') && + (j - 1 < here || !Character.isDigit(chs[j - 1 - start])) && + (j + 1 >= next || !Character.isDigit(chs[j + 1 - start]))) || + ((c == '/' || c == '-') && + (j + 1 >= next || !Character.isDigit(chs[j + 1 - start]))) || + (c >= FIRST_CJK && isIdeographic(c) && + j + 1 < next && isIdeographic(chs[j + 1 - start]))) { okwidth = w; ok = j + 1; @@ -592,6 +613,11 @@ extends Layout } else if (breakOnlyAtSpaces) { if (ok != here) { // Log.e("text", "output ok " + here + " to " +ok); + + while (ok < next && chs[ok - start] == ' ') { + ok++; + } + v = out(source, here, ok, okascent, okdescent, oktop, okbottom, @@ -623,6 +649,11 @@ extends Layout } else { if (ok != here) { // Log.e("text", "output ok " + here + " to " +ok); + + while (ok < next && chs[ok - start] == ' ') { + ok++; + } + v = out(source, here, ok, okascent, okdescent, oktop, okbottom, @@ -739,6 +770,51 @@ extends Layout } } + private static final char FIRST_CJK = '\u2E80'; + /** + * Returns true if the specified character is one of those specified + * as being Ideographic (class ID) by the Unicode Line Breaking Algorithm + * (http://www.unicode.org/unicode/reports/tr14/), and is therefore OK + * to break between a pair of. + */ + private static final boolean isIdeographic(char c) { + if (c >= '\u2E80' && c <= '\u2FFF') { + return true; // CJK, KANGXI RADICALS, DESCRIPTION SYMBOLS + } + if (c == '\u3000') { + return true; // IDEOGRAPHIC SPACE + } + if (c >= '\u3040' && c <= '\u309F') { + return true; // Hiragana (except small characters) + } + if (c >= '\u30A0' && c <= '\u30FF') { + return true; // Katakana (except small characters) + } + if (c >= '\u3400' && c <= '\u4DB5') { + return true; // CJK UNIFIED IDEOGRAPHS EXTENSION A + } + if (c >= '\u4E00' && c <= '\u9FBB') { + return true; // CJK UNIFIED IDEOGRAPHS + } + if (c >= '\uF900' && c <= '\uFAD9') { + return true; // CJK COMPATIBILITY IDEOGRAPHS + } + if (c >= '\uA000' && c <= '\uA48F') { + return true; // YI SYLLABLES + } + if (c >= '\uA490' && c <= '\uA4CF') { + return true; // YI RADICALS + } + if (c >= '\uFE62' && c <= '\uFE66') { + return true; // SMALL PLUS SIGN to SMALL EQUALS SIGN + } + if (c >= '\uFF10' && c <= '\uFF19') { + return true; // WIDE DIGITS + } + + return false; + } + /* private static void dump(byte[] data, int count, String label) { if (false) { diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 405d934..5b4c380 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -236,6 +236,13 @@ public class TextUtils { return match; } + /** + * Create a new String object containing the given range of characters + * from the source string. This is different than simply calling + * {@link CharSequence#subSequence(int, int) CharSequence.subSequence} + * in that it does not preserve any style runs in the source sequence, + * allowing a more efficient implementation. + */ public static String substring(CharSequence source, int start, int end) { if (source instanceof String) return ((String) source).substring(start, end); @@ -447,13 +454,26 @@ public class TextUtils { /** * Returns true if a and b are equal, including if they are both null. - * + * <p><i>Note: In platform versions 1.1 and earlier, this method only worked well if + * both the arguments were instances of String.</i></p> * @param a first CharSequence to check * @param b second CharSequence to check * @return true if a and b are equal */ public static boolean equals(CharSequence a, CharSequence b) { - return a == b || (a != null && a.equals(b)); + if (a == b) return true; + int length; + if (a != null && b != null && (length = a.length()) == b.length()) { + if (a instanceof String && b instanceof String) { + return a.equals(b); + } else { + for (int i = 0; i < length; i++) { + if (a.charAt(i) != b.charAt(i)) return false; + } + return true; + } + } + return false; } // XXX currently this only reverses chars, not spans @@ -510,24 +530,42 @@ public class TextUtils { private int mEnd; } - private static final int ALIGNMENT_SPAN = 1; - private static final int FOREGROUND_COLOR_SPAN = 2; - private static final int RELATIVE_SIZE_SPAN = 3; - private static final int SCALE_X_SPAN = 4; - private static final int STRIKETHROUGH_SPAN = 5; - private static final int UNDERLINE_SPAN = 6; - private static final int STYLE_SPAN = 7; - private static final int BULLET_SPAN = 8; - private static final int QUOTE_SPAN = 9; - private static final int LEADING_MARGIN_SPAN = 10; - private static final int URL_SPAN = 11; - private static final int BACKGROUND_COLOR_SPAN = 12; - private static final int TYPEFACE_SPAN = 13; - private static final int SUPERSCRIPT_SPAN = 14; - private static final int SUBSCRIPT_SPAN = 15; - private static final int ABSOLUTE_SIZE_SPAN = 16; - private static final int TEXT_APPEARANCE_SPAN = 17; - private static final int ANNOTATION = 18; + /** @hide */ + public static final int ALIGNMENT_SPAN = 1; + /** @hide */ + public static final int FOREGROUND_COLOR_SPAN = 2; + /** @hide */ + public static final int RELATIVE_SIZE_SPAN = 3; + /** @hide */ + public static final int SCALE_X_SPAN = 4; + /** @hide */ + public static final int STRIKETHROUGH_SPAN = 5; + /** @hide */ + public static final int UNDERLINE_SPAN = 6; + /** @hide */ + public static final int STYLE_SPAN = 7; + /** @hide */ + public static final int BULLET_SPAN = 8; + /** @hide */ + public static final int QUOTE_SPAN = 9; + /** @hide */ + public static final int LEADING_MARGIN_SPAN = 10; + /** @hide */ + public static final int URL_SPAN = 11; + /** @hide */ + public static final int BACKGROUND_COLOR_SPAN = 12; + /** @hide */ + public static final int TYPEFACE_SPAN = 13; + /** @hide */ + public static final int SUPERSCRIPT_SPAN = 14; + /** @hide */ + public static final int SUBSCRIPT_SPAN = 15; + /** @hide */ + public static final int ABSOLUTE_SIZE_SPAN = 16; + /** @hide */ + public static final int TEXT_APPEARANCE_SPAN = 17; + /** @hide */ + public static final int ANNOTATION = 18; /** * Flatten a CharSequence and whatever styles can be copied across processes @@ -555,136 +593,10 @@ public class TextUtils { prop = ((CharacterStyle) prop).getUnderlying(); } - if (prop instanceof AlignmentSpan) { - p.writeInt(ALIGNMENT_SPAN); - p.writeString(((AlignmentSpan) prop).getAlignment().name()); - writeWhere(p, sp, o); - } - - if (prop instanceof ForegroundColorSpan) { - p.writeInt(FOREGROUND_COLOR_SPAN); - p.writeInt(((ForegroundColorSpan) prop).getForegroundColor()); - writeWhere(p, sp, o); - } - - if (prop instanceof RelativeSizeSpan) { - p.writeInt(RELATIVE_SIZE_SPAN); - p.writeFloat(((RelativeSizeSpan) prop).getSizeChange()); - writeWhere(p, sp, o); - } - - if (prop instanceof ScaleXSpan) { - p.writeInt(SCALE_X_SPAN); - p.writeFloat(((ScaleXSpan) prop).getScaleX()); - writeWhere(p, sp, o); - } - - if (prop instanceof StrikethroughSpan) { - p.writeInt(STRIKETHROUGH_SPAN); - writeWhere(p, sp, o); - } - - if (prop instanceof UnderlineSpan) { - p.writeInt(UNDERLINE_SPAN); - writeWhere(p, sp, o); - } - - if (prop instanceof StyleSpan) { - p.writeInt(STYLE_SPAN); - p.writeInt(((StyleSpan) prop).getStyle()); - writeWhere(p, sp, o); - } - - if (prop instanceof LeadingMarginSpan) { - if (prop instanceof BulletSpan) { - p.writeInt(BULLET_SPAN); - writeWhere(p, sp, o); - } else if (prop instanceof QuoteSpan) { - p.writeInt(QUOTE_SPAN); - p.writeInt(((QuoteSpan) prop).getColor()); - writeWhere(p, sp, o); - } else { - p.writeInt(LEADING_MARGIN_SPAN); - p.writeInt(((LeadingMarginSpan) prop). - getLeadingMargin(true)); - p.writeInt(((LeadingMarginSpan) prop). - getLeadingMargin(false)); - writeWhere(p, sp, o); - } - } - - if (prop instanceof URLSpan) { - p.writeInt(URL_SPAN); - p.writeString(((URLSpan) prop).getURL()); - writeWhere(p, sp, o); - } - - if (prop instanceof BackgroundColorSpan) { - p.writeInt(BACKGROUND_COLOR_SPAN); - p.writeInt(((BackgroundColorSpan) prop).getBackgroundColor()); - writeWhere(p, sp, o); - } - - if (prop instanceof TypefaceSpan) { - p.writeInt(TYPEFACE_SPAN); - p.writeString(((TypefaceSpan) prop).getFamily()); - writeWhere(p, sp, o); - } - - if (prop instanceof SuperscriptSpan) { - p.writeInt(SUPERSCRIPT_SPAN); - writeWhere(p, sp, o); - } - - if (prop instanceof SubscriptSpan) { - p.writeInt(SUBSCRIPT_SPAN); - writeWhere(p, sp, o); - } - - if (prop instanceof AbsoluteSizeSpan) { - p.writeInt(ABSOLUTE_SIZE_SPAN); - p.writeInt(((AbsoluteSizeSpan) prop).getSize()); - writeWhere(p, sp, o); - } - - if (prop instanceof TextAppearanceSpan) { - TextAppearanceSpan tas = (TextAppearanceSpan) prop; - p.writeInt(TEXT_APPEARANCE_SPAN); - - String tf = tas.getFamily(); - if (tf != null) { - p.writeInt(1); - p.writeString(tf); - } else { - p.writeInt(0); - } - - p.writeInt(tas.getTextStyle()); - p.writeInt(tas.getTextSize()); - - ColorStateList csl = tas.getTextColor(); - if (csl == null) { - p.writeInt(0); - } else { - p.writeInt(1); - csl.writeToParcel(p, parcelableFlags); - } - - csl = tas.getLinkTextColor(); - if (csl == null) { - p.writeInt(0); - } else { - p.writeInt(1); - csl.writeToParcel(p, parcelableFlags); - } - - writeWhere(p, sp, o); - } - - if (prop instanceof Annotation) { - p.writeInt(ANNOTATION); - p.writeString(((Annotation) prop).getKey()); - p.writeString(((Annotation) prop).getValue()); + if (prop instanceof ParcelableSpan) { + ParcelableSpan ps = (ParcelableSpan)prop; + p.writeInt(ps.getSpanTypeId()); + ps.writeToParcel(p, parcelableFlags); writeWhere(p, sp, o); } } @@ -707,8 +619,7 @@ public class TextUtils { } public static final Parcelable.Creator<CharSequence> CHAR_SEQUENCE_CREATOR - = new Parcelable.Creator<CharSequence>() - { + = new Parcelable.Creator<CharSequence>() { /** * Read and return a new CharSequence, possibly with styles, * from the parcel. @@ -729,89 +640,75 @@ public class TextUtils { switch (kind) { case ALIGNMENT_SPAN: - readSpan(p, sp, new AlignmentSpan.Standard( - Layout.Alignment.valueOf(p.readString()))); + readSpan(p, sp, new AlignmentSpan.Standard(p)); break; case FOREGROUND_COLOR_SPAN: - readSpan(p, sp, new ForegroundColorSpan(p.readInt())); + readSpan(p, sp, new ForegroundColorSpan(p)); break; case RELATIVE_SIZE_SPAN: - readSpan(p, sp, new RelativeSizeSpan(p.readFloat())); + readSpan(p, sp, new RelativeSizeSpan(p)); break; case SCALE_X_SPAN: - readSpan(p, sp, new ScaleXSpan(p.readFloat())); + readSpan(p, sp, new ScaleXSpan(p)); break; case STRIKETHROUGH_SPAN: - readSpan(p, sp, new StrikethroughSpan()); + readSpan(p, sp, new StrikethroughSpan(p)); break; case UNDERLINE_SPAN: - readSpan(p, sp, new UnderlineSpan()); + readSpan(p, sp, new UnderlineSpan(p)); break; case STYLE_SPAN: - readSpan(p, sp, new StyleSpan(p.readInt())); + readSpan(p, sp, new StyleSpan(p)); break; case BULLET_SPAN: - readSpan(p, sp, new BulletSpan()); + readSpan(p, sp, new BulletSpan(p)); break; case QUOTE_SPAN: - readSpan(p, sp, new QuoteSpan(p.readInt())); + readSpan(p, sp, new QuoteSpan(p)); break; case LEADING_MARGIN_SPAN: - readSpan(p, sp, new LeadingMarginSpan.Standard(p.readInt(), - p.readInt())); + readSpan(p, sp, new LeadingMarginSpan.Standard(p)); break; case URL_SPAN: - readSpan(p, sp, new URLSpan(p.readString())); + readSpan(p, sp, new URLSpan(p)); break; case BACKGROUND_COLOR_SPAN: - readSpan(p, sp, new BackgroundColorSpan(p.readInt())); + readSpan(p, sp, new BackgroundColorSpan(p)); break; case TYPEFACE_SPAN: - readSpan(p, sp, new TypefaceSpan(p.readString())); + readSpan(p, sp, new TypefaceSpan(p)); break; case SUPERSCRIPT_SPAN: - readSpan(p, sp, new SuperscriptSpan()); + readSpan(p, sp, new SuperscriptSpan(p)); break; case SUBSCRIPT_SPAN: - readSpan(p, sp, new SubscriptSpan()); + readSpan(p, sp, new SubscriptSpan(p)); break; case ABSOLUTE_SIZE_SPAN: - readSpan(p, sp, new AbsoluteSizeSpan(p.readInt())); + readSpan(p, sp, new AbsoluteSizeSpan(p)); break; case TEXT_APPEARANCE_SPAN: - readSpan(p, sp, new TextAppearanceSpan( - p.readInt() != 0 - ? p.readString() - : null, - p.readInt(), // style - p.readInt(), // size - p.readInt() != 0 - ? ColorStateList.CREATOR.createFromParcel(p) - : null, - p.readInt() != 0 - ? ColorStateList.CREATOR.createFromParcel(p) - : null)); + readSpan(p, sp, new TextAppearanceSpan(p)); break; case ANNOTATION: - readSpan(p, sp, - new Annotation(p.readString(), p.readString())); + readSpan(p, sp, new Annotation(p)); break; default: diff --git a/core/java/android/text/TextWatcher.java b/core/java/android/text/TextWatcher.java index 7456b28..bad09f2 100644 --- a/core/java/android/text/TextWatcher.java +++ b/core/java/android/text/TextWatcher.java @@ -20,7 +20,7 @@ package android.text; * When an object of a type is attached to an Editable, its methods will * be called when the text is changed. */ -public interface TextWatcher { +public interface TextWatcher extends NoCopySpan { /** * This method is called to notify you that, within <code>s</code>, * the <code>count</code> characters beginning at <code>start</code> diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index 73adedf..0dc96c3 100644 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java @@ -27,6 +27,7 @@ import com.android.internal.R; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; +import java.util.Locale; import java.util.TimeZone; import java.text.SimpleDateFormat; @@ -188,6 +189,12 @@ public class DateFormat { */ public static final char YEAR = 'y'; + + private static final Object sLocaleLock = new Object(); + private static Locale sIs24HourLocale; + private static boolean sIs24Hour; + + /** * Returns true if user preference is set to 24-hour format. * @param context the context to use for the content resolver @@ -198,20 +205,34 @@ public class DateFormat { Settings.System.TIME_12_24); if (value == null) { + Locale locale = context.getResources().getConfiguration().locale; + + synchronized (sLocaleLock) { + if (sIs24HourLocale != null && sIs24HourLocale.equals(locale)) { + return sIs24Hour; + } + } + java.text.DateFormat natural = java.text.DateFormat.getTimeInstance( - java.text.DateFormat.LONG, - context.getResources().getConfiguration().locale); + java.text.DateFormat.LONG, locale); if (natural instanceof SimpleDateFormat) { SimpleDateFormat sdf = (SimpleDateFormat) natural; String pattern = sdf.toPattern(); if (pattern.indexOf('H') >= 0) { - return true; + value = "24"; } else { - return false; + value = "12"; } + } else { + value = "12"; + } + + synchronized (sLocaleLock) { + sIs24HourLocale = locale; + sIs24Hour = !value.equals("12"); } } diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java index 7457439..a559b9d 100644 --- a/core/java/android/text/method/ArrowKeyMovementMethod.java +++ b/core/java/android/text/method/ArrowKeyMovementMethod.java @@ -131,6 +131,16 @@ implements MovementMethod } public boolean onKeyDown(TextView widget, Spannable buffer, int keyCode, KeyEvent event) { + if (executeDown(widget, buffer, keyCode)) { + MetaKeyKeyListener.adjustMetaAfterKeypress(buffer); + MetaKeyKeyListener.resetLockedMeta(buffer); + return true; + } + + return false; + } + + private boolean executeDown(TextView widget, Spannable buffer, int keyCode) { boolean handled = false; switch (keyCode) { @@ -170,6 +180,26 @@ implements MovementMethod return false; } + public boolean onKeyOther(TextView view, Spannable text, KeyEvent event) { + int code = event.getKeyCode(); + if (code != KeyEvent.KEYCODE_UNKNOWN + && event.getAction() == KeyEvent.ACTION_MULTIPLE) { + int repeat = event.getRepeatCount(); + boolean first = true; + boolean handled = false; + while ((--repeat) > 0) { + if (first && executeDown(view, text, code)) { + handled = true; + MetaKeyKeyListener.adjustMetaAfterKeypress(text); + MetaKeyKeyListener.resetLockedMeta(text); + } + first = false; + } + return handled; + } + return false; + } + public boolean onTrackballEvent(TextView widget, Spannable text, MotionEvent event) { return false; diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java index a875368..6df6a3a 100644 --- a/core/java/android/text/method/BaseKeyListener.java +++ b/core/java/android/text/method/BaseKeyListener.java @@ -18,7 +18,6 @@ package android.text.method; import android.view.KeyEvent; import android.view.View; -import android.text.InputType; import android.text.*; import android.text.method.TextKeyListener.Capitalize; import android.widget.TextView; @@ -26,7 +25,7 @@ import android.widget.TextView; public abstract class BaseKeyListener extends MetaKeyKeyListener implements KeyListener { - /* package */ static final Object OLD_SEL_START = new Object(); + /* package */ static final Object OLD_SEL_START = new NoCopySpan.Concrete(); /** * Performs the action that happens when you press the DEL key in @@ -127,5 +126,35 @@ implements KeyListener { return super.onKeyDown(view, content, keyCode, event); } + + /** + * Base implementation handles ACTION_MULTIPLE KEYCODE_UNKNOWN by inserting + * the event's text into the content. + */ + public boolean onKeyOther(View view, Editable content, KeyEvent event) { + if (event.getAction() != KeyEvent.ACTION_MULTIPLE + || event.getKeyCode() != KeyEvent.KEYCODE_UNKNOWN) { + // Not something we are interested in. + return false; + } + + int selStart, selEnd; + + { + int a = Selection.getSelectionStart(content); + int b = Selection.getSelectionEnd(content); + + selStart = Math.min(a, b); + selEnd = Math.max(a, b); + } + + CharSequence text = event.getCharacters(); + if (text == null) { + return false; + } + + content.replace(selStart, selEnd, text); + return true; + } } diff --git a/core/java/android/text/method/KeyListener.java b/core/java/android/text/method/KeyListener.java index 4ae6191..8594852 100644 --- a/core/java/android/text/method/KeyListener.java +++ b/core/java/android/text/method/KeyListener.java @@ -66,6 +66,13 @@ public interface KeyListener { int keyCode, KeyEvent event); /** + * If the key listener wants to other kinds of key events, return true, + * otherwise return false and the caller (i.e. the widget host) + * will handle the key. + */ + public boolean onKeyOther(View view, Editable text, KeyEvent event); + + /** * Remove the given shift states from the edited text. */ public void clearMetaKeyState(View view, Editable content, int states); diff --git a/core/java/android/text/method/LinkMovementMethod.java b/core/java/android/text/method/LinkMovementMethod.java index 92ac531..22e9cc6 100644 --- a/core/java/android/text/method/LinkMovementMethod.java +++ b/core/java/android/text/method/LinkMovementMethod.java @@ -252,5 +252,5 @@ extends ScrollingMovementMethod } private static LinkMovementMethod sInstance; - private static Object FROM_BELOW = new Object(); + private static Object FROM_BELOW = new NoCopySpan.Concrete(); } diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java index d5a473b..d89fbec 100644 --- a/core/java/android/text/method/MetaKeyKeyListener.java +++ b/core/java/android/text/method/MetaKeyKeyListener.java @@ -71,10 +71,10 @@ public abstract class MetaKeyKeyListener { | META_SYM_LOCKED | META_SYM_USED | META_SYM_PRESSED | META_SYM_RELEASED; - private static final Object CAP = new Object(); - private static final Object ALT = new Object(); - private static final Object SYM = new Object(); - private static final Object SELECTING = new Object(); + private static final Object CAP = new NoCopySpan.Concrete(); + private static final Object ALT = new NoCopySpan.Concrete(); + private static final Object SYM = new NoCopySpan.Concrete(); + private static final Object SELECTING = new NoCopySpan.Concrete(); /** * Resets all meta state to inactive. @@ -283,6 +283,10 @@ public abstract class MetaKeyKeyListener { } public void clearMetaKeyState(View view, Editable content, int states) { + clearMetaKeyState(content, states); + } + + public static void clearMetaKeyState(Editable content, int states) { if ((states&META_SHIFT_ON) != 0) resetLock(content, CAP); if ((states&META_ALT_ON) != 0) resetLock(content, ALT); if ((states&META_SYM_ON) != 0) resetLock(content, SYM); diff --git a/core/java/android/text/method/MovementMethod.java b/core/java/android/text/method/MovementMethod.java index 9e37e59..29f67a1 100644 --- a/core/java/android/text/method/MovementMethod.java +++ b/core/java/android/text/method/MovementMethod.java @@ -26,6 +26,14 @@ public interface MovementMethod public void initialize(TextView widget, Spannable text); public boolean onKeyDown(TextView widget, Spannable text, int keyCode, KeyEvent event); public boolean onKeyUp(TextView widget, Spannable text, int keyCode, KeyEvent event); + + /** + * If the key listener wants to other kinds of key events, return true, + * otherwise return false and the caller (i.e. the widget host) + * will handle the key. + */ + public boolean onKeyOther(TextView view, Spannable text, KeyEvent event); + public void onTakeFocus(TextView widget, Spannable text, int direction); public boolean onTrackballEvent(TextView widget, Spannable text, MotionEvent event); diff --git a/core/java/android/text/method/NumberKeyListener.java b/core/java/android/text/method/NumberKeyListener.java index 348b658..e500fae 100644 --- a/core/java/android/text/method/NumberKeyListener.java +++ b/core/java/android/text/method/NumberKeyListener.java @@ -24,7 +24,6 @@ import android.text.Selection; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.Spanned; -import android.util.SparseIntArray; /** * For numeric text entry diff --git a/core/java/android/text/method/PasswordTransformationMethod.java b/core/java/android/text/method/PasswordTransformationMethod.java index edaa836..85adabd 100644 --- a/core/java/android/text/method/PasswordTransformationMethod.java +++ b/core/java/android/text/method/PasswordTransformationMethod.java @@ -22,6 +22,7 @@ import android.graphics.Rect; import android.view.View; import android.text.Editable; import android.text.GetChars; +import android.text.NoCopySpan; import android.text.TextUtils; import android.text.TextWatcher; import android.text.Selection; @@ -249,7 +250,8 @@ implements TransformationMethod, TextWatcher * Used to stash a reference back to the View in the Editable so we * can use it to check the settings. */ - private static class ViewReference extends WeakReference<View> { + private static class ViewReference extends WeakReference<View> + implements NoCopySpan { public ViewReference(View v) { super(v); } diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java index 863b2e2..0b39517 100644 --- a/core/java/android/text/method/QwertyKeyListener.java +++ b/core/java/android/text/method/QwertyKeyListener.java @@ -16,18 +16,12 @@ package android.text.method; -import android.os.Message; -import android.os.Handler; import android.text.*; import android.text.method.TextKeyListener.Capitalize; import android.util.SparseArray; -import android.util.SparseIntArray; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.View; -import android.widget.TextView; - -import java.util.HashMap; /** * This is the standard key listener for alphabetic input on qwerty @@ -442,7 +436,7 @@ public class QwertyKeyListener extends BaseKeyListener { return Character.toUpperCase(src.charAt(0)) + src.substring(1); } - /* package */ static class Replaced + /* package */ static class Replaced implements NoCopySpan { public Replaced(char[] text) { mText = text; diff --git a/core/java/android/text/method/ScrollingMovementMethod.java b/core/java/android/text/method/ScrollingMovementMethod.java index db470be..563ceed 100644 --- a/core/java/android/text/method/ScrollingMovementMethod.java +++ b/core/java/android/text/method/ScrollingMovementMethod.java @@ -144,6 +144,10 @@ implements MovementMethod } public boolean onKeyDown(TextView widget, Spannable buffer, int keyCode, KeyEvent event) { + return executeDown(widget, buffer, keyCode); + } + + private boolean executeDown(TextView widget, Spannable buffer, int keyCode) { boolean handled = false; switch (keyCode) { @@ -171,6 +175,26 @@ implements MovementMethod return false; } + public boolean onKeyOther(TextView view, Spannable text, KeyEvent event) { + int code = event.getKeyCode(); + if (code != KeyEvent.KEYCODE_UNKNOWN + && event.getAction() == KeyEvent.ACTION_MULTIPLE) { + int repeat = event.getRepeatCount(); + boolean first = true; + boolean handled = false; + while ((--repeat) > 0) { + if (first && executeDown(view, text, code)) { + handled = true; + MetaKeyKeyListener.adjustMetaAfterKeypress(text); + MetaKeyKeyListener.resetLockedMeta(text); + } + first = false; + } + return handled; + } + return false; + } + public boolean onTrackballEvent(TextView widget, Spannable text, MotionEvent event) { return false; diff --git a/core/java/android/text/method/SingleLineTransformationMethod.java b/core/java/android/text/method/SingleLineTransformationMethod.java index a4fcf15..6a05fe4 100644 --- a/core/java/android/text/method/SingleLineTransformationMethod.java +++ b/core/java/android/text/method/SingleLineTransformationMethod.java @@ -27,22 +27,24 @@ import android.view.View; /** * This transformation method causes any newline characters (\n) to be - * displayed as spaces instead of causing line breaks. + * displayed as spaces instead of causing line breaks, and causes + * carriage return characters (\r) to have no appearance. */ public class SingleLineTransformationMethod extends ReplacementTransformationMethod { - private static char[] ORIGINAL = new char[] { '\n' }; - private static char[] REPLACEMENT = new char[] { ' ' }; + private static char[] ORIGINAL = new char[] { '\n', '\r' }; + private static char[] REPLACEMENT = new char[] { ' ', '\uFEFF' }; /** - * The character to be replaced is \n. + * The characters to be replaced are \n and \r. */ protected char[] getOriginal() { return ORIGINAL; } /** - * The character \n is replaced with is space. + * The character \n is replaced with is space; + * the character \r is replaced with is FEFF (zero width space). */ protected char[] getReplacement() { return REPLACEMENT; diff --git a/core/java/android/text/method/TextKeyListener.java b/core/java/android/text/method/TextKeyListener.java index b1c380a..5be2a48 100644 --- a/core/java/android/text/method/TextKeyListener.java +++ b/core/java/android/text/method/TextKeyListener.java @@ -38,10 +38,10 @@ public class TextKeyListener extends BaseKeyListener implements SpanWatcher { private static TextKeyListener[] sInstance = new TextKeyListener[Capitalize.values().length * 2]; - /* package */ static final Object ACTIVE = new Object(); - /* package */ static final Object CAPPED = new Object(); - /* package */ static final Object INHIBIT_REPLACEMENT = new Object(); - /* package */ static final Object LAST_TYPED = new Object(); + /* package */ static final Object ACTIVE = new NoCopySpan.Concrete(); + /* package */ static final Object CAPPED = new NoCopySpan.Concrete(); + /* package */ static final Object INHIBIT_REPLACEMENT = new NoCopySpan.Concrete(); + /* package */ static final Object LAST_TYPED = new NoCopySpan.Concrete(); private Capitalize mAutoCap; private boolean mAutoText; @@ -140,6 +140,13 @@ public class TextKeyListener extends BaseKeyListener implements SpanWatcher { return im.onKeyUp(view, content, keyCode, event); } + @Override + public boolean onKeyOther(View view, Editable content, KeyEvent event) { + KeyListener im = getKeyListener(event); + + return im.onKeyOther(view, content, event); + } + /** * Clear all the input state (autotext, autocap, multitap, undo) * from the specified Editable, going beyond Editable.clear(), which @@ -205,6 +212,10 @@ public class TextKeyListener extends BaseKeyListener implements SpanWatcher { return false; } + public boolean onKeyOther(View view, Editable content, KeyEvent event) { + return false; + } + public void clearMetaKeyState(View view, Editable content, int states) { } diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java index 8b097c5..65036ad 100644 --- a/core/java/android/text/method/Touch.java +++ b/core/java/android/text/method/Touch.java @@ -17,6 +17,7 @@ package android.text.method; import android.text.Layout; +import android.text.NoCopySpan; import android.text.Layout.Alignment; import android.text.Spannable; import android.view.MotionEvent; @@ -103,7 +104,7 @@ public class Touch { if (ds.length > 0) { if (ds[0].mFarEnough == false) { - int slop = ViewConfiguration.getTouchSlop(); + int slop = ViewConfiguration.get(widget.getContext()).getScaledTouchSlop(); if (Math.abs(event.getX() - ds[0].mX) >= slop || Math.abs(event.getY() - ds[0].mY) >= slop) { @@ -141,7 +142,7 @@ public class Touch { return false; } - private static class DragState { + private static class DragState implements NoCopySpan { public float mX; public float mY; public boolean mFarEnough; diff --git a/core/java/android/text/style/AbsoluteSizeSpan.java b/core/java/android/text/style/AbsoluteSizeSpan.java index 8f6ed5a..484f8ce 100644 --- a/core/java/android/text/style/AbsoluteSizeSpan.java +++ b/core/java/android/text/style/AbsoluteSizeSpan.java @@ -16,17 +16,35 @@ package android.text.style; -import android.graphics.Paint; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; -public class AbsoluteSizeSpan extends MetricAffectingSpan { +public class AbsoluteSizeSpan extends MetricAffectingSpan implements ParcelableSpan { - private int mSize; + private final int mSize; public AbsoluteSizeSpan(int size) { mSize = size; } + public AbsoluteSizeSpan(Parcel src) { + mSize = src.readInt(); + } + + public int getSpanTypeId() { + return TextUtils.ABSOLUTE_SIZE_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mSize); + } + public int getSize() { return mSize; } diff --git a/core/java/android/text/style/AlignmentSpan.java b/core/java/android/text/style/AlignmentSpan.java index d51edcc..b8a37da 100644 --- a/core/java/android/text/style/AlignmentSpan.java +++ b/core/java/android/text/style/AlignmentSpan.java @@ -16,24 +16,40 @@ package android.text.style; +import android.os.Parcel; import android.text.Layout; +import android.text.ParcelableSpan; +import android.text.TextUtils; -public interface AlignmentSpan -extends ParagraphStyle -{ +public interface AlignmentSpan extends ParagraphStyle { public Layout.Alignment getAlignment(); public static class Standard - implements AlignmentSpan - { + implements AlignmentSpan, ParcelableSpan { public Standard(Layout.Alignment align) { mAlignment = align; } + public Standard(Parcel src) { + mAlignment = Layout.Alignment.valueOf(src.readString()); + } + + public int getSpanTypeId() { + return TextUtils.ALIGNMENT_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mAlignment.name()); + } + public Layout.Alignment getAlignment() { return mAlignment; } - private Layout.Alignment mAlignment; + private final Layout.Alignment mAlignment; } } diff --git a/core/java/android/text/style/BackgroundColorSpan.java b/core/java/android/text/style/BackgroundColorSpan.java index 27eda69..580a369 100644 --- a/core/java/android/text/style/BackgroundColorSpan.java +++ b/core/java/android/text/style/BackgroundColorSpan.java @@ -16,16 +16,36 @@ package android.text.style; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; -public class BackgroundColorSpan extends CharacterStyle implements UpdateAppearance { +public class BackgroundColorSpan extends CharacterStyle + implements UpdateAppearance, ParcelableSpan { - private int mColor; + private final int mColor; public BackgroundColorSpan(int color) { mColor = color; } + public BackgroundColorSpan(Parcel src) { + mColor = src.readInt(); + } + + public int getSpanTypeId() { + return TextUtils.BACKGROUND_COLOR_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mColor); + } + public int getBackgroundColor() { return mColor; } diff --git a/core/java/android/text/style/BulletSpan.java b/core/java/android/text/style/BulletSpan.java index 70c4d33..655bd81 100644 --- a/core/java/android/text/style/BulletSpan.java +++ b/core/java/android/text/style/BulletSpan.java @@ -18,17 +18,30 @@ package android.text.style; import android.graphics.Canvas; import android.graphics.Paint; +import android.os.Parcel; import android.text.Layout; +import android.text.ParcelableSpan; import android.text.Spanned; +import android.text.TextUtils; -public class BulletSpan implements LeadingMarginSpan { +public class BulletSpan implements LeadingMarginSpan, ParcelableSpan { + private final int mGapWidth; + private final boolean mWantColor; + private final int mColor; + + private static final int BULLET_RADIUS = 3; + public static final int STANDARD_GAP_WIDTH = 2; public BulletSpan() { mGapWidth = STANDARD_GAP_WIDTH; + mWantColor = false; + mColor = 0; } public BulletSpan(int gapWidth) { mGapWidth = gapWidth; + mWantColor = false; + mColor = 0; } public BulletSpan(int gapWidth, int color) { @@ -37,6 +50,26 @@ public class BulletSpan implements LeadingMarginSpan { mColor = color; } + public BulletSpan(Parcel src) { + mGapWidth = src.readInt(); + mWantColor = src.readInt() != 0; + mColor = src.readInt(); + } + + public int getSpanTypeId() { + return TextUtils.BULLET_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mGapWidth); + dest.writeInt(mWantColor ? 1 : 0); + dest.writeInt(mColor); + } + public int getLeadingMargin(boolean first) { return 2 * BULLET_RADIUS + mGapWidth; } @@ -66,11 +99,4 @@ public class BulletSpan implements LeadingMarginSpan { p.setStyle(style); } } - - private int mGapWidth; - private boolean mWantColor; - private int mColor; - - private static final int BULLET_RADIUS = 3; - public static final int STANDARD_GAP_WIDTH = 2; } diff --git a/core/java/android/text/style/ForegroundColorSpan.java b/core/java/android/text/style/ForegroundColorSpan.java index 99b3381..476124d 100644 --- a/core/java/android/text/style/ForegroundColorSpan.java +++ b/core/java/android/text/style/ForegroundColorSpan.java @@ -16,16 +16,36 @@ package android.text.style; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; -public class ForegroundColorSpan extends CharacterStyle implements UpdateAppearance { +public class ForegroundColorSpan extends CharacterStyle + implements UpdateAppearance, ParcelableSpan { - private int mColor; + private final int mColor; public ForegroundColorSpan(int color) { mColor = color; } + public ForegroundColorSpan(Parcel src) { + mColor = src.readInt(); + } + + public int getSpanTypeId() { + return TextUtils.FOREGROUND_COLOR_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mColor); + } + public int getForegroundColor() { return mColor; } diff --git a/core/java/android/text/style/LeadingMarginSpan.java b/core/java/android/text/style/LeadingMarginSpan.java index 85a27dc..8e212e3 100644 --- a/core/java/android/text/style/LeadingMarginSpan.java +++ b/core/java/android/text/style/LeadingMarginSpan.java @@ -18,7 +18,10 @@ package android.text.style; import android.graphics.Paint; import android.graphics.Canvas; +import android.os.Parcel; import android.text.Layout; +import android.text.ParcelableSpan; +import android.text.TextUtils; public interface LeadingMarginSpan extends ParagraphStyle @@ -30,9 +33,9 @@ extends ParagraphStyle CharSequence text, int start, int end, boolean first, Layout layout); - public static class Standard - implements LeadingMarginSpan - { + public static class Standard implements LeadingMarginSpan, ParcelableSpan { + private final int mFirst, mRest; + public Standard(int first, int rest) { mFirst = first; mRest = rest; @@ -42,6 +45,24 @@ extends ParagraphStyle this(every, every); } + public Standard(Parcel src) { + mFirst = src.readInt(); + mRest = src.readInt(); + } + + public int getSpanTypeId() { + return TextUtils.LEADING_MARGIN_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mFirst); + dest.writeInt(mRest); + } + public int getLeadingMargin(boolean first) { return first ? mFirst : mRest; } @@ -53,7 +74,5 @@ extends ParagraphStyle boolean first, Layout layout) { ; } - - private int mFirst, mRest; } } diff --git a/core/java/android/text/style/QuoteSpan.java b/core/java/android/text/style/QuoteSpan.java index 3f4a32f..29dd273 100644 --- a/core/java/android/text/style/QuoteSpan.java +++ b/core/java/android/text/style/QuoteSpan.java @@ -18,26 +18,43 @@ package android.text.style; import android.graphics.Paint; import android.graphics.Canvas; -import android.graphics.RectF; +import android.os.Parcel; import android.text.Layout; +import android.text.ParcelableSpan; +import android.text.TextUtils; -public class QuoteSpan -implements LeadingMarginSpan -{ +public class QuoteSpan implements LeadingMarginSpan, ParcelableSpan { private static final int STRIPE_WIDTH = 2; private static final int GAP_WIDTH = 2; - private int mColor = 0xff0000ff; + private final int mColor; public QuoteSpan() { super(); + mColor = 0xff0000ff; } public QuoteSpan(int color) { - this(); + super(); mColor = color; } + public QuoteSpan(Parcel src) { + mColor = src.readInt(); + } + + public int getSpanTypeId() { + return TextUtils.QUOTE_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mColor); + } + public int getColor() { return mColor; } diff --git a/core/java/android/text/style/RelativeSizeSpan.java b/core/java/android/text/style/RelativeSizeSpan.java index a8ad076..9717362 100644 --- a/core/java/android/text/style/RelativeSizeSpan.java +++ b/core/java/android/text/style/RelativeSizeSpan.java @@ -16,17 +16,35 @@ package android.text.style; -import android.graphics.Paint; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; -public class RelativeSizeSpan extends MetricAffectingSpan { +public class RelativeSizeSpan extends MetricAffectingSpan implements ParcelableSpan { - private float mProportion; + private final float mProportion; public RelativeSizeSpan(float proportion) { mProportion = proportion; } + public RelativeSizeSpan(Parcel src) { + mProportion = src.readFloat(); + } + + public int getSpanTypeId() { + return TextUtils.RELATIVE_SIZE_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeFloat(mProportion); + } + public float getSizeChange() { return mProportion; } diff --git a/core/java/android/text/style/ScaleXSpan.java b/core/java/android/text/style/ScaleXSpan.java index ac9e35d..655064b 100644 --- a/core/java/android/text/style/ScaleXSpan.java +++ b/core/java/android/text/style/ScaleXSpan.java @@ -16,17 +16,35 @@ package android.text.style; -import android.graphics.Paint; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; -public class ScaleXSpan extends MetricAffectingSpan { +public class ScaleXSpan extends MetricAffectingSpan implements ParcelableSpan { - private float mProportion; + private final float mProportion; public ScaleXSpan(float proportion) { mProportion = proportion; } + public ScaleXSpan(Parcel src) { + mProportion = src.readFloat(); + } + + public int getSpanTypeId() { + return TextUtils.SCALE_X_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeFloat(mProportion); + } + public float getScaleX() { return mProportion; } diff --git a/core/java/android/text/style/StrikethroughSpan.java b/core/java/android/text/style/StrikethroughSpan.java index dd430e5..b51363a 100644 --- a/core/java/android/text/style/StrikethroughSpan.java +++ b/core/java/android/text/style/StrikethroughSpan.java @@ -16,9 +16,29 @@ package android.text.style; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; -public class StrikethroughSpan extends CharacterStyle implements UpdateAppearance { +public class StrikethroughSpan extends CharacterStyle + implements UpdateAppearance, ParcelableSpan { + public StrikethroughSpan() { + } + + public StrikethroughSpan(Parcel src) { + } + + public int getSpanTypeId() { + return TextUtils.STRIKETHROUGH_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + } @Override public void updateDrawState(TextPaint ds) { diff --git a/core/java/android/text/style/StyleSpan.java b/core/java/android/text/style/StyleSpan.java index cc8b06c..8e6147c 100644 --- a/core/java/android/text/style/StyleSpan.java +++ b/core/java/android/text/style/StyleSpan.java @@ -18,7 +18,10 @@ package android.text.style; import android.graphics.Paint; import android.graphics.Typeface; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; /** * @@ -28,9 +31,9 @@ import android.text.TextPaint; * you get bold italic. You can't turn off a style from the base style. * */ -public class StyleSpan extends MetricAffectingSpan { +public class StyleSpan extends MetricAffectingSpan implements ParcelableSpan { - private int mStyle; + private final int mStyle; /** * @@ -42,6 +45,22 @@ public class StyleSpan extends MetricAffectingSpan { mStyle = style; } + public StyleSpan(Parcel src) { + mStyle = src.readInt(); + } + + public int getSpanTypeId() { + return TextUtils.STYLE_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mStyle); + } + /** * Returns the style constant defined in {@link android.graphics.Typeface}. */ diff --git a/core/java/android/text/style/SubscriptSpan.java b/core/java/android/text/style/SubscriptSpan.java index 78d6ba9..de1d8b2 100644 --- a/core/java/android/text/style/SubscriptSpan.java +++ b/core/java/android/text/style/SubscriptSpan.java @@ -16,9 +16,29 @@ package android.text.style; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; + +public class SubscriptSpan extends MetricAffectingSpan implements ParcelableSpan { + public SubscriptSpan() { + } + + public SubscriptSpan(Parcel src) { + } + + public int getSpanTypeId() { + return TextUtils.SUBSCRIPT_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + } -public class SubscriptSpan extends MetricAffectingSpan { @Override public void updateDrawState(TextPaint tp) { tp.baselineShift -= (int) (tp.ascent() / 2); diff --git a/core/java/android/text/style/SuperscriptSpan.java b/core/java/android/text/style/SuperscriptSpan.java index 79be4de..285fe84 100644 --- a/core/java/android/text/style/SuperscriptSpan.java +++ b/core/java/android/text/style/SuperscriptSpan.java @@ -16,9 +16,29 @@ package android.text.style; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; + +public class SuperscriptSpan extends MetricAffectingSpan implements ParcelableSpan { + public SuperscriptSpan() { + } + + public SuperscriptSpan(Parcel src) { + } + + public int getSpanTypeId() { + return TextUtils.SUPERSCRIPT_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + } -public class SuperscriptSpan extends MetricAffectingSpan { @Override public void updateDrawState(TextPaint tp) { tp.baselineShift += (int) (tp.ascent() / 2); diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java index c4ec976..de929e3 100644 --- a/core/java/android/text/style/TextAppearanceSpan.java +++ b/core/java/android/text/style/TextAppearanceSpan.java @@ -19,20 +19,22 @@ package android.text.style; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; -import android.graphics.Paint; import android.graphics.Typeface; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; /** * Sets the text color, size, style, and typeface to match a TextAppearance * resource. */ -public class TextAppearanceSpan extends MetricAffectingSpan { - private String mTypeface; - private int mStyle; - private int mTextSize; - private ColorStateList mTextColor; - private ColorStateList mTextColorLink; +public class TextAppearanceSpan extends MetricAffectingSpan implements ParcelableSpan { + private final String mTypeface; + private final int mStyle; + private final int mTextSize; + private final ColorStateList mTextColor; + private final ColorStateList mTextColorLink; /** * Uses the specified TextAppearance resource to determine the @@ -53,11 +55,13 @@ public class TextAppearanceSpan extends MetricAffectingSpan { */ public TextAppearanceSpan(Context context, int appearance, int colorList) { + ColorStateList textColor; + TypedArray a = context.obtainStyledAttributes(appearance, com.android.internal.R.styleable.TextAppearance); - mTextColor = a.getColorStateList(com.android.internal.R.styleable. + textColor = a.getColorStateList(com.android.internal.R.styleable. TextAppearance_textColor); mTextColorLink = a.getColorStateList(com.android.internal.R.styleable. TextAppearance_textColorLink); @@ -79,6 +83,10 @@ public class TextAppearanceSpan extends MetricAffectingSpan { case 3: mTypeface = "monospace"; break; + + default: + mTypeface = null; + break; } a.recycle(); @@ -87,9 +95,11 @@ public class TextAppearanceSpan extends MetricAffectingSpan { a = context.obtainStyledAttributes(com.android.internal.R.style.Theme, com.android.internal.R.styleable.Theme); - mTextColor = a.getColorStateList(colorList); + textColor = a.getColorStateList(colorList); a.recycle(); } + + mTextColor = textColor; } /** @@ -105,6 +115,48 @@ public class TextAppearanceSpan extends MetricAffectingSpan { mTextColorLink = linkColor; } + public TextAppearanceSpan(Parcel src) { + mTypeface = src.readString(); + mStyle = src.readInt(); + mTextSize = src.readInt(); + if (src.readInt() != 0) { + mTextColor = ColorStateList.CREATOR.createFromParcel(src); + } else { + mTextColor = null; + } + if (src.readInt() != 0) { + mTextColorLink = ColorStateList.CREATOR.createFromParcel(src); + } else { + mTextColorLink = null; + } + } + + public int getSpanTypeId() { + return TextUtils.TEXT_APPEARANCE_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mTypeface); + dest.writeInt(mStyle); + dest.writeInt(mTextSize); + if (mTextColor != null) { + dest.writeInt(1); + mTextColor.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } + if (mTextColorLink != null) { + dest.writeInt(1); + mTextColorLink.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } + } + /** * Returns the typeface family specified by this span, or <code>null</code> * if it does not specify one. diff --git a/core/java/android/text/style/TypefaceSpan.java b/core/java/android/text/style/TypefaceSpan.java index 7519ac2..f194060 100644 --- a/core/java/android/text/style/TypefaceSpan.java +++ b/core/java/android/text/style/TypefaceSpan.java @@ -18,13 +18,16 @@ package android.text.style; import android.graphics.Paint; import android.graphics.Typeface; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; /** * Changes the typeface family of the text to which the span is attached. */ -public class TypefaceSpan extends MetricAffectingSpan { - private String mFamily; +public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan { + private final String mFamily; /** * @param family The font family for this typeface. Examples include @@ -34,6 +37,22 @@ public class TypefaceSpan extends MetricAffectingSpan { mFamily = family; } + public TypefaceSpan(Parcel src) { + mFamily = src.readString(); + } + + public int getSpanTypeId() { + return TextUtils.TYPEFACE_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mFamily); + } + /** * Returns the font family name. */ diff --git a/core/java/android/text/style/URLSpan.java b/core/java/android/text/style/URLSpan.java index 79809b5..f458611 100644 --- a/core/java/android/text/style/URLSpan.java +++ b/core/java/android/text/style/URLSpan.java @@ -18,17 +18,35 @@ package android.text.style; import android.content.Intent; import android.net.Uri; -import android.text.TextPaint; +import android.os.Parcel; +import android.text.ParcelableSpan; +import android.text.TextUtils; import android.view.View; -public class URLSpan extends ClickableSpan { +public class URLSpan extends ClickableSpan implements ParcelableSpan { - private String mURL; + private final String mURL; public URLSpan(String url) { mURL = url; } + public URLSpan(Parcel src) { + mURL = src.readString(); + } + + public int getSpanTypeId() { + return TextUtils.URL_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mURL); + } + public String getURL() { return mURL; } diff --git a/core/java/android/text/style/UnderlineSpan.java b/core/java/android/text/style/UnderlineSpan.java index ca6f10c..b0cb0e8 100644 --- a/core/java/android/text/style/UnderlineSpan.java +++ b/core/java/android/text/style/UnderlineSpan.java @@ -16,9 +16,29 @@ package android.text.style; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; -public class UnderlineSpan extends CharacterStyle implements UpdateAppearance { +public class UnderlineSpan extends CharacterStyle + implements UpdateAppearance, ParcelableSpan { + public UnderlineSpan() { + } + + public UnderlineSpan(Parcel src) { + } + + public int getSpanTypeId() { + return TextUtils.UNDERLINE_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + } @Override public void updateDrawState(TextPaint ds) { diff --git a/core/java/android/text/util/Rfc822Validator.java b/core/java/android/text/util/Rfc822Validator.java index 9f03bb0..6a6bf69 100644 --- a/core/java/android/text/util/Rfc822Validator.java +++ b/core/java/android/text/util/Rfc822Validator.java @@ -16,6 +16,7 @@ package android.text.util; +import android.text.TextUtils; import android.widget.AutoCompleteTextView; import java.util.regex.Pattern; @@ -67,7 +68,7 @@ public class Rfc822Validator implements AutoCompleteTextView.Validator { /** * @return a string in which all the characters that are illegal for the username - * part of the email address have been removed. + * or the domain name part of the email address have been removed. */ private String removeIllegalCharacters(String s) { StringBuilder result = new StringBuilder(); @@ -101,6 +102,9 @@ public class Rfc822Validator implements AutoCompleteTextView.Validator { * {@inheritDoc} */ public CharSequence fixText(CharSequence cs) { + // Return an empty string if the email address only contains spaces, \n or \t + if (TextUtils.getTrimmedLength(cs) == 0) return ""; + Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(cs); StringBuilder sb = new StringBuilder(); @@ -111,10 +115,10 @@ public class Rfc822Validator implements AutoCompleteTextView.Validator { // If there is no @, just append the domain of the account tokens[i].setAddress(removeIllegalCharacters(text) + "@" + mDomain); } else { - // Otherwise, remove everything right of the '@' and append the domain - // ("a@b" becomes "a@gmail.com"). + // Otherwise, remove the illegal characters on both sides of the '@' String fix = removeIllegalCharacters(text.substring(0, index)); - tokens[i].setAddress(fix + "@" + mDomain); + String domain = removeIllegalCharacters(text.substring(index + 1)); + tokens[i].setAddress(fix + "@" + (domain.length() != 0 ? domain : mDomain)); } sb.append(tokens[i].toString()); diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index 8fc3602..9de4cbe 100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -16,6 +16,8 @@ package android.util; +import android.os.*; + /** * A structure describing general information about a display, such as its @@ -23,6 +25,16 @@ package android.util; */ public class DisplayMetrics { /** + * The reference density used throughout the system. + * + * @hide Pending API council approval + */ + public static final int DEFAULT_DENSITY = 160; + + private static final int sLcdDensity = SystemProperties.getInt("ro.sf.lcd_density", + DEFAULT_DENSITY); + + /** * The absolute width of the display in pixels. */ public int widthPixels; @@ -43,7 +55,9 @@ public class DisplayMetrics { * example, a 240x320 screen will have a density of 1 even if its width is * 1.8", 1.3", etc. However, if the screen resolution is increased to * 320x480 but the screen size remained 1.5"x2" then the density would be - * increased (probably to 1.5). + * increased (probably to 1.5). + * + * @see #DEFAULT_DENSITY */ public float density; /** @@ -60,7 +74,7 @@ public class DisplayMetrics { * The exact physical pixels per inch of the screen in the Y dimension. */ public float ydpi; - + public DisplayMetrics() { } @@ -76,10 +90,9 @@ public class DisplayMetrics { public void setToDefaults() { widthPixels = 0; heightPixels = 0; - density = 1; - scaledDensity = 1; - xdpi = 160; - ydpi = 160; + density = sLcdDensity / (float) DEFAULT_DENSITY; + scaledDensity = density; + xdpi = sLcdDensity; + ydpi = sLcdDensity; } } - diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java index a4ee35a..d4ba9e2 100644 --- a/core/java/android/util/TypedValue.java +++ b/core/java/android/util/TypedValue.java @@ -16,9 +16,6 @@ package android.util; -import android.util.Config; -import android.util.Log; - /** * Container for a dynamically typed data value. Primarily used with * {@link android.content.res.Resources} for holding resource values. @@ -141,6 +138,16 @@ public class TypedValue { /* ------------------------------------------------------------ */ + /** + * If {@link #density} is equal to this value, then the density should be + * treated as the system's default density value: {@link DisplayMetrics#DEFAULT_DENSITY}. + * + * @hide Pending API council approval + */ + public static final int DENSITY_DEFAULT = 0; + + /* ------------------------------------------------------------ */ + /** The type held by this value, as defined by the constants here. * This tells you how to interpret the other fields in the object. */ public int type; @@ -161,7 +168,14 @@ public class TypedValue { /** If Value came from a resource, these are the configurations for which * its contents can change. */ public int changingConfigurations = -1; - + + /** + * If the Value came from a resource, this holds the corresponding pixel density. + * + * @hide Pending API council approval + * */ + public int density; + /* ------------------------------------------------------------ */ /** Return the data for this value as a float. Only use for values @@ -454,6 +468,7 @@ public class TypedValue { data = other.data; assetCookie = other.assetCookie; resourceId = other.resourceId; + density = other.density; } public String toString() diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java index 4048763..15fb839 100644 --- a/core/java/android/view/FocusFinder.java +++ b/core/java/android/view/FocusFinder.java @@ -397,7 +397,7 @@ public class FocusFinder { int numTouchables = touchables.size(); - int edgeSlop = ViewConfiguration.getEdgeSlop(); + int edgeSlop = ViewConfiguration.get(root.mContext).getScaledEdgeSlop(); Rect closestBounds = new Rect(); Rect touchableBounds = mOtherRect; diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java index fc9af05..a472689 100644 --- a/core/java/android/view/GestureDetector.java +++ b/core/java/android/view/GestureDetector.java @@ -18,6 +18,7 @@ package android.view; import android.os.Handler; import android.os.Message; +import android.content.Context; /** * Detects various gestures and events using the supplied {@link MotionEvent}s. @@ -34,7 +35,6 @@ import android.os.Message; * </ul> */ public class GestureDetector { - /** * The listener that is used to notify when gestures occur. * If you want to listen for all the different gestures then implement @@ -113,6 +113,14 @@ public class GestureDetector { } /** + * @hide pending API council + */ + public interface OnDoubleTapListener { + boolean onSingleTapConfirmed(MotionEvent e); + boolean onDoubleTapEvent(MotionEvent e); + } + + /** * A convenience class to extend when you only want to listen for a * subset of all the gestures. This implements all methods in the * {@link OnGestureListener} but does nothing and return {@code false} @@ -144,22 +152,40 @@ public class GestureDetector { } } - private static final int TOUCH_SLOP_SQUARE = ViewConfiguration.getTouchSlop() - * ViewConfiguration.getTouchSlop(); - + // TODO: ViewConfiguration + private int mBiggerTouchSlopSquare = 20 * 20; + + private int mTouchSlopSquare; + private int mDoubleTapSlopSquare; + private int mMinimumFlingVelocity; + + private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout(); + private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout(); + // TODO make new double-tap timeout, and define its events (i.e. either time + // between down-down or time between up-down) + private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getJumpTapTimeout(); + // constants for Message.what used by GestureHandler below private static final int SHOW_PRESS = 1; private static final int LONG_PRESS = 2; + private static final int TAP = 3; private final Handler mHandler; private final OnGestureListener mListener; + private OnDoubleTapListener mDoubleTapListener; private boolean mInLongPress; private boolean mAlwaysInTapRegion; + private boolean mAlwaysInBiggerTapRegion; private MotionEvent mCurrentDownEvent; - private MotionEvent mCurrentUpEvent; - + + /** + * True when the user is still touching for the second tap (down, move, and + * up events). Can only be true if there is a double tap listener attached. + */ + private boolean mIsDoubleTapping; + private float mLastMotionY; private float mLastMotionX; @@ -189,6 +215,12 @@ public class GestureDetector { case LONG_PRESS: dispatchLongPress(); break; + + case TAP: + if (mDoubleTapListener != null) { + mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent); + } + break; default: throw new RuntimeException("Unknown message " + msg); //never @@ -203,16 +235,17 @@ public class GestureDetector { * * @param listener the listener invoked for all the callbacks, this must * not be null. - * @param handler the handler to use, this must - * not be null. + * @param handler the handler to use * * @throws NullPointerException if either {@code listener} or * {@code handler} is null. + * + * @deprecated Use {@link #GestureDetector(android.content.Context, + * android.view.GestureDetector.OnGestureListener, android.os.Handler)} instead. */ + @Deprecated public GestureDetector(OnGestureListener listener, Handler handler) { - mHandler = new GestureHandler(handler); - mListener = listener; - init(); + this(null, listener, handler); } /** @@ -222,19 +255,84 @@ public class GestureDetector { * * @param listener the listener invoked for all the callbacks, this must * not be null. + * * @throws NullPointerException if {@code listener} is null. + * + * @deprecated Use {@link #GestureDetector(android.content.Context, + * android.view.GestureDetector.OnGestureListener)} instead. */ + @Deprecated public GestureDetector(OnGestureListener listener) { - mHandler = new GestureHandler(); + this(null, listener, null); + } + + /** + * Creates a GestureDetector with the supplied listener. + * You may only use this constructor from a UI thread (this is the usual situation). + * @see android.os.Handler#Handler() + * + * @param context the application's context + * @param listener the listener invoked for all the callbacks, this must + * not be null. + * + * @throws NullPointerException if {@code listener} is null. + */ + public GestureDetector(Context context, OnGestureListener listener) { + this(context, listener, null); + } + + /** + * Creates a GestureDetector with the supplied listener. + * You may only use this constructor from a UI thread (this is the usual situation). + * @see android.os.Handler#Handler() + * + * @param context the application's context + * @param listener the listener invoked for all the callbacks, this must + * not be null. + * @param handler the handler to use + * + * @throws NullPointerException if {@code listener} is null. + */ + public GestureDetector(Context context, OnGestureListener listener, Handler handler) { + if (handler != null) { + mHandler = new GestureHandler(handler); + } else { + mHandler = new GestureHandler(); + } mListener = listener; - init(); + init(context); } - private void init() { + private void init(Context context) { if (mListener == null) { throw new NullPointerException("OnGestureListener must not be null"); } mIsLongpressEnabled = true; + + // Fallback to support pre-donuts releases + int touchSlop, doubleTapSlop; + if (context == null) { + //noinspection deprecation + touchSlop = ViewConfiguration.getTouchSlop(); + doubleTapSlop = ViewConfiguration.getDoubleTapSlop(); + //noinspection deprecation + mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity(); + } else { + final ViewConfiguration configuration = ViewConfiguration.get(context); + touchSlop = configuration.getScaledTouchSlop(); + doubleTapSlop = configuration.getScaledDoubleTapSlop(); + mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity(); + } + mTouchSlopSquare = touchSlop * touchSlop; + mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop; + } + + /** + * @hide pending API council + * @param onDoubleTapListener + */ + public void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener) { + mDoubleTapListener = onDoubleTapListener; } /** @@ -266,9 +364,6 @@ public class GestureDetector { * else false. */ public boolean onTouchEvent(MotionEvent ev) { - final long tapTime = ViewConfiguration.getTapTimeout(); - final long longpressTime = ViewConfiguration.getLongPressTimeout(); - final int touchSlop = ViewConfiguration.getTouchSlop(); final int action = ev.getAction(); final float y = ev.getY(); final float x = ev.getX(); @@ -282,19 +377,32 @@ public class GestureDetector { switch (action) { case MotionEvent.ACTION_DOWN: + if (mDoubleTapListener != null) { + mHandler.removeMessages(TAP); + if (mCurrentDownEvent != null && isConsideredDoubleTap(mCurrentDownEvent, ev)) { + // This is a second tap + mIsDoubleTapping = true; + handled = mDoubleTapListener.onDoubleTapEvent(ev); + } else { + // This is a first tap + mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT); + } + } + mLastMotionX = x; mLastMotionY = y; mCurrentDownEvent = MotionEvent.obtain(ev); mAlwaysInTapRegion = true; + mAlwaysInBiggerTapRegion = true; mInLongPress = false; - + if (mIsLongpressEnabled) { mHandler.removeMessages(LONG_PRESS); mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime() - + tapTime + longpressTime); + + TAP_TIMEOUT + LONGPRESS_TIMEOUT); } - mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + tapTime); - handled = mListener.onDown(ev); + mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT); + handled |= mListener.onDown(ev); break; case MotionEvent.ACTION_MOVE: @@ -303,11 +411,13 @@ public class GestureDetector { } final float scrollX = mLastMotionX - x; final float scrollY = mLastMotionY - y; - if (mAlwaysInTapRegion) { + if (mIsDoubleTapping) { + handled = mDoubleTapListener.onDoubleTapEvent(ev); + } else if (mAlwaysInTapRegion) { final int deltaX = (int) (x - mCurrentDownEvent.getX()); final int deltaY = (int) (y - mCurrentDownEvent.getY()); int distance = (deltaX * deltaX) + (deltaY * deltaY); - if (distance > TOUCH_SLOP_SQUARE) { + if (distance > mTouchSlopSquare) { handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); mLastMotionX = x; mLastMotionY = y; @@ -315,6 +425,9 @@ public class GestureDetector { mHandler.removeMessages(SHOW_PRESS); mHandler.removeMessages(LONG_PRESS); } + if (distance > mBiggerTouchSlopSquare) { + mAlwaysInBiggerTapRegion = false; + } } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) { handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); mLastMotionX = x; @@ -323,8 +436,13 @@ public class GestureDetector { break; case MotionEvent.ACTION_UP: - mCurrentUpEvent = MotionEvent.obtain(ev); - if (mInLongPress) { + MotionEvent currentUpEvent = MotionEvent.obtain(ev); + if (mIsDoubleTapping) { + handled = mDoubleTapListener.onDoubleTapEvent(ev); + mIsDoubleTapping = false; + break; + } else if (mInLongPress) { + mHandler.removeMessages(TAP); mInLongPress = false; break; } @@ -338,9 +456,9 @@ public class GestureDetector { final float velocityY = velocityTracker.getYVelocity(); final float velocityX = velocityTracker.getXVelocity(); - if ((Math.abs(velocityY) > ViewConfiguration.getMinimumFlingVelocity()) - || (Math.abs(velocityX) > ViewConfiguration.getMinimumFlingVelocity())){ - handled = mListener.onFling(mCurrentDownEvent, mCurrentUpEvent, velocityX, velocityY); + if ((Math.abs(velocityY) > mMinimumFlingVelocity) + || (Math.abs(velocityX) > mMinimumFlingVelocity)){ + handled = mListener.onFling(mCurrentDownEvent, currentUpEvent, velocityX, velocityY); } } mVelocityTracker.recycle(); @@ -351,6 +469,7 @@ public class GestureDetector { case MotionEvent.ACTION_CANCEL: mHandler.removeMessages(SHOW_PRESS); mHandler.removeMessages(LONG_PRESS); + mHandler.removeMessages(TAP); mVelocityTracker.recycle(); mVelocityTracker = null; if (mInLongPress) { @@ -361,6 +480,20 @@ public class GestureDetector { return handled; } + private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent secondDown) { + if (!mAlwaysInBiggerTapRegion) { + return false; + } + + if (secondDown.getEventTime() - firstDown.getEventTime() > DOUBLE_TAP_TIMEOUT) { + return false; + } + + int deltaX = (int) firstDown.getX() - (int) secondDown.getX(); + int deltaY = (int) firstDown.getY() - (int) secondDown.getY(); + return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare); + } + private void dispatchLongPress() { mInLongPress = true; mListener.onLongPress(mCurrentDownEvent); diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 40251db..a856b24 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -104,6 +104,9 @@ interface IWindowManager int getKeycodeState(int sw); int getKeycodeStateForDevice(int devid, int sw); + // Report whether the hardware supports the given keys; returns true if successful + boolean hasKeys(in int[] keycodes, inout boolean[] keyExists); + // For testing void setInTouchMode(boolean showFocus); diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java index 0347d50..25958aa 100644 --- a/core/java/android/view/KeyCharacterMap.java +++ b/core/java/android/view/KeyCharacterMap.java @@ -18,6 +18,8 @@ package android.view; import android.text.method.MetaKeyKeyListener; import android.util.SparseIntArray; +import android.os.RemoteException; +import android.os.ServiceManager; import android.os.SystemClock; import android.util.SparseArray; @@ -350,6 +352,28 @@ public class KeyCharacterMap return getKeyboardType_native(mPointer); } + /** + * Queries the framework about whether any physical keys exist on the + * device that are capable of producing the given key codes. + */ + public static boolean deviceHasKey(int keyCode) { + int[] codeArray = new int[1]; + codeArray[0] = keyCode; + boolean[] ret = deviceHasKeys(codeArray); + return ret[0]; + } + + public static boolean[] deviceHasKeys(int[] keyCodes) { + boolean[] ret = new boolean[keyCodes.length]; + IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); + try { + wm.hasKeys(keyCodes, ret); + } catch (RemoteException e) { + // no fallback; just return the empty array + } + return ret; + } + private int mPointer; private int mKeyboardDevice; diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 1575aad..d5434b6 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -92,7 +92,7 @@ public class KeyEvent implements Parcelable { public static final int KEYCODE_SYM = 63; public static final int KEYCODE_EXPLORER = 64; public static final int KEYCODE_ENVELOPE = 65; - public static final int KEYCODE_ENTER = 66; + public static final int KEYCODE_ENTER = 66; public static final int KEYCODE_DEL = 67; public static final int KEYCODE_GRAVE = 68; public static final int KEYCODE_MINUS = 69; @@ -144,8 +144,12 @@ public class KeyEvent implements Parcelable { public static final int ACTION_UP = 1; /** * {@link #getAction} value: multiple duplicate key events have - * occurred in a row. The {#link {@link #getRepeatCount()} method returns - * the number of duplicates. + * occurred in a row, or a complex string is being delivered. If the + * key code is not {#link {@link #KEYCODE_UNKNOWN} then the + * {#link {@link #getRepeatCount()} method returns the number of times + * the given key code should be executed. + * Otherwise, if the key code {@link #KEYCODE_UNKNOWN}, then + * this is a sequence of characters as returned by {@link #getCharacters}. */ public static final int ACTION_MULTIPLE = 2; @@ -248,6 +252,7 @@ public class KeyEvent implements Parcelable { private int mFlags; private long mDownTime; private long mEventTime; + private String mCharacters; public interface Callback { /** @@ -406,6 +411,28 @@ public class KeyEvent implements Parcelable { } /** + * Create a new key event for a string of characters. The key code, + * action, and repeat could will automatically be set to + * {@link #KEYCODE_UNKNOWN}, {@link #ACTION_MULTIPLE}, and 0 for you. + * + * @param time The time (in {@link android.os.SystemClock#uptimeMillis}) + * at which this event occured. + * @param characters The string of characters. + * @param device The device ID that generated the key event. + * @param flags The flags for this key event + */ + public KeyEvent(long time, String characters, int device, int flags) { + mDownTime = time; + mEventTime = time; + mCharacters = characters; + mAction = ACTION_MULTIPLE; + mKeyCode = KEYCODE_UNKNOWN; + mRepeatCount = 0; + mDeviceId = device; + mFlags = flags; + } + + /** * Copy an existing key event, modifying its time and repeat count. * * @param origEvent The existing event to be copied. @@ -423,6 +450,7 @@ public class KeyEvent implements Parcelable { mDeviceId = origEvent.mDeviceId; mScancode = origEvent.mScancode; mFlags = origEvent.mFlags; + mCharacters = origEvent.mCharacters; } /** @@ -441,6 +469,8 @@ public class KeyEvent implements Parcelable { mDeviceId = origEvent.mDeviceId; mScancode = origEvent.mScancode; mFlags = origEvent.mFlags; + // Don't copy mCharacters, since one way or the other we'll lose it + // when changing the action. } /** @@ -580,7 +610,7 @@ public class KeyEvent implements Parcelable { /** * Retrieve the key code of the key event. This is the physical key that - * was pressed -- not the Unicode character. + * was pressed, <em>not</em> the Unicode character. * * @return The key code of the event. */ @@ -589,6 +619,18 @@ public class KeyEvent implements Parcelable { } /** + * For the special case of a {@link #ACTION_MULTIPLE} event with key + * code of {@link #KEYCODE_UNKNOWN}, this is a raw string of characters + * associated with the event. In all other cases it is null. + * + * @return Returns a String of 1 or more characters associated with + * the event. + */ + public final String getCharacters() { + return mCharacters; + } + + /** * Retrieve the hardware key id of this key event. These values are not * reliable and vary from device to device. * @@ -772,16 +814,18 @@ public class KeyEvent implements Parcelable { if (receiver.onKeyMultiple(code, count, this)) { return true; } - mAction = ACTION_DOWN; - mRepeatCount = 0; - boolean handled = receiver.onKeyDown(code, this); - if (handled) { - mAction = ACTION_UP; - receiver.onKeyUp(code, this); + if (code != KeyEvent.KEYCODE_UNKNOWN) { + mAction = ACTION_DOWN; + mRepeatCount = 0; + boolean handled = receiver.onKeyDown(code, this); + if (handled) { + mAction = ACTION_UP; + receiver.onKeyUp(code, this); + } + mAction = ACTION_MULTIPLE; + mRepeatCount = count; + return handled; } - mAction = ACTION_MULTIPLE; - mRepeatCount = count; - return handled; } return false; } diff --git a/core/java/android/view/TouchDelegate.java b/core/java/android/view/TouchDelegate.java index 057df92..27b49db 100644 --- a/core/java/android/view/TouchDelegate.java +++ b/core/java/android/view/TouchDelegate.java @@ -77,7 +77,9 @@ public class TouchDelegate { * actual extent. */ public static final int TO_RIGHT = 8; - + + private int mSlop; + /** * Constructor * @@ -87,10 +89,10 @@ public class TouchDelegate { */ public TouchDelegate(Rect bounds, View delegateView) { mBounds = bounds; - - int slop = ViewConfiguration.getTouchSlop(); + + mSlop = ViewConfiguration.get(delegateView.getContext()).getScaledTouchSlop(); mSlopBounds = new Rect(bounds); - mSlopBounds.inset(-slop, -slop); + mSlopBounds.inset(-mSlop, -mSlop); mDelegateView = delegateView; } @@ -141,7 +143,7 @@ public class TouchDelegate { } else { // Offset event coordinates to be outside the target view (in case it does // something like tracking pressed state) - int slop = ViewConfiguration.getTouchSlop(); + int slop = mSlop; event.setLocation(-(slop * 2), -(slop * 2)); } handled = delegateView.dispatchTouchEvent(event); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 85f482c..a51b564 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -60,12 +60,31 @@ import java.util.Arrays; /** * <p> - * The <code>View</code> class represents the basic UI building block. A view + * This class represents the basic building block for user interface components. A View * occupies a rectangular area on the screen and is responsible for drawing and - * event handling. <code>View</code> is the base class for <em>widgets</em>, - * used to create interactive graphical user interfaces. + * event handling. View is the base class for <em>widgets</em>, which are + * used to create interactive UI components (buttons, text fields, etc.). The + * {@link android.view.ViewGroup} subclass is the base class for <em>layouts</em>, which + * are invisible containers that hold other Views (or other ViewGroups) and define + * their layout properties. * </p> * + * <div class="special"> + * <p>For an introduction to using this class to develop your + * application's user interface, read the Developer Guide documentation on + * <strong><a href="{@docRoot}guide/topics/ui/index.html">User Interface</a></strong>. Special topics + * include: + * <br/><a href="{@docRoot}guide/topics/ui/declaring-layout.html">Declaring Layout</a> + * <br/><a href="{@docRoot}guide/topics/ui/menus.html">Creating Menus</a> + * <br/><a href="{@docRoot}guide/topics/ui/layout-objects.html">Common Layout Objects</a> + * <br/><a href="{@docRoot}guide/topics/ui/binding.html">Binding to Data with AdapterView</a> + * <br/><a href="{@docRoot}guide/topics/ui/ui-events.html">Handling UI Events</a> + * <br/><a href="{@docRoot}guide/topics/ui/themes.html">Applying Styles and Themes</a> + * <br/><a href="{@docRoot}guide/topics/ui/custom-components.html">Building Custom Components</a> + * <br/><a href="{@docRoot}guide/topics/ui/how-android-draws.html">How Android Draws Views</a>. + * </p> + * </div> + * * <a name="Using"></a> * <h3>Using Views</h3> * <p> @@ -1308,6 +1327,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback { static final int HAS_BOUNDS = 0x00000010; /** {@hide} */ static final int DRAWN = 0x00000020; + /** + * When this flag is set, this view is running an animation on behalf of its + * children and should therefore not cancel invalidate requests, even if they + * lie outside of this view's bounds. + * + * {@hide} + */ + static final int DRAW_ANIMATION = 0x00000040; /** {@hide} */ static final int SKIP_DRAW = 0x00000080; /** {@hide} */ @@ -1353,8 +1380,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback { */ static final int SCROLL_CONTAINER_ADDED = 0x00100000; - // Note: flag 0x00000040 is available - /** * The parent this view is attached to. * {@hide} @@ -1559,6 +1584,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { private int mNextFocusDownId = View.NO_ID; private CheckForLongPress mPendingCheckForLongPress; + private UnsetPressedState mUnsetPressedState; /** * Whether the long press's action has been invoked. The tap's action is invoked on the @@ -1898,7 +1924,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback { initScrollCache(); mScrollCache.fadingEdgeLength = a.getDimensionPixelSize( - R.styleable.View_fadingEdgeLength, ViewConfiguration.getFadingEdgeLength()); + R.styleable.View_fadingEdgeLength, + ViewConfiguration.get(mContext).getScaledFadingEdgeLength()); } /** @@ -2013,36 +2040,38 @@ public class View implements Drawable.Callback, KeyEvent.Callback { mScrollCache.scrollBar = new ScrollBarDrawable(); } - mScrollCache.scrollBarSize = a.getDimensionPixelSize( + final ScrollabilityCache scrollabilityCache = mScrollCache; + + scrollabilityCache.scrollBarSize = a.getDimensionPixelSize( com.android.internal.R.styleable.View_scrollbarSize, - ViewConfiguration.getScrollBarSize()); + ViewConfiguration.get(mContext).getScaledScrollBarSize()); Drawable track = a.getDrawable(R.styleable.View_scrollbarTrackHorizontal); - mScrollCache.scrollBar.setHorizontalTrackDrawable(track); + scrollabilityCache.scrollBar.setHorizontalTrackDrawable(track); Drawable thumb = a.getDrawable(R.styleable.View_scrollbarThumbHorizontal); if (thumb != null) { - mScrollCache.scrollBar.setHorizontalThumbDrawable(thumb); + scrollabilityCache.scrollBar.setHorizontalThumbDrawable(thumb); } boolean alwaysDraw = a.getBoolean(R.styleable.View_scrollbarAlwaysDrawHorizontalTrack, false); if (alwaysDraw) { - mScrollCache.scrollBar.setAlwaysDrawHorizontalTrack(true); + scrollabilityCache.scrollBar.setAlwaysDrawHorizontalTrack(true); } track = a.getDrawable(R.styleable.View_scrollbarTrackVertical); - mScrollCache.scrollBar.setVerticalTrackDrawable(track); + scrollabilityCache.scrollBar.setVerticalTrackDrawable(track); thumb = a.getDrawable(R.styleable.View_scrollbarThumbVertical); if (thumb != null) { - mScrollCache.scrollBar.setVerticalThumbDrawable(thumb); + scrollabilityCache.scrollBar.setVerticalThumbDrawable(thumb); } alwaysDraw = a.getBoolean(R.styleable.View_scrollbarAlwaysDrawVerticalTrack, false); if (alwaysDraw) { - mScrollCache.scrollBar.setAlwaysDrawVerticalTrack(true); + scrollabilityCache.scrollBar.setAlwaysDrawVerticalTrack(true); } // Re-apply user/background padding so that scrollbar(s) get added @@ -2056,7 +2085,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { */ private void initScrollCache() { if (mScrollCache == null) { - mScrollCache = new ScrollabilityCache(); + mScrollCache = new ScrollabilityCache(ViewConfiguration.get(mContext)); } } @@ -2635,6 +2664,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { */ public void setVisibility(int visibility) { setFlags(visibility, VISIBILITY_MASK); + if (mBGDrawable != null) mBGDrawable.setVisible(visibility == VISIBLE, false); } /** @@ -3410,6 +3440,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } void performCollectViewAttributes(int visibility) { + //noinspection PointlessBitwiseExpression if (((visibility | mViewFlags) & (VISIBILITY_MASK | KEEP_SCREEN_ON)) == (VISIBLE | KEEP_SCREEN_ON)) { mAttachInfo.mKeepScreenOn = true; @@ -3708,10 +3739,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } } - final UnsetPressedState unsetPressedState = new UnsetPressedState(); - if (!post(unsetPressedState)) { + if (mUnsetPressedState == null) { + mUnsetPressedState = new UnsetPressedState(); + } + + if (!post(mUnsetPressedState)) { // If the post failed, unpress right now - unsetPressedState.run(); + mUnsetPressedState.run(); } } break; @@ -3734,7 +3768,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { final int y = (int) event.getY(); // Be lenient about moving outside of buttons - int slop = ViewConfiguration.getTouchSlop(); + int slop = ViewConfiguration.get(mContext).getScaledTouchSlop(); if ((x < 0 - slop) || (x >= getWidth() + slop) || (y < 0 - slop) || (y >= getHeight() + slop)) { // Outside button @@ -4413,14 +4447,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * @see #invalidate() */ public void postInvalidate() { - // We try only with the AttachInfo because there's no point in invalidating - // if we are not attached to our window - if (mAttachInfo != null) { - Message msg = Message.obtain(); - msg.what = AttachInfo.INVALIDATE_MSG; - msg.obj = this; - mAttachInfo.mHandler.sendMessage(msg); - } + postInvalidateDelayed(0); } /** @@ -4436,16 +4463,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * @see #invalidate(Rect) */ public void postInvalidate(int left, int top, int right, int bottom) { - // We try only with the AttachInfo because there's no point in invalidating - // if we are not attached to our window - if (mAttachInfo != null) { - Message msg = Message.obtain(); - msg.what = AttachInfo.INVALIDATE_RECT_MSG; - msg.obj = this; - msg.arg1 = (left << 16) | (top & 0xFFFF); - msg.arg2 = (right << 16) | (bottom & 0xFFFF); - mAttachInfo.mHandler.sendMessage(msg); - } + postInvalidateDelayed(0, left, top, right, bottom); } /** @@ -4477,16 +4495,22 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * @param right The right coordinate of the rectangle to invalidate. * @param bottom The bottom coordinate of the rectangle to invalidate. */ - public void postInvalidateDelayed(long delayMilliseconds, int left, int top - , int right, int bottom) { + public void postInvalidateDelayed(long delayMilliseconds, int left, int top, + int right, int bottom) { + // We try only with the AttachInfo because there's no point in invalidating // if we are not attached to our window if (mAttachInfo != null) { - Message msg = Message.obtain(); + final AttachInfo.InvalidateInfo info = AttachInfo.InvalidateInfo.acquire(); + info.target = this; + info.left = left; + info.top = top; + info.right = right; + info.bottom = bottom; + + final Message msg = Message.obtain(); msg.what = AttachInfo.INVALIDATE_RECT_MSG; - msg.obj = this; - msg.arg1 = (left << 16) | (top & 0xFFFF); - msg.arg2 = (right << 16) | (bottom & 0xFFFF); + msg.obj = info; mAttachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds); } } @@ -4865,7 +4889,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback { final boolean drawHorizontalScrollBar = (viewFlags & SCROLLBARS_HORIZONTAL) == SCROLLBARS_HORIZONTAL; final boolean drawVerticalScrollBar = - (viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL; + (viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL + && !isVerticalScrollBarHidden(); if (drawVerticalScrollBar || drawHorizontalScrollBar) { final int width = mRight - mLeft; @@ -4887,6 +4912,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } } } + + /** + * Override this if the vertical scrollbar needs to be hidden in a subclass, like when + * FastScroller is visible. + * @return whether to temporarily hide the vertical scrollbar + * @hide + */ + protected boolean isVerticalScrollBarHidden() { + return false; + } /** * <p>Draw the horizontal scrollbar if @@ -5022,6 +5057,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { if (mPendingCheckForLongPress != null) { removeCallbacks(mPendingCheckForLongPress); } + destroyDrawingCache(); } /** @@ -5408,7 +5444,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { if (width <= 0 || height <= 0 || (width * height * (opaque ? 2 : 4) >= // Projected bitmap size in bytes - ViewConfiguration.getMaximumDrawingCacheSize())) { + ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) { if (mDrawingCache != null) { mDrawingCache.recycle(); } @@ -5485,9 +5521,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback { final int restoreCount = canvas.save(); canvas.translate(-mScrollX, -mScrollY); + mPrivateFlags |= DRAWN; + // Fast path for layouts with no backgrounds if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { - mPrivateFlags |= DRAWN; if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); } @@ -5616,6 +5653,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); } + mPrivateFlags |= DRAWN; + /* * Draw traversal performs several drawing steps which must be executed * in the appropriate order: @@ -5656,7 +5695,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback { boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0; if (!verticalEdges && !horizontalEdges) { // Step 3, draw the content - mPrivateFlags |= DRAWN; onDraw(canvas); // Step 4, draw the children @@ -5760,7 +5798,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } // Step 3, draw the content - mPrivateFlags |= DRAWN; onDraw(canvas); // Step 4, draw the children @@ -7671,6 +7708,67 @@ public class View implements Drawable.Callback, KeyEvent.Callback { void playSoundEffect(int effectId); } + /** + * InvalidateInfo is used to post invalidate(int, int, int, int) messages + * to a Handler. This class contains the target (View) to invalidate and + * the coordinates of the dirty rectangle. + * + * For performance purposes, this class also implements a pool of up to + * POOL_LIMIT objects that get reused. This reduces memory allocations + * whenever possible. + * + * The pool is implemented as a linked list of InvalidateInfo object with + * the root pointing to the next available InvalidateInfo. If the root + * is null (i.e. when all instances from the pool have been acquired), + * then a new InvalidateInfo is created and returned to the caller. + * + * An InvalidateInfo is sent back to the pool by calling its release() + * method. If the pool is full the object is simply discarded. + * + * This implementation follows the object pool pattern used in the + * MotionEvent class. + */ + static class InvalidateInfo { + private static final int POOL_LIMIT = 10; + private static final Object sLock = new Object(); + + private static int sAcquiredCount = 0; + private static InvalidateInfo sRoot; + + private InvalidateInfo next; + + View target; + + int left; + int top; + int right; + int bottom; + + static InvalidateInfo acquire() { + synchronized (sLock) { + if (sRoot == null) { + return new InvalidateInfo(); + } + + InvalidateInfo info = sRoot; + sRoot = info.next; + sAcquiredCount--; + + return info; + } + } + + void release() { + synchronized (sLock) { + if (sAcquiredCount < POOL_LIMIT) { + sAcquiredCount++; + next = sRoot; + sRoot = this; + } + } + } + } + final IWindowSession mSession; final IWindow mWindow; @@ -7839,18 +7937,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * instances of View.</p> */ private static class ScrollabilityCache { - public int fadingEdgeLength = ViewConfiguration.getFadingEdgeLength(); + public int fadingEdgeLength; - public int scrollBarSize = ViewConfiguration.getScrollBarSize(); + public int scrollBarSize; public ScrollBarDrawable scrollBar; public final Paint paint; public final Matrix matrix; public Shader shader; - private int mLastColor = 0; + private int mLastColor; + + public ScrollabilityCache(ViewConfiguration configuration) { + fadingEdgeLength = configuration.getScaledFadingEdgeLength(); + scrollBarSize = configuration.getScaledScrollBarSize(); - public ScrollabilityCache() { paint = new Paint(); matrix = new Matrix(); // use use a height of 1, and then wack the matrix each time we diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index b7110ce..7153ea1 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -16,17 +16,19 @@ package android.view; +import android.content.Context; +import android.util.DisplayMetrics; +import android.util.SparseArray; + /** * Contains methods to standard constants used in the UI for timeouts, sizes, and distances. - * */ public class ViewConfiguration { - /** * Defines the width of the horizontal scrollbar and the height of the vertical scrollbar in * pixels */ - private static final int SCROLL_BAR_SIZE = 6; + private static final int SCROLL_BAR_SIZE = 10; /** * Defines the length of the fading edges in pixels @@ -83,6 +85,11 @@ public class ViewConfiguration { private static final int TOUCH_SLOP = 12; /** + * Distance between the first touch and second touch to still be considered a double tap + */ + private static final int DOUBLE_TAP_SLOP = 100; + + /** * Distance a touch needs to be outside of a window's bounds for it to * count as outside for purposes of dismissing the window. */ @@ -97,28 +104,124 @@ public class ViewConfiguration { * The maximum size of View's drawing cache, expressed in bytes. This size * should be at least equal to the size of the screen in ARGB888 format. */ - private static final int MAXIMUM_DRAWING_CACHE_SIZE = 320 * 480 * 4; // One HVGA screen, ARGB8888 + @Deprecated + private static final int MAXIMUM_DRAWING_CACHE_SIZE = 320 * 480 * 4; // HVGA screen, ARGB8888 /** * The coefficient of friction applied to flings/scrolls. */ private static float SCROLL_FRICTION = 0.015f; + private final int mEdgeSlop; + private final int mFadingEdgeLength; + private final int mMinimumFlingVelocity; + private final int mScrollbarSize; + private final int mTouchSlop; + private final int mDoubleTapSlop; + private final int mWindowTouchSlop; + private final int mMaximumDrawingCacheSize; + + private static final SparseArray<ViewConfiguration> sConfigurations = + new SparseArray<ViewConfiguration>(2); + + /** + * @deprecated Use {@link android.view.ViewConfiguration#get(android.content.Context)} instead. + */ + @Deprecated + public ViewConfiguration() { + mEdgeSlop = EDGE_SLOP; + mFadingEdgeLength = FADING_EDGE_LENGTH; + mMinimumFlingVelocity = MINIMUM_FLING_VELOCITY; + mScrollbarSize = SCROLL_BAR_SIZE; + mTouchSlop = TOUCH_SLOP; + mDoubleTapSlop = DOUBLE_TAP_SLOP; + mWindowTouchSlop = WINDOW_TOUCH_SLOP; + //noinspection deprecation + mMaximumDrawingCacheSize = MAXIMUM_DRAWING_CACHE_SIZE; + } + + /** + * Creates a new configuration for the specified context. The configuration depends on + * various parameters of the context, like the dimension of the display or the density + * of the display. + * + * @param context The application context used to initialize this view configuration. + * + * @see #get(android.content.Context) + * @see android.util.DisplayMetrics + */ + private ViewConfiguration(Context context) { + final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + final float density = metrics.density; + + mEdgeSlop = (int) (density * EDGE_SLOP + 0.5f); + mFadingEdgeLength = (int) (density * FADING_EDGE_LENGTH + 0.5f); + mMinimumFlingVelocity = (int) (density * MINIMUM_FLING_VELOCITY + 0.5f); + mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f); + mTouchSlop = (int) (density * TOUCH_SLOP + 0.5f); + mDoubleTapSlop = (int) (density * DOUBLE_TAP_SLOP + 0.5f); + mWindowTouchSlop = (int) (density * WINDOW_TOUCH_SLOP + 0.5f); + + // Size of the screen in bytes, in ARGB_8888 format + mMaximumDrawingCacheSize = 4 * metrics.widthPixels * metrics.heightPixels; + } + + /** + * Returns a configuration for the specified context. The configuration depends on + * various parameters of the context, like the dimension of the display or the + * density of the display. + * + * @param context The application context used to initialize the view configuration. + */ + public static ViewConfiguration get(Context context) { + final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + final int density = (int) (100.0f * metrics.density); + + ViewConfiguration configuration = sConfigurations.get(density); + if (configuration == null) { + configuration = new ViewConfiguration(context); + sConfigurations.put(density, configuration); + } + + return configuration; + } + /** * @return The width of the horizontal scrollbar and the height of the vertical * scrollbar in pixels + * + * @deprecated Use {@link #getScaledScrollBarSize()} instead. */ + @Deprecated public static int getScrollBarSize() { return SCROLL_BAR_SIZE; } /** + * @return The width of the horizontal scrollbar and the height of the vertical + * scrollbar in pixels + */ + public int getScaledScrollBarSize() { + return mScrollbarSize; + } + + /** * @return Defines the length of the fading edges in pixels + * + * @deprecated Use {@link #getScaledFadingEdgeLength()} instead. */ + @Deprecated public static int getFadingEdgeLength() { return FADING_EDGE_LENGTH; } - + + /** + * @return Defines the length of the fading edges in pixels + */ + public int getScaledFadingEdgeLength() { + return mFadingEdgeLength; + } + /** * @return Defines the duration in milliseconds of the pressed state in child * components. @@ -156,44 +259,121 @@ public class ViewConfiguration { /** * @return Inset in pixels to look for touchable content when the user touches the edge of the * screen + * + * @deprecated Use {@link #getScaledEdgeSlop()} instead. */ + @Deprecated public static int getEdgeSlop() { return EDGE_SLOP; } - + + /** + * @return Inset in pixels to look for touchable content when the user touches the edge of the + * screen + */ + public int getScaledEdgeSlop() { + return mEdgeSlop; + } + /** * @return Distance a touch can wander before we think the user is scrolling in pixels + * + * @deprecated Use {@link #getScaledTouchSlop()} instead. */ + @Deprecated public static int getTouchSlop() { return TOUCH_SLOP; } + + /** + * @return Distance a touch can wander before we think the user is scrolling in pixels + */ + public int getScaledTouchSlop() { + return mTouchSlop; + } + + /** + * @return Distance between the first touch and second touch to still be + * considered a double tap + * @deprecated Use {@link #getScaledDoubleTapSlop()} instead. + * @hide The only client of this should be GestureDetector, which needs this + * for clients that still use its deprecated constructor. + */ + @Deprecated + public static int getDoubleTapSlop() { + return DOUBLE_TAP_SLOP; + } /** + * @return Distance between the first touch and second touch to still be + * considered a double tap + * @hide pending API council + */ + public int getScaledDoubleTapSlop() { + return mDoubleTapSlop; + } + + /** * @return Distance a touch must be outside the bounds of a window for it * to be counted as outside the window for purposes of dismissing that * window. + * + * @deprecated Use {@link #getScaledWindowTouchSlop()} instead. */ + @Deprecated public static int getWindowTouchSlop() { return WINDOW_TOUCH_SLOP; } + + /** + * @return Distance a touch must be outside the bounds of a window for it + * to be counted as outside the window for purposes of dismissing that + * window. + */ + public int getScaledWindowTouchSlop() { + return mWindowTouchSlop; + } /** - * Minimum velocity to initiate a fling, as measured in pixels per second + * @return Minimum velocity to initiate a fling, as measured in pixels per second. + * + * @deprecated Use {@link #getScaledMinimumFlingVelocity()} instead. */ - public static int getMinimumFlingVelocity() { - return MINIMUM_FLING_VELOCITY; + @Deprecated + public static int getMinimumFlingVelocity() { + return MINIMUM_FLING_VELOCITY; + } + + /** + * @return Minimum velocity to initiate a fling, as measured in pixels per second. + */ + public int getScaledMinimumFlingVelocity() { + return mMinimumFlingVelocity; } /** * The maximum drawing cache size expressed in bytes. * * @return the maximum size of View's drawing cache expressed in bytes + * + * @deprecated Use {@link #getScaledMaximumDrawingCacheSize()} instead. */ + @Deprecated public static int getMaximumDrawingCacheSize() { + //noinspection deprecation return MAXIMUM_DRAWING_CACHE_SIZE; } /** + * The maximum drawing cache size expressed in bytes. + * + * @return the maximum size of View's drawing cache expressed in bytes + */ + public int getScaledMaximumDrawingCacheSize() { + return mMaximumDrawingCacheSize; + } + + /** * The amount of time that the zoom controls should be * displayed on the screen expressed in milliseconds. * diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index e26a19e..c758662 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -25,6 +25,7 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Region; +import android.graphics.RectF; import android.os.Parcelable; import android.util.AttributeSet; import android.util.EventLog; @@ -74,6 +75,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // The current transformation to apply on the child being drawn private Transformation mChildTransformation; + private RectF mInvalidateRegion; // Target of Motion events private View mMotionTarget; @@ -1199,6 +1201,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } + // We will draw our child's animation, let's reset the flag + mPrivateFlags &= ~DRAW_ANIMATION; mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED; boolean more = false; @@ -1328,8 +1332,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager boolean concatMatrix = false; if (a != null) { - if (!a.isInitialized()) { + if (mInvalidateRegion == null) { + mInvalidateRegion = new RectF(); + } + final RectF region = mInvalidateRegion; + + final boolean initialized = a.isInitialized(); + if (!initialized) { a.initialize(cr - cl, cb - ct, getWidth(), getHeight()); + a.initializeInvalidateRegion(cl, ct, cr, cb); child.onAnimationStart(); } @@ -1347,10 +1358,22 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager FLAG_OPTIMIZE_INVALIDATE) { mGroupFlags |= FLAG_INVALIDATE_REQUIRED; } else if ((flags & FLAG_INVALIDATE_REQUIRED) == 0) { + // The child need to draw an animation, potentially offscreen, so + // make sure we do not cancel invalidate requests + mPrivateFlags |= DRAW_ANIMATION; invalidate(cl, ct, cr, cb); } } else { - mGroupFlags |= FLAG_INVALIDATE_REQUIRED; + a.getInvalidateRegion(cl, ct, cr, cb, region, transformToApply); + + // The child need to draw an animation, potentially offscreen, so + // make sure we do not cancel invalidate requests + mPrivateFlags |= DRAW_ANIMATION; + // Enlarge the invalidate region to account for rounding errors + // in Animation#getInvalidateRegion(); Using 0.5f is unfortunately + // not enough for some types of animations (e.g. scale down.) + invalidate((int) (region.left - 1.0f), (int) (region.top - 1.0f), + (int) (region.right + 1.0f), (int) (region.bottom + 1.0f)); } } } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) == @@ -1367,7 +1390,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } - if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW)) { + if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) && + (child.mPrivateFlags & DRAW_ANIMATION) == 0) { return more; } @@ -1435,10 +1459,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + // Clear the flag as early as possible to allow draw() implementations + // to call invalidate() successfully when doing animations + child.mPrivateFlags |= DRAWN; + if (hasNoCache) { // Fast path for layouts with no backgrounds if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { - child.mPrivateFlags |= DRAWN; if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); } @@ -1455,7 +1482,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager cachePaint.setAlpha(255); mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE; } - child.mPrivateFlags |= DRAWN; if (ViewRoot.PROFILE_DRAWING) { EventLog.writeEvent(60003, hashCode()); } @@ -1922,8 +1948,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager LayoutAnimationController.AnimationParameters animationParams = params.layoutAnimationParameters; if (animationParams == null) { - animationParams = - new LayoutAnimationController.AnimationParameters(); + animationParams = new LayoutAnimationController.AnimationParameters(); params.layoutAnimationParameters = animationParams; } @@ -2278,8 +2303,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final int[] location = attachInfo.mInvalidateChildLocation; location[CHILD_LEFT_INDEX] = child.mLeft; location[CHILD_TOP_INDEX] = child.mTop; + + // If the child is drawing an animation, we want to copy this flag onto + // ourselves and the parent to make sure the invalidate request goes + // through + final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION; do { + if (drawAnimation && parent instanceof View) { + ((View) parent).mPrivateFlags |= DRAW_ANIMATION; + } parent = parent.invalidateChildInParent(location, dirty); } while (parent != null); } @@ -2307,7 +2340,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final int left = mLeft; final int top = mTop; - if (dirty.intersect(0, 0, mRight - left, mBottom - top)) { + if (dirty.intersect(0, 0, mRight - left, mBottom - top) || + (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) { mPrivateFlags &= ~DRAWING_CACHE_VALID; location[CHILD_LEFT_INDEX] = left; diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 9e0289a..4e46397 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -177,13 +177,14 @@ public final class ViewRoot extends Handler implements ViewParent, boolean mUseGL; boolean mGlWanted; + final ViewConfiguration mViewConfiguration; + /** * see {@link #playSoundEffect(int)} */ AudioManager mAudioManager; - public ViewRoot(Context context) { super(); @@ -224,6 +225,7 @@ public final class ViewRoot extends Handler implements ViewParent, mSurface = new Surface(); mAdded = false; mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this); + mViewConfiguration = ViewConfiguration.get(context); } @Override @@ -1101,6 +1103,7 @@ public final class ViewRoot extends Handler implements ViewParent, mAttachInfo.mDrawingTime = SystemClock.uptimeMillis(); canvas.translate(0, -yoff); + mView.mPrivateFlags |= View.DRAWN; mView.draw(canvas); canvas.translate(0, yoff); @@ -1139,6 +1142,8 @@ public final class ViewRoot extends Handler implements ViewParent, Canvas canvas; try { canvas = surface.lockCanvas(dirty); + // TODO: Do this in native + canvas.setDensityScale(mView.getResources().getDisplayMetrics().density); } catch (Surface.OutOfResourcesException e) { Log.e("ViewRoot", "OutOfResourcesException locking surface", e); // TODO: we should ask the window manager to do something! @@ -1175,6 +1180,7 @@ public final class ViewRoot extends Handler implements ViewParent, dirty.setEmpty(); mAttachInfo.mDrawingTime = SystemClock.uptimeMillis(); canvas.translate(0, -yoff); + mView.mPrivateFlags |= View.DRAWN; mView.draw(canvas); canvas.translate(0, yoff); @@ -1197,6 +1203,23 @@ public final class ViewRoot extends Handler implements ViewParent, if (LOCAL_LOGV) { Log.v("ViewRoot", "Surface " + surface + " unlockCanvasAndPost"); } + + } else if (mWidth == 0 || mHeight == 0) { + // This is a special case where a window dimension is 0 -- we + // normally wouldn't draw anything because we have an empty + // dirty rect, but the surface flinger may be waiting for us to + // draw the window before it stops freezing the screen, so we + // need to diddle it like this to keep it from getting stuck. + Canvas canvas; + try { + canvas = surface.lockCanvas(dirty); + } catch (Surface.OutOfResourcesException e) { + Log.e("ViewRoot", "OutOfResourcesException locking surface", e); + // TODO: we should ask the window manager to do something! + // for now we just do nothing + return; + } + surface.unlockCanvasAndPost(canvas); } if (scrolling) { @@ -1414,6 +1437,7 @@ public final class ViewRoot extends Handler implements ViewParent, public final static int FINISHED_EVENT = 1010; public final static int DISPATCH_KEY_FROM_IME = 1011; public final static int FINISH_INPUT_CONNECTION = 1012; + public final static int CHECK_FOCUS = 1013; @Override public void handleMessage(Message msg) { @@ -1422,11 +1446,9 @@ public final class ViewRoot extends Handler implements ViewParent, ((View) msg.obj).invalidate(); break; case View.AttachInfo.INVALIDATE_RECT_MSG: - int left = msg.arg1 >>> 16; - int top = msg.arg1 & 0xFFFF; - int right = msg.arg2 >>> 16; - int bottom = msg.arg2 & 0xFFFF; - ((View) msg.obj).invalidate(left, top, right, bottom); + final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj; + info.target.invalidate(info.left, info.top, info.right, info.bottom); + info.release(); break; case DO_TRAVERSAL: if (mProfile) { @@ -1478,7 +1500,7 @@ public final class ViewRoot extends Handler implements ViewParent, event.offsetLocation(0, mCurScrollY); handled = mView.dispatchTouchEvent(event); if (!handled && isDown) { - int edgeSlop = ViewConfiguration.getEdgeSlop(); + int edgeSlop = mViewConfiguration.getScaledEdgeSlop(); final int edgeFlags = event.getEdgeFlags(); int direction = View.FOCUS_UP; @@ -1615,7 +1637,7 @@ public final class ViewRoot extends Handler implements ViewParent, dispatchDetachedFromWindow(); break; case DISPATCH_KEY_FROM_IME: - if (LOCAL_LOGV) Log.v( + if (true) Log.v( "ViewRoot", "Dispatching key " + msg.obj + " from IME to " + mView); deliverKeyEventToViewHierarchy((KeyEvent)msg.obj, false); @@ -1626,6 +1648,12 @@ public final class ViewRoot extends Handler implements ViewParent, imm.reportFinishInputConnection((InputConnection)msg.obj); } } break; + case CHECK_FOCUS: { + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + imm.checkFocus((View)msg.obj); + } + } break; } } @@ -2042,8 +2070,10 @@ public final class ViewRoot extends Handler implements ViewParent, } private void deliverKeyEvent(KeyEvent event, boolean sendDone) { - boolean handled = false; - handled = mView.dispatchKeyEventPreIme(event); + // If mView is null, we just consume the key event because it doesn't + // make sense to do anything else with it. + boolean handled = mView != null + ? mView.dispatchKeyEventPreIme(event) : true; if (handled) { if (sendDone) { if (LOCAL_LOGV) Log.v( @@ -2061,7 +2091,7 @@ public final class ViewRoot extends Handler implements ViewParent, if (WindowManager.LayoutParams.mayUseInputMethod( mWindowAttributes.flags)) { InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null && mView != null && imm.isActive()) { + if (imm != null && mView != null) { int seq = enqueuePendingEvent(event, sendDone); if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq=" + seq + " event=" + event); @@ -2748,7 +2778,6 @@ public final class ViewRoot extends Handler implements ViewParent, synchronized (mActions) { final ArrayList<HandlerAction> actions = mActions; - final int count = actions.size(); while (actions.remove(handlerAction)) { // Keep going @@ -2776,7 +2805,20 @@ public final class ViewRoot extends Handler implements ViewParent, @Override public boolean equals(Object o) { - return action.equals(o); + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + HandlerAction that = (HandlerAction) o; + + return !(action != null ? !action.equals(that.action) : that.action != null); + + } + + @Override + public int hashCode() { + int result = action != null ? action.hashCode() : 0; + result = 31 * result + (int) (delay ^ (delay >>> 32)); + return result; } } } diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java index f4d0fde..a573983 100644 --- a/core/java/android/view/VolumePanel.java +++ b/core/java/android/view/VolumePanel.java @@ -16,18 +16,17 @@ package android.view; -import android.media.ToneGenerator; -import android.media.AudioManager; -import android.media.AudioService; -import android.media.AudioSystem; +import android.bluetooth.HeadsetBase; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.res.Resources; +import android.media.AudioManager; +import android.media.AudioService; +import android.media.AudioSystem; +import android.media.ToneGenerator; import android.os.Handler; import android.os.Message; import android.os.Vibrator; -import android.text.TextUtils; import android.util.Config; import android.util.Log; import android.widget.ImageView; @@ -39,7 +38,7 @@ import android.widget.Toast; * Handle the volume up and down keys. * * This code really should be moved elsewhere. - * + * * @hide */ public class VolumePanel extends Handler @@ -54,7 +53,7 @@ public class VolumePanel extends Handler * PhoneWindow will implement this part. */ public static final int PLAY_SOUND_DELAY = 300; - + /** * The delay before vibrating. This small period exists so if the user is * moving to silent mode, it will not emit a short vibrate (it normally @@ -64,28 +63,30 @@ public class VolumePanel extends Handler public static final int VIBRATE_DELAY = 300; private static final int VIBRATE_DURATION = 300; - private static final int BEEP_DURATION = 150; + private static final int BEEP_DURATION = 150; private static final int MAX_VOLUME = 100; private static final int FREE_DELAY = 10000; - + private static final int MSG_VOLUME_CHANGED = 0; private static final int MSG_FREE_RESOURCES = 1; private static final int MSG_PLAY_SOUND = 2; private static final int MSG_STOP_SOUNDS = 3; private static final int MSG_VIBRATE = 4; - + private static final int RINGTONE_VOLUME_TEXT = com.android.internal.R.string.volume_ringtone; private static final int MUSIC_VOLUME_TEXT = com.android.internal.R.string.volume_music; private static final int INCALL_VOLUME_TEXT = com.android.internal.R.string.volume_call; private static final int ALARM_VOLUME_TEXT = com.android.internal.R.string.volume_alarm; private static final int UNKNOWN_VOLUME_TEXT = com.android.internal.R.string.volume_unknown; private static final int NOTIFICATION_VOLUME_TEXT = - com.android.internal.R.string.volume_notification; - + com.android.internal.R.string.volume_notification; + private static final int BLUETOOTH_INCALL_VOLUME_TEXT = + com.android.internal.R.string.volume_bluetooth_call; + protected Context mContext; private AudioManager mAudioManager; protected AudioService mAudioService; - + private final Toast mToast; private final View mView; private final TextView mMessage; @@ -117,13 +118,13 @@ public class VolumePanel extends Handler mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()]; mVibrator = new Vibrator(); } - + public void postVolumeChanged(int streamType, int flags) { if (hasMessages(MSG_VOLUME_CHANGED)) return; removeMessages(MSG_FREE_RESOURCES); obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget(); } - + /** * Override this if you have other work to do when the volume changes (for * example, vibrating, playing a sound, etc.). Make sure to call through to @@ -132,31 +133,31 @@ public class VolumePanel extends Handler protected void onVolumeChanged(int streamType, int flags) { if (LOGD) Log.d(TAG, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")"); - + if ((flags & AudioManager.FLAG_SHOW_UI) != 0) { onShowVolumeChanged(streamType, flags); } - + if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0) { removeMessages(MSG_PLAY_SOUND); sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY); } - + if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) { removeMessages(MSG_PLAY_SOUND); removeMessages(MSG_VIBRATE); onStopSounds(); } - + removeMessages(MSG_FREE_RESOURCES); - sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY); + sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY); } protected void onShowVolumeChanged(int streamType, int flags) { int index = mAudioService.getStreamVolume(streamType); int message = UNKNOWN_VOLUME_TEXT; int additionalMessage = 0; - + if (LOGD) { Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType + ", flags: " + flags + "), index: " + index); @@ -166,13 +167,13 @@ public class VolumePanel extends Handler int max = mAudioService.getStreamMaxVolume(streamType); switch (streamType) { - + case AudioManager.STREAM_RING: { message = RINGTONE_VOLUME_TEXT; setRingerIcon(index); break; } - + case AudioManager.STREAM_MUSIC: { message = MUSIC_VOLUME_TEXT; if (mAudioManager.isBluetoothA2dpOn()) { @@ -184,7 +185,7 @@ public class VolumePanel extends Handler } break; } - + case AudioManager.STREAM_VOICE_CALL: { /* * For in-call voice call volume, there is no inaudible volume. @@ -194,13 +195,7 @@ public class VolumePanel extends Handler index++; max++; message = INCALL_VOLUME_TEXT; - if (mAudioManager.isBluetoothScoOn()) { - additionalMessage = - com.android.internal.R.string.volume_call_hint_playing_through_bluetooth; - setLargeIcon(com.android.internal.R.drawable.ic_volume_bluetooth_in_call); - } else { - setSmallIcon(index); - } + setSmallIcon(index); break; } @@ -209,12 +204,25 @@ public class VolumePanel extends Handler setSmallIcon(index); break; } - + case AudioManager.STREAM_NOTIFICATION: { message = NOTIFICATION_VOLUME_TEXT; setSmallIcon(index); break; } + + case AudioManager.STREAM_BLUETOOTH_SCO: { + /* + * For in-call voice call volume, there is no inaudible volume. + * Rescale the UI control so the progress bar doesn't go all + * the way to zero and don't show the mute icon. + */ + index++; + max++; + message = BLUETOOTH_INCALL_VOLUME_TEXT; + setLargeIcon(com.android.internal.R.drawable.ic_volume_bluetooth_in_call); + break; + } } String messageString = Resources.getSystem().getString(message); @@ -228,25 +236,25 @@ public class VolumePanel extends Handler mAdditionalMessage.setVisibility(View.VISIBLE); mAdditionalMessage.setText(Resources.getSystem().getString(additionalMessage)); } - + if (max != mLevel.getMax()) { mLevel.setMax(max); } mLevel.setProgress(index); - + mToast.setView(mView); mToast.setDuration(Toast.LENGTH_SHORT); mToast.setGravity(Gravity.TOP, 0, 0); mToast.show(); - + // Do a little vibrate if applicable (only when going into vibrate mode) - if ((flags & AudioManager.FLAG_VIBRATE) != 0 && + if ((flags & AudioManager.FLAG_VIBRATE) != 0 && mAudioService.isStreamAffectedByRingerMode(streamType) && mAudioService.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE && mAudioService.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)) { sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY); } - + } protected void onPlaySound(int streamType, int flags) { @@ -256,7 +264,7 @@ public class VolumePanel extends Handler // Force stop right now onStopSounds(); } - + synchronized (this) { ToneGenerator toneGen = getOrCreateToneGenerator(streamType); toneGen.startTone(ToneGenerator.TONE_PROP_BEEP); @@ -266,7 +274,7 @@ public class VolumePanel extends Handler } protected void onStopSounds() { - + synchronized (this) { int numStreamTypes = AudioSystem.getNumStreamTypes(); for (int i = numStreamTypes - 1; i >= 0; i--) { @@ -277,17 +285,17 @@ public class VolumePanel extends Handler } } } - + protected void onVibrate() { - + // Make sure we ended up in vibrate ringer mode if (mAudioService.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) { return; } - + mVibrator.vibrate(VIBRATE_DURATION); } - + /** * Lock on this VolumePanel instance as long as you use the returned ToneGenerator. */ @@ -303,13 +311,13 @@ public class VolumePanel extends Handler /** * Makes the small icon visible, and hides the large icon. - * + * * @param index The volume index, where 0 means muted. */ private void setSmallIcon(int index) { mLargeStreamIcon.setVisibility(View.GONE); mSmallStreamIcon.setVisibility(View.VISIBLE); - + mSmallStreamIcon.setImageResource(index == 0 ? com.android.internal.R.drawable.ic_volume_off_small : com.android.internal.R.drawable.ic_volume_small); @@ -317,7 +325,7 @@ public class VolumePanel extends Handler /** * Makes the large image view visible with the given icon. - * + * * @param resId The icon to display. */ private void setLargeIcon(int resId) { @@ -329,7 +337,7 @@ public class VolumePanel extends Handler /** * Makes the ringer icon visible with an icon that is chosen * based on the current ringer mode. - * + * * @param index */ private void setRingerIcon(int index) { @@ -350,13 +358,13 @@ public class VolumePanel extends Handler } mLargeStreamIcon.setImageResource(icon); } - + protected void onFreeResources() { // We'll keep the views, just ditch the cached drawable and hence // bitmaps mSmallStreamIcon.setImageDrawable(null); mLargeStreamIcon.setImageDrawable(null); - + synchronized (this) { for (int i = mToneGenerators.length - 1; i >= 0; i--) { if (mToneGenerators[i] != null) { @@ -366,26 +374,26 @@ public class VolumePanel extends Handler } } } - + @Override public void handleMessage(Message msg) { switch (msg.what) { - + case MSG_VOLUME_CHANGED: { onVolumeChanged(msg.arg1, msg.arg2); break; } - + case MSG_FREE_RESOURCES: { onFreeResources(); break; } - + case MSG_STOP_SOUNDS: { onStopSounds(); break; } - + case MSG_PLAY_SOUND: { onPlaySound(msg.arg1, msg.arg2); break; @@ -395,8 +403,8 @@ public class VolumePanel extends Handler onVibrate(); break; } - + } } - + } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index a68436b..428de67 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -525,6 +525,21 @@ public abstract class Window { } /** + * Specify custom animations to use for the window, as per + * {@link WindowManager.LayoutParams#windowAnimations + * WindowManager.LayoutParams.windowAnimations}. Providing anything besides + * 0 here will override the animations the window would + * normally retrieve from its theme. + */ + public void setWindowAnimations(int resId) { + final WindowManager.LayoutParams attrs = getAttributes(); + attrs.windowAnimations = resId; + if (mCallback != null) { + mCallback.onWindowAttributesChanged(attrs); + } + } + + /** * Specify an explicit soft input mode to use for the window, as per * {@link WindowManager.LayoutParams#softInputMode * WindowManager.LayoutParams.softInputMode}. Providing anything besides diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 7e47ad1..d08a6fa 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -16,6 +16,7 @@ package android.view; +import android.content.pm.ActivityInfo; import android.graphics.PixelFormat; import android.os.IBinder; import android.os.Parcel; @@ -126,8 +127,6 @@ public interface WindowManager extends ViewManager { * @see #TYPE_APPLICATION_MEDIA * @see #TYPE_APPLICATION_SUB_PANEL * @see #TYPE_APPLICATION_ATTACHED_DIALOG - * @see #TYPE_INPUT_METHOD - * @see #TYPE_INPUT_METHOD_DIALOG * @see #TYPE_STATUS_BAR * @see #TYPE_SEARCH_BAR * @see #TYPE_PHONE @@ -645,6 +644,17 @@ public interface WindowManager extends ViewManager { */ public String packageName = null; + /** + * Specific orientation value for a window. + * May be any of the same values allowed + * for {@link android.content.pm.ActivityInfo#screenOrientation}. + * If not set, a default value of + * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} + * will be used. + */ + public int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + + public LayoutParams() { super(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); type = TYPE_APPLICATION; @@ -722,6 +732,7 @@ public interface WindowManager extends ViewManager { out.writeStrongBinder(token); out.writeString(packageName); TextUtils.writeToParcel(mTitle, out, parcelableFlags); + out.writeInt(screenOrientation); } public static final Parcelable.Creator<LayoutParams> CREATOR @@ -755,6 +766,7 @@ public interface WindowManager extends ViewManager { token = in.readStrongBinder(); packageName = in.readString(); mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + screenOrientation = in.readInt(); } public static final int LAYOUT_CHANGED = 1<<0; @@ -767,6 +779,7 @@ public interface WindowManager extends ViewManager { public static final int ALPHA_CHANGED = 1<<7; public static final int MEMORY_TYPE_CHANGED = 1<<8; public static final int SOFT_INPUT_MODE_CHANGED = 1<<9; + public static final int SCREEN_ORIENTATION_CHANGED = 1<<10; public final int copyFrom(LayoutParams o) { int changes = 0; @@ -862,6 +875,10 @@ public interface WindowManager extends ViewManager { changes |= DIM_AMOUNT_CHANGED; } + if (screenOrientation != o.screenOrientation) { + screenOrientation = o.screenOrientation; + changes |= SCREEN_ORIENTATION_CHANGED; + } return changes; } @@ -907,6 +924,10 @@ public interface WindowManager extends ViewManager { sb.append(" wanim=0x"); sb.append(Integer.toHexString(windowAnimations)); } + if (screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { + sb.append("or="); + sb.append(screenOrientation); + } sb.append('}'); return sb.toString(); } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 6af4915..542b35f 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -289,16 +289,18 @@ public interface WindowManagerPolicy { * Can be called by the policy to force a window to be hidden, * regardless of whether the client or window manager would like * it shown. Must be called with the window manager lock held. + * Returns true if {@link #showLw} was last called for the window. */ - public void hideLw(boolean doAnimation); + public boolean hideLw(boolean doAnimation); /** * Can be called to undo the effect of {@link #hideLw}, allowing a * window to be shown as long as the window manager and client would * also like it to be shown. Must be called with the window manager * lock held. + * Returns true if {@link #hideLw} was last called for the window. */ - public void showLw(boolean doAnimation); + public boolean showLw(boolean doAnimation); } /** No transition happening. */ @@ -735,10 +737,17 @@ public interface WindowManagerPolicy { * ActivityInfo.SCREEN_ORIENTATION_PORTRAIT}), return a surface * rotation. */ - public int rotationForOrientation(int orientation); + public int rotationForOrientation(int orientation, int lastRotation, + boolean displayEnabled); /** - * Called when the system is mostly done booting + * Called when the system is mostly done booting to dentermine whether + * the system should go into safe mode. + */ + public boolean detectSafeMode(); + + /** + * Called when the system is mostly done booting. */ public void systemReady(); diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java index 9264398..c96b3e5 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -20,13 +20,14 @@ import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.util.TypedValue; +import android.graphics.RectF; /** * Abstraction for an Animation that can be applied to Views, Surfaces, or * other objects. See the {@link android.view.animation animation package * description file}. */ -public abstract class Animation { +public abstract class Animation implements Cloneable { /** * Repeat the animation indefinitely. */ @@ -174,8 +175,12 @@ public abstract class Animation { */ private int mZAdjustment; - // Indicates what was the last value returned by getTransformation() private boolean mMore = true; + private boolean mOneMoreTime = true; + + RectF mPreviousRegion = new RectF(); + Transformation mTransformation = new Transformation(); + Transformation mPreviousTransformation = new Transformation(); /** * Creates a new animation with a duration of 0ms, the default interpolator, with @@ -217,16 +222,28 @@ public abstract class Animation { a.recycle(); } + @Override + protected Animation clone() throws CloneNotSupportedException { + final Animation animation = (Animation) super.clone(); + animation.mPreviousRegion = new RectF(); + animation.mTransformation = new Transformation(); + animation.mPreviousTransformation = new Transformation(); + return animation; + } + /** * Reset the initialization state of this animation. * * @see #initialize(int, int, int, int) */ public void reset() { + mPreviousRegion.setEmpty(); + mPreviousTransformation.clear(); mInitialized = false; mCycleFlip = false; mRepeated = 0; mMore = true; + mOneMoreTime = true; } /** @@ -255,10 +272,8 @@ public abstract class Animation { * @param parentHeight Height of the animated object's parent */ public void initialize(int width, int height, int parentWidth, int parentHeight) { + reset(); mInitialized = true; - mCycleFlip = false; - mRepeated = 0; - mMore = true; } /** @@ -707,6 +722,11 @@ public abstract class Animation { } } + if (!mMore && mOneMoreTime) { + mOneMoreTime = false; + return true; + } + return mMore; } @@ -765,7 +785,54 @@ public abstract class Animation { return value; } } - + + /** + * @param left + * @param top + * @param right + * @param bottom + * @param invalidate + * @param transformation + * + * @hide + */ + public void getInvalidateRegion(int left, int top, int right, int bottom, + RectF invalidate, Transformation transformation) { + + final RectF previousRegion = mPreviousRegion; + + invalidate.set(left, top, right, bottom); + transformation.getMatrix().mapRect(invalidate); + invalidate.union(previousRegion); + + previousRegion.set(left, top, right, bottom); + transformation.getMatrix().mapRect(previousRegion); + + final Transformation tempTransformation = mTransformation; + final Transformation previousTransformation = mPreviousTransformation; + + tempTransformation.set(transformation); + transformation.set(previousTransformation); + previousTransformation.set(tempTransformation); + } + + /** + * @param left + * @param top + * @param right + * @param bottom + * + * @hide + */ + public void initializeInvalidateRegion(int left, int top, int right, int bottom) { + final RectF region = mPreviousRegion; + region.set(left, top, right, bottom); + if (mFillBefore) { + final Transformation previousTransformation = mPreviousTransformation; + applyTransformation(0.0f, previousTransformation); + } + } + /** * Utility class to parse a string description of a size. */ diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java index 688da70..7b56f00 100644 --- a/core/java/android/view/animation/AnimationSet.java +++ b/core/java/android/view/animation/AnimationSet.java @@ -19,6 +19,7 @@ package android.view.animation; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; +import android.graphics.RectF; import java.util.ArrayList; import java.util.List; @@ -39,6 +40,7 @@ public class AnimationSet extends Animation { private static final int PROPERTY_SHARE_INTERPOLATOR_MASK = 0x10; private static final int PROPERTY_DURATION_MASK = 0x20; private static final int PROPERTY_MORPH_MATRIX_MASK = 0x40; + private static final int PROPERTY_CHANGE_BOUNDS_MASK = 0x80; private int mFlags = 0; @@ -82,6 +84,22 @@ public class AnimationSet extends Animation { init(); } + @Override + protected AnimationSet clone() throws CloneNotSupportedException { + final AnimationSet animation = (AnimationSet) super.clone(); + animation.mTempTransformation = new Transformation(); + animation.mAnimations = new ArrayList<Animation>(); + + final int count = mAnimations.size(); + final ArrayList<Animation> animations = mAnimations; + + for (int i = 0; i < count; i++) { + animation.mAnimations.add(animations.get(i).clone()); + } + + return animation; + } + private void setFlag(int mask, boolean value) { if (value) { mFlags |= mask; @@ -145,6 +163,11 @@ public class AnimationSet extends Animation { mFlags |= PROPERTY_MORPH_MATRIX_MASK; } + boolean changeBounds = (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == 0; + if (changeBounds && a.willChangeTransformationMatrix()) { + mFlags |= PROPERTY_CHANGE_BOUNDS_MASK; + } + if (mAnimations.size() == 1) { mDuration = a.getStartOffset() + a.getDuration(); mLastEnd = mStartOffset + mDuration; @@ -239,7 +262,54 @@ public class AnimationSet extends Animation { } return duration; } - + + /** + * @hide + */ + public void getInvalidateRegion(int left, int top, int right, int bottom, + RectF invalidate, Transformation transformation) { + + final RectF previousRegion = mPreviousRegion; + + invalidate.set(left, top, right, bottom); + transformation.getMatrix().mapRect(invalidate); + invalidate.union(previousRegion); + + previousRegion.set(left, top, right, bottom); + transformation.getMatrix().mapRect(previousRegion); + + final Transformation tempTransformation = mTransformation; + final Transformation previousTransformation = mPreviousTransformation; + + tempTransformation.set(transformation); + transformation.set(previousTransformation); + previousTransformation.set(tempTransformation); + } + + /** + * @hide + */ + public void initializeInvalidateRegion(int left, int top, int right, int bottom) { + final RectF region = mPreviousRegion; + region.set(left, top, right, bottom); + + if (mFillBefore) { + final int count = mAnimations.size(); + final ArrayList<Animation> animations = mAnimations; + final Transformation temp = mTempTransformation; + + final Transformation previousTransformation = mPreviousTransformation; + + for (int i = count - 1; i >= 0; --i) { + final Animation a = animations.get(i); + + temp.clear(); + a.applyTransformation(0.0f, temp); + previousTransformation.compose(temp); + } + } + } + /** * The transformation of an animation set is the concatenation of all of its * component animations. @@ -313,7 +383,7 @@ public class AnimationSet extends Animation { == PROPERTY_SHARE_INTERPOLATOR_MASK; boolean startOffsetSet = (mFlags & PROPERTY_START_OFFSET_MASK) == PROPERTY_START_OFFSET_MASK; - + if (shareInterpolator) { ensureInterpolator(); } @@ -327,7 +397,13 @@ public class AnimationSet extends Animation { final int repeatMode = mRepeatMode; final Interpolator interpolator = mInterpolator; final long startOffset = mStartOffset; - + + + long[] storedOffsets = mStoredOffsets; + if (storedOffsets == null || storedOffsets.length != count) { + storedOffsets = mStoredOffsets = new long[count]; + } + for (int i = 0; i < count; i++) { Animation a = children.get(i); if (durationSet) { @@ -346,42 +422,36 @@ public class AnimationSet extends Animation { a.setInterpolator(interpolator); } if (startOffsetSet) { - a.setStartOffset(startOffset); + long offset = a.getStartOffset(); + a.setStartOffset(offset + startOffset); + storedOffsets[i] = offset; } a.initialize(width, height, parentWidth, parentHeight); } } - /** - * @hide - * @param startOffset the startOffset to add to the children's startOffset - */ - void saveChildrenStartOffset(long startOffset) { - final ArrayList<Animation> children = mAnimations; - final int count = children.size(); - long[] storedOffsets = mStoredOffsets = new long[count]; - - for (int i = 0; i < count; i++) { - Animation animation = children.get(i); - long offset = animation.getStartOffset(); - animation.setStartOffset(offset + startOffset); - storedOffsets[i] = offset; - } + @Override + public void reset() { + super.reset(); + restoreChildrenStartOffset(); } /** * @hide */ void restoreChildrenStartOffset() { + final long[] offsets = mStoredOffsets; + if (offsets == null) return; + final ArrayList<Animation> children = mAnimations; final int count = children.size(); - final long[] offsets = mStoredOffsets; + for (int i = 0; i < count; i++) { children.get(i).setStartOffset(offsets[i]); } } - + /** * @return All the child animations in this AnimationSet. Note that * this may include other AnimationSets, which are not expanded. @@ -394,4 +464,9 @@ public class AnimationSet extends Animation { public boolean willChangeTransformationMatrix() { return (mFlags & PROPERTY_MORPH_MATRIX_MASK) == PROPERTY_MORPH_MATRIX_MASK; } + + @Override + public boolean willChangeBounds() { + return (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == PROPERTY_CHANGE_BOUNDS_MASK; + } } diff --git a/core/java/android/view/animation/LayoutAnimationController.java b/core/java/android/view/animation/LayoutAnimationController.java index 9cfa8d7..882e738 100644 --- a/core/java/android/view/animation/LayoutAnimationController.java +++ b/core/java/android/view/animation/LayoutAnimationController.java @@ -318,9 +318,16 @@ public class LayoutAnimationController { * @see #getDelayForView(android.view.View) */ public final Animation getAnimationForView(View view) { - final long delay = getDelayForView(view); + final long delay = getDelayForView(view) + mAnimation.getStartOffset(); mMaxDelay = Math.max(mMaxDelay, delay); - return new DelayedAnimation(delay, mAnimation); + + try { + final Animation animation = mAnimation.clone(); + animation.setStartOffset(delay); + return animation; + } catch (CloneNotSupportedException e) { + return null; + } } /** @@ -425,149 +432,4 @@ public class LayoutAnimationController { */ public int index; } - - /** - * Encapsulates an animation and delays its start offset by a specified - * amount. This allows to reuse the same base animation for various views - * and get the effect of running multiple instances of the animation at - * different times. - */ - private static class DelayedAnimation extends Animation { - private final long mDelay; - private final Animation mAnimation; - - /** - * Creates a new delayed animation that will delay the controller's - * animation by the specified delay in milliseconds. - * - * @param delay the delay in milliseconds by which to offset the - * @param animation the animation to delay - */ - private DelayedAnimation(long delay, Animation animation) { - mDelay = delay; - mAnimation = animation; - } - - @Override - public boolean isInitialized() { - return mAnimation.isInitialized(); - } - - @Override - public void initialize(int width, int height, int parentWidth, int parentHeight) { - mAnimation.initialize(width, height, parentWidth, parentHeight); - } - - @Override - public void reset() { - mAnimation.reset(); - } - - @Override - public boolean getTransformation(long currentTime, Transformation outTransformation) { - final long oldOffset = mAnimation.getStartOffset(); - final boolean isSet = mAnimation instanceof AnimationSet; - if (isSet) { - AnimationSet set = ((AnimationSet) mAnimation); - set.saveChildrenStartOffset(mDelay); - } - mAnimation.setStartOffset(oldOffset + mDelay); - - boolean result = mAnimation.getTransformation(currentTime, - outTransformation); - - if (isSet) { - AnimationSet set = ((AnimationSet) mAnimation); - set.restoreChildrenStartOffset(); - } - mAnimation.setStartOffset(oldOffset); - - return result; - } - - @Override - public void setStartTime(long startTimeMillis) { - mAnimation.setStartTime(startTimeMillis); - } - - @Override - public long getStartTime() { - return mAnimation.getStartTime(); - } - - @Override - public void setInterpolator(Interpolator i) { - mAnimation.setInterpolator(i); - } - - @Override - public void setStartOffset(long startOffset) { - mAnimation.setStartOffset(startOffset); - } - - @Override - public void setDuration(long durationMillis) { - mAnimation.setDuration(durationMillis); - } - - @Override - public void scaleCurrentDuration(float scale) { - mAnimation.scaleCurrentDuration(scale); - } - - @Override - public void setRepeatMode(int repeatMode) { - mAnimation.setRepeatMode(repeatMode); - } - - @Override - public void setFillBefore(boolean fillBefore) { - mAnimation.setFillBefore(fillBefore); - } - - @Override - public void setFillAfter(boolean fillAfter) { - mAnimation.setFillAfter(fillAfter); - } - - @Override - public Interpolator getInterpolator() { - return mAnimation.getInterpolator(); - } - - @Override - public long getDuration() { - return mAnimation.getDuration(); - } - - @Override - public long getStartOffset() { - return mAnimation.getStartOffset() + mDelay; - } - - @Override - public int getRepeatMode() { - return mAnimation.getRepeatMode(); - } - - @Override - public boolean getFillBefore() { - return mAnimation.getFillBefore(); - } - - @Override - public boolean getFillAfter() { - return mAnimation.getFillAfter(); - } - - @Override - public boolean willChangeTransformationMatrix() { - return mAnimation.willChangeTransformationMatrix(); - } - - @Override - public boolean willChangeBounds() { - return mAnimation.willChangeBounds(); - } - } } diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index a6ce293..56c6c92 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -17,35 +17,365 @@ package android.view.inputmethod; import android.content.Context; +import android.content.res.TypedArray; +import android.os.Bundle; import android.os.Handler; -import android.os.Message; +import android.os.SystemClock; +import android.text.Editable; +import android.text.NoCopySpan; +import android.text.Selection; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.method.MetaKeyKeyListener; +import android.util.Log; +import android.util.LogPrinter; +import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.View; import android.view.ViewRoot; +class ComposingText implements NoCopySpan { +} + /** * Base class for implementors of the InputConnection interface, taking care - * of implementing common system-oriented parts of the functionality. + * of most of the common behavior for providing a connection to an Editable. + * Implementors of this class will want to be sure to implement + * {@link #getEditable} to provide access to their own editable object. */ -public abstract class BaseInputConnection implements InputConnection { +public class BaseInputConnection implements InputConnection { + private static final boolean DEBUG = false; + private static final String TAG = "BaseInputConnection"; + static final Object COMPOSING = new ComposingText(); + final InputMethodManager mIMM; final Handler mH; final View mTargetView; + final boolean mDummyMode; + + private Object[] mDefaultComposingSpans; + + Editable mEditable; + KeyCharacterMap mKeyCharacterMap; - BaseInputConnection(InputMethodManager mgr) { + BaseInputConnection(InputMethodManager mgr, boolean dummyMode) { mIMM = mgr; mTargetView = null; mH = null; + mDummyMode = dummyMode; } - public BaseInputConnection(View targetView) { + public BaseInputConnection(View targetView, boolean dummyMode) { mIMM = (InputMethodManager)targetView.getContext().getSystemService( Context.INPUT_METHOD_SERVICE); mH = targetView.getHandler(); mTargetView = targetView; + mDummyMode = dummyMode; + } + + public static final void removeComposingSpans(Spannable text) { + text.removeSpan(COMPOSING); + Object[] sps = text.getSpans(0, text.length(), Object.class); + if (sps != null) { + for (int i=sps.length-1; i>=0; i--) { + Object o = sps[i]; + if ((text.getSpanFlags(o)&Spanned.SPAN_COMPOSING) != 0) { + text.removeSpan(o); + } + } + } + } + + public static void setComposingSpans(Spannable text) { + final Object[] sps = text.getSpans(0, text.length(), Object.class); + if (sps != null) { + for (int i=sps.length-1; i>=0; i--) { + final Object o = sps[i]; + if (o == COMPOSING) { + text.removeSpan(o); + continue; + } + final int fl = text.getSpanFlags(o); + if ((fl&(Spanned.SPAN_COMPOSING|Spanned.SPAN_POINT_MARK_MASK)) + != (Spanned.SPAN_COMPOSING|Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)) { + text.setSpan(o, text.getSpanStart(o), text.getSpanEnd(o), + (fl&Spanned.SPAN_POINT_MARK_MASK) + | Spanned.SPAN_COMPOSING + | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + } + + text.setSpan(COMPOSING, 0, text.length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING); + } + + public static int getComposingSpanStart(Spannable text) { + return text.getSpanStart(COMPOSING); + } + + public static int getComposingSpanEnd(Spannable text) { + return text.getSpanEnd(COMPOSING); + } + + /** + * Return the target of edit operations. The default implementation + * returns its own fake editable that is just used for composing text; + * subclasses that are real text editors should override this and + * supply their own. + */ + public Editable getEditable() { + if (mEditable == null) { + mEditable = Editable.Factory.getInstance().newEditable(""); + Selection.setSelection(mEditable, 0); + } + return mEditable; } /** + * Default implementation does nothing. + */ + public boolean beginBatchEdit() { + return false; + } + + /** + * Default implementation does nothing. + */ + public boolean endBatchEdit() { + return false; + } + + /** + * Default implementation uses + * {@link MetaKeyKeyListener#clearMetaKeyState(long, int) + * MetaKeyKeyListener.clearMetaKeyState(long, int)} to clear the state. + */ + public boolean clearMetaKeyStates(int states) { + final Editable content = getEditable(); + if (content == null) return false; + MetaKeyKeyListener.clearMetaKeyState(content, states); + return true; + } + + /** + * Default implementation does nothing. + */ + public boolean commitCompletion(CompletionInfo text) { + return false; + } + + /** + * Default implementation replaces any existing composing text with + * the given text. In addition, only if dummy mode, a key event is + * sent for the new text and the current editable buffer cleared. + */ + public boolean commitText(CharSequence text, int newCursorPosition) { + if (DEBUG) Log.v(TAG, "commitText " + text); + replaceText(text, newCursorPosition, false); + sendCurrentText(); + return true; + } + + /** + * The default implementation performs the deletion around the current + * selection position of the editable text. + */ + public boolean deleteSurroundingText(int leftLength, int rightLength) { + if (DEBUG) Log.v(TAG, "deleteSurroundingText " + leftLength + + " / " + rightLength); + final Editable content = getEditable(); + if (content == null) return false; + + beginBatchEdit(); + + int a = Selection.getSelectionStart(content); + int b = Selection.getSelectionEnd(content); + + if (a > b) { + int tmp = a; + a = b; + b = tmp; + } + + // ignore the composing text. + int ca = getComposingSpanStart(content); + int cb = getComposingSpanEnd(content); + if (cb < ca) { + int tmp = ca; + ca = cb; + cb = tmp; + } + if (ca != -1 && cb != -1) { + if (ca < a) a = ca; + if (cb > b) b = cb; + } + + int deleted = 0; + + if (leftLength > 0) { + int start = a - leftLength; + if (start < 0) start = 0; + content.delete(start, a); + deleted = a - start; + } + + if (rightLength > 0) { + b = b - deleted; + + int end = b + rightLength; + if (end > content.length()) end = content.length(); + + content.delete(b, end); + } + + endBatchEdit(); + + return true; + } + + /** + * The default implementation removes the composing state from the + * current editable text. In addition, only if dummy mode, a key event is + * sent for the new text and the current editable buffer cleared. + */ + public boolean finishComposingText() { + if (DEBUG) Log.v(TAG, "finishComposingText"); + final Editable content = getEditable(); + if (content != null) { + beginBatchEdit(); + removeComposingSpans(content); + endBatchEdit(); + sendCurrentText(); + } + return true; + } + + /** + * The default implementation uses TextUtils.getCapsMode to get the + * cursor caps mode for the current selection position in the editable + * text, unless in dummy mode in which case 0 is always returned. + */ + public int getCursorCapsMode(int reqModes) { + if (mDummyMode) return 0; + + final Editable content = getEditable(); + if (content == null) return 0; + + int a = Selection.getSelectionStart(content); + int b = Selection.getSelectionEnd(content); + + if (a > b) { + int tmp = a; + a = b; + b = tmp; + } + + return TextUtils.getCapsMode(content, a, reqModes); + } + + /** + * The default implementation always returns null. + */ + public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { + return null; + } + + /** + * The default implementation returns the given amount of text from the + * current cursor position in the buffer. + */ + public CharSequence getTextBeforeCursor(int length, int flags) { + final Editable content = getEditable(); + if (content == null) return null; + + int a = Selection.getSelectionStart(content); + int b = Selection.getSelectionEnd(content); + + if (a > b) { + int tmp = a; + a = b; + b = tmp; + } + + if (length > a) { + length = a; + } + + if ((flags&GET_TEXT_WITH_STYLES) != 0) { + return content.subSequence(a - length, a); + } + return TextUtils.substring(content, a - length, a); + } + + /** + * The default implementation returns the given amount of text from the + * current cursor position in the buffer. + */ + public CharSequence getTextAfterCursor(int length, int flags) { + final Editable content = getEditable(); + if (content == null) return null; + + int a = Selection.getSelectionStart(content); + int b = Selection.getSelectionEnd(content); + + if (a > b) { + int tmp = a; + a = b; + b = tmp; + } + + if (b + length > content.length()) { + length = content.length() - b; + } + + + if ((flags&GET_TEXT_WITH_STYLES) != 0) { + return content.subSequence(b, b + length); + } + return TextUtils.substring(content, b, b + length); + } + + /** + * The default implementation does nothing. + */ + public boolean performContextMenuAction(int id) { + return false; + } + + /** + * The default implementation does nothing. + */ + public boolean performPrivateCommand(String action, Bundle data) { + return false; + } + + /** + * The default implementation places the given text into the editable, + * replacing any existing composing text. The new text is marked as + * in a composing state with the composing style. + */ + public boolean setComposingText(CharSequence text, int newCursorPosition) { + if (DEBUG) Log.v(TAG, "setComposingText " + text); + replaceText(text, newCursorPosition, true); + return true; + } + + /** + * The default implementation changes the selection position in the + * current editable text. + */ + public boolean setSelection(int start, int end) { + if (DEBUG) Log.v(TAG, "setSelection " + start + ", " + end); + final Editable content = getEditable(); + if (content == null) return false; + Selection.setSelection(content, start, end); + return true; + } + + /** * Provides standard implementation for sending a key event to the window * attached to the input connection's view. */ @@ -82,4 +412,144 @@ public abstract class BaseInputConnection implements InputConnection { mIMM.updateStatusIcon(resId, packageName); return true; } + + private void sendCurrentText() { + if (!mDummyMode) { + return; + } + + Editable content = getEditable(); + if (content != null) { + if (content.length() == 1) { + // If it's 1 character, we have a chance of being + // able to generate normal key events... + if (mKeyCharacterMap == null) { + mKeyCharacterMap = KeyCharacterMap.load( + KeyCharacterMap.BUILT_IN_KEYBOARD); + } + char[] chars = new char[1]; + content.getChars(0, 1, chars, 0); + KeyEvent[] events = mKeyCharacterMap.getEvents(chars); + if (events != null) { + for (int i=0; i<events.length; i++) { + if (DEBUG) Log.v(TAG, "Sending: " + events[i]); + sendKeyEvent(events[i]); + } + content.clear(); + return; + } + } + + // Otherwise, revert to the special key event containing + // the actual characters. + KeyEvent event = new KeyEvent(SystemClock.uptimeMillis(), + content.toString(), KeyCharacterMap.BUILT_IN_KEYBOARD, 0); + sendKeyEvent(event); + content.clear(); + } + } + + private void replaceText(CharSequence text, int newCursorPosition, + boolean composing) { + final Editable content = getEditable(); + if (content == null) { + return; + } + + beginBatchEdit(); + + // delete composing text set previously. + int a = getComposingSpanStart(content); + int b = getComposingSpanEnd(content); + + if (DEBUG) Log.v(TAG, "Composing span: " + a + " to " + b); + + if (b < a) { + int tmp = a; + a = b; + b = tmp; + } + + if (a != -1 && b != -1) { + removeComposingSpans(content); + } else { + a = Selection.getSelectionStart(content); + b = Selection.getSelectionEnd(content); + if (a >=0 && b>= 0 && a != b) { + if (b < a) { + int tmp = a; + a = b; + b = tmp; + } + } + } + + if (composing) { + Spannable sp = null; + if (!(text instanceof Spannable)) { + sp = new SpannableStringBuilder(text); + text = sp; + if (mDefaultComposingSpans == null) { + Context context; + if (mTargetView != null) { + context = mTargetView.getContext(); + } else if (mIMM.mServedView != null) { + context = mIMM.mServedView.getContext(); + } else { + context = null; + } + if (context != null) { + TypedArray ta = context.getTheme() + .obtainStyledAttributes(new int[] { + com.android.internal.R.attr.candidatesTextStyleSpans + }); + CharSequence style = ta.getText(0); + ta.recycle(); + if (style != null && style instanceof Spanned) { + mDefaultComposingSpans = ((Spanned)style).getSpans( + 0, style.length(), Object.class); + } + } + } + if (mDefaultComposingSpans != null) { + for (int i = 0; i < mDefaultComposingSpans.length; ++i) { + sp.setSpan(mDefaultComposingSpans[i], 0, sp.length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + } else { + sp = (Spannable)text; + } + setComposingSpans(sp); + } + + // Adjust newCursorPosition to be relative the start of the text. + newCursorPosition += a; + + if (DEBUG) Log.v(TAG, "Replacing from " + a + " to " + b + " with \"" + + text + "\", composing=" + composing + + ", type=" + text.getClass().getCanonicalName()); + + if (DEBUG) { + LogPrinter lp = new LogPrinter(Log.VERBOSE, TAG); + lp.println("Current text:"); + TextUtils.dumpSpans(content, lp, " "); + lp.println("Composing text:"); + TextUtils.dumpSpans(text, lp, " "); + } + + content.replace(a, b, text); + if (newCursorPosition < 0) newCursorPosition = 0; + if (newCursorPosition > content.length()) + newCursorPosition = content.length(); + Selection.setSelection(content, newCursorPosition); + + if (DEBUG) { + LogPrinter lp = new LogPrinter(Log.VERBOSE, TAG); + lp.println("Final text:"); + TextUtils.dumpSpans(content, lp, " "); + } + + endBatchEdit(); + } } diff --git a/core/java/android/view/inputmethod/DefaultInputMethod.java b/core/java/android/view/inputmethod/DefaultInputMethod.java deleted file mode 100644 index 073b01c..0000000 --- a/core/java/android/view/inputmethod/DefaultInputMethod.java +++ /dev/null @@ -1,239 +0,0 @@ -package android.view.inputmethod; - -import android.graphics.Rect; -import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; -import android.view.KeyEvent; -import android.view.MotionEvent; - -import com.android.internal.view.IInputContext; -import com.android.internal.view.IInputMethod; -import com.android.internal.view.IInputMethodCallback; -import com.android.internal.view.IInputMethodSession; -import com.android.internal.view.InputConnectionWrapper; - -/** - * This is the default input method that runs in the same context of the - * application that requests text input. It does nothing but returns false for - * any key events, so that all key events will be processed by the key listener - * of the focused text box. - * {@hide} - */ -public class DefaultInputMethod implements InputMethod, InputMethodSession { - private static IInputMethod sInstance = new SimpleInputMethod( - new DefaultInputMethod()); - - private static InputMethodInfo sProperty = new InputMethodInfo( - "android.text.inputmethod", DefaultInputMethod.class.getName(), - "Default", "android.text.inputmethod.defaultImeSettings"); - - private InputConnection mInputConnection; - - public static IInputMethod getInstance() { - return sInstance; - } - - public static InputMethodInfo getMetaInfo() { - return sProperty; - } - - public void bindInput(InputBinding binding) { - mInputConnection = binding.getConnection(); - } - - public void unbindInput() { - } - - public void createSession(SessionCallback callback) { - callback.sessionCreated(this); - } - - public void setSessionEnabled(InputMethodSession session, boolean enabled) { - } - - public void revokeSession(InputMethodSession session) { - } - - public void finishInput() { - mInputConnection.hideStatusIcon(); - } - - public void displayCompletions(CompletionInfo[] completions) { - } - - public void updateExtractedText(int token, ExtractedText text) { - } - - public void updateSelection(int oldSelStart, int oldSelEnd, - int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) { - } - - public void updateCursor(Rect newCursor) { - } - - public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback) { - callback.finishedEvent(seq, false); - } - - public void dispatchTrackballEvent(int seq, MotionEvent event, EventCallback callback) { - callback.finishedEvent(seq, false); - } - - public void restartInput(EditorInfo attribute) { - } - - public void attachToken(IBinder token) { - } - - public void startInput(EditorInfo attribute) { - mInputConnection - .showStatusIcon("android", com.android.internal.R.drawable.ime_qwerty); - } - - public void appPrivateCommand(String action, Bundle data) { - } - - public void hideSoftInput() { - } - - public void showSoftInput(int flags) { - } -} - -// ---------------------------------------------------------------------- - -class SimpleInputMethod extends IInputMethod.Stub { - final InputMethod mInputMethod; - - static class Session extends IInputMethodSession.Stub { - final InputMethodSession mSession; - - Session(InputMethodSession session) { - mSession = session; - } - - public void finishInput() { - mSession.finishInput(); - } - - public void updateSelection(int oldSelStart, int oldSelEnd, - int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) { - mSession.updateSelection(oldSelStart, oldSelEnd, - newSelStart, newSelEnd, candidatesStart, candidatesEnd); - } - - public void updateCursor(Rect newCursor) { - mSession.updateCursor(newCursor); - } - - static class InputMethodEventCallbackWrapper implements InputMethodSession.EventCallback { - final IInputMethodCallback mCb; - InputMethodEventCallbackWrapper(IInputMethodCallback cb) { - mCb = cb; - } - public void finishedEvent(int seq, boolean handled) { - try { - mCb.finishedEvent(seq, handled); - } catch (RemoteException e) { - } - } - } - - public void dispatchKeyEvent(int seq, KeyEvent event, IInputMethodCallback callback) { - mSession.dispatchKeyEvent(seq, event, - new InputMethodEventCallbackWrapper(callback)); - } - - public void dispatchTrackballEvent(int seq, MotionEvent event, IInputMethodCallback callback) { - mSession.dispatchTrackballEvent(seq, event, - new InputMethodEventCallbackWrapper(callback)); - } - - public void displayCompletions(CompletionInfo[] completions) { - mSession.displayCompletions(completions); - } - - public void updateExtractedText(int token, ExtractedText text) { - mSession.updateExtractedText(token, text); - } - - public void appPrivateCommand(String action, Bundle data) { - mSession.appPrivateCommand(action, data); - } - } - - public SimpleInputMethod(InputMethod inputMethod) { - mInputMethod = inputMethod; - } - - public InputMethod getInternalInputMethod() { - return mInputMethod; - } - - public void attachToken(IBinder token) { - mInputMethod.attachToken(token); - } - - public void bindInput(InputBinding binding) { - InputConnectionWrapper ic = new InputConnectionWrapper( - IInputContext.Stub.asInterface(binding.getConnectionToken())); - InputBinding nu = new InputBinding(ic, binding); - mInputMethod.bindInput(nu); - } - - public void unbindInput() { - mInputMethod.unbindInput(); - } - - public void restartInput(EditorInfo attribute) { - mInputMethod.restartInput(attribute); - } - - public void startInput(EditorInfo attribute) { - mInputMethod.startInput(attribute); - } - - static class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback { - final IInputMethodCallback mCb; - InputMethodSessionCallbackWrapper(IInputMethodCallback cb) { - mCb = cb; - } - - public void sessionCreated(InputMethodSession session) { - try { - mCb.sessionCreated(new Session(session)); - } catch (RemoteException e) { - } - } - } - - public void createSession(IInputMethodCallback callback) throws RemoteException { - mInputMethod.createSession(new InputMethodSessionCallbackWrapper(callback)); - } - - public void setSessionEnabled(IInputMethodSession session, boolean enabled) throws RemoteException { - try { - InputMethodSession ls = ((Session)session).mSession; - mInputMethod.setSessionEnabled(ls, enabled); - } catch (ClassCastException e) { - Log.w("SimpleInputMethod", "Incoming session not of correct type: " + session, e); - } - } - - public void revokeSession(IInputMethodSession session) throws RemoteException { - try { - InputMethodSession ls = ((Session)session).mSession; - mInputMethod.revokeSession(ls); - } catch (ClassCastException e) { - Log.w("SimpleInputMethod", "Incoming session not of correct type: " + session, e); - } - } - - public void showSoftInput(boolean blah) { - } - - public void hideSoftInput() { - } -} diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java index c050691..b2f26d7 100644 --- a/core/java/android/view/inputmethod/EditorInfo.java +++ b/core/java/android/view/inputmethod/EditorInfo.java @@ -5,6 +5,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.InputType; import android.text.TextUtils; +import android.util.Printer; /** * An EditorInfo describes several attributes of a text editing object @@ -21,7 +22,7 @@ public class EditorInfo implements InputType, Parcelable { * @see #TYPE_MASK_VARIATION * @see #TYPE_MASK_FLAGS */ - public int inputType = TYPE_CLASS_TEXT; + public int inputType = TYPE_NULL; /** * A string supplying additional information about the content type that @@ -71,6 +72,26 @@ public class EditorInfo implements InputType, Parcelable { public CharSequence label; /** + * Name of the package that owns this editor. + */ + public String packageName; + + /** + * Identifier for the editor's field. This is optional, and may be + * 0. By default it is filled in with the result of + * {@link android.view.View#getId() View.getId()} on the View that + * is being edited. + */ + public int fieldId; + + /** + * Additional name for the editor's field. This can supply additional + * name information for the field. By default it is null. The actual + * contents have no meaning. + */ + public String fieldName; + + /** * Any extra data to supply to the input method. This is for extended * communication with specific input methods; the name fields in the * bundle should be scoped (such as "com.mydomain.im.SOME_FIELD") so @@ -81,6 +102,24 @@ public class EditorInfo implements InputType, Parcelable { public Bundle extras; /** + * Write debug output of this object. + */ + public void dump(Printer pw, String prefix) { + pw.println(prefix + "inputType=0x" + Integer.toHexString(inputType) + + " privateContentType=" + privateContentType); + pw.println(prefix + "initialSelStart=" + initialSelStart + + " initialSelEnd=" + initialSelEnd + + " initialCapsMode=0x" + + Integer.toHexString(initialCapsMode)); + pw.println(prefix + "hintText=" + hintText + + " label=" + label); + pw.println(prefix + "packageName=" + packageName + + " fieldId=" + fieldId + + " fieldName=" + fieldName); + pw.println(prefix + "extras=" + extras); + } + + /** * Used to package this object into a {@link Parcel}. * * @param dest The {@link Parcel} to be written. @@ -94,6 +133,9 @@ public class EditorInfo implements InputType, Parcelable { dest.writeInt(initialCapsMode); TextUtils.writeToParcel(hintText, dest, flags); TextUtils.writeToParcel(label, dest, flags); + dest.writeString(packageName); + dest.writeInt(fieldId); + dest.writeString(fieldName); dest.writeBundle(extras); } @@ -110,6 +152,9 @@ public class EditorInfo implements InputType, Parcelable { res.initialCapsMode = source.readInt(); res.hintText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); res.label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); + res.packageName = source.readString(); + res.fieldId = source.readInt(); + res.fieldName = source.readString(); res.extras = source.readBundle(); return res; } diff --git a/core/java/android/view/inputmethod/ExtractedText.java b/core/java/android/view/inputmethod/ExtractedText.java index 0ca3c79..e5d3cae 100644 --- a/core/java/android/view/inputmethod/ExtractedText.java +++ b/core/java/android/view/inputmethod/ExtractedText.java @@ -19,6 +19,22 @@ public class ExtractedText implements Parcelable { public int startOffset; /** + * If the content is a report of a partial text change, this is the + * offset where the change starts and it runs until + * {@link #partialEndOffset}. If the content is the full text, this + * field is -1. + */ + public int partialStartOffset; + + /** + * If the content is a report of a partial text change, this is the offset + * where the change ends. Note that the actual text may be larger or + * smaller than the difference between this and {@link #partialEndOffset}, + * meaning a reduction or increase, respectively, in the total text. + */ + public int partialEndOffset; + + /** * The offset where the selection currently starts within the extracted * text. The real selection start position is at * <var>startOffset</var>+<var>selectionStart</var>. @@ -52,6 +68,8 @@ public class ExtractedText implements Parcelable { public void writeToParcel(Parcel dest, int flags) { TextUtils.writeToParcel(text, dest, flags); dest.writeInt(startOffset); + dest.writeInt(partialStartOffset); + dest.writeInt(partialEndOffset); dest.writeInt(selectionStart); dest.writeInt(selectionEnd); dest.writeInt(flags); @@ -65,6 +83,8 @@ public class ExtractedText implements Parcelable { ExtractedText res = new ExtractedText(); res.text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); res.startOffset = source.readInt(); + res.partialStartOffset = source.readInt(); + res.partialEndOffset = source.readInt(); res.selectionStart = source.readInt(); res.selectionEnd = source.readInt(); res.flags = source.readInt(); diff --git a/core/java/android/view/inputmethod/ExtractedTextRequest.java b/core/java/android/view/inputmethod/ExtractedTextRequest.java index d962329..e84b094 100644 --- a/core/java/android/view/inputmethod/ExtractedTextRequest.java +++ b/core/java/android/view/inputmethod/ExtractedTextRequest.java @@ -16,6 +16,13 @@ public class ExtractedTextRequest implements Parcelable { public int token; /** + * Additional request flags, having the same possible values as the + * flags parameter of {@link InputConnection#getTextBeforeCursor + * InputConnection.getTextBeforeCursor()}. + */ + public int flags; + + /** * Hint for the maximum number of lines to return. */ public int hintMaxLines; @@ -33,6 +40,7 @@ public class ExtractedTextRequest implements Parcelable { */ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(token); + dest.writeInt(this.flags); dest.writeInt(hintMaxLines); dest.writeInt(hintMaxChars); } @@ -45,6 +53,7 @@ public class ExtractedTextRequest implements Parcelable { public ExtractedTextRequest createFromParcel(Parcel source) { ExtractedTextRequest res = new ExtractedTextRequest(); res.token = source.readInt(); + res.flags = source.readInt(); res.hintMaxLines = source.readInt(); res.hintMaxChars = source.readInt(); return res; diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index bd7b050..8c30d3f 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -32,6 +32,21 @@ import android.view.KeyEvent; */ public interface InputConnection { /** + * Flag for use with {@link #getTextAfterCursor} and + * {@link #getTextBeforeCursor} to have style information returned along + * with the text. If not set, you will receive only the raw text. If + * set, you may receive a complex CharSequence of both text and style + * spans. + */ + static final int GET_TEXT_WITH_STYLES = 0x0001; + + /** + * Flag for use with {@link #getExtractedText} to indicate you would + * like to receive updates when the extracted text changes. + */ + public static final int GET_EXTRACTED_TEXT_MONITOR = 0x0001; + + /** * Get <var>n</var> characters of text before the current cursor position. * * <p>This method may fail either if the input connection has become invalid @@ -40,11 +55,13 @@ public interface InputConnection { * In either case, a null is returned. * * @param n The expected length of the text. + * @param flags Supplies additional options controlling how the text is + * returned. May be either 0 or {@link #GET_TEXT_WITH_STYLES}. * * @return Returns the text before the cursor position; the length of the * returned text might be less than <var>n</var>. */ - public CharSequence getTextBeforeCursor(int n); + public CharSequence getTextBeforeCursor(int n, int flags); /** * Get <var>n</var> characters of text after the current cursor position. @@ -55,11 +72,13 @@ public interface InputConnection { * In either case, a null is returned. * * @param n The expected length of the text. + * @param flags Supplies additional options controlling how the text is + * returned. May be either 0 or {@link #GET_TEXT_WITH_STYLES}. * * @return Returns the text after the cursor position; the length of the * returned text might be less than <var>n</var>. */ - public CharSequence getTextAfterCursor(int n); + public CharSequence getTextAfterCursor(int n, int flags); /** * Retrieve the current capitalization mode in effect at the current @@ -82,8 +101,6 @@ public interface InputConnection { */ public int getCursorCapsMode(int reqModes); - public static final int EXTRACTED_TEXT_MONITOR = 0x0001; - /** * Retrieve the current text in the input connection's editor, and monitor * for any changes to it. This function returns with the current text, @@ -97,7 +114,7 @@ public interface InputConnection { * * @param request Description of how the text should be returned. * @param flags Additional options to control the client, either 0 or - * {@link #EXTRACTED_TEXT_MONITOR}. + * {@link #GET_EXTRACTED_TEXT_MONITOR}. * * @return Returns an ExtractedText object describing the state of the * text view and containing the extracted text itself. @@ -141,7 +158,7 @@ public interface InputConnection { /** * Have the text editor finish whatever composing text is currently - * active. This simple leaves the text as-is, removing any special + * active. This simply leaves the text as-is, removing any special * composing styling or other state that was around it. The cursor * position remains unchanged. */ @@ -177,6 +194,22 @@ public interface InputConnection { public boolean commitCompletion(CompletionInfo text); /** + * Set the selection of the text editor. To set the cursor position, + * start and end should have the same value. + */ + public boolean setSelection(int start, int end); + + /** + * Perform a context menu action on the field. The given id may be one of: + * {@link android.R.id#selectAll}, + * {@link android.R.id#startSelectingText}, {@link android.R.id#stopSelectingText}, + * {@link android.R.id#cut}, {@link android.R.id#copy}, + * {@link android.R.id#paste}, {@link android.R.id#copyUrl}, + * or {@link android.R.id#switchInputMethod} + */ + public boolean performContextMenuAction(int id); + + /** * Tell the editor that you are starting a batch of editor operations. * The editor will try to avoid sending you updates about its state * until {@link #endBatchEdit} is called. diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java deleted file mode 100644 index f65b2a1..0000000 --- a/core/java/android/view/inputmethod/InputConnectionWrapper.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2007-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 android.view.inputmethod; - -import android.os.Bundle; -import android.view.KeyEvent; - -/** - * Wrapper around InputConnection interface, calling through to another - * implementation of it. - */ -public class InputConnectionWrapper implements InputConnection { - InputConnection mBase; - - /** - * Create a new wrapper around an existing InputConnection implementation. - */ - public InputConnectionWrapper(InputConnection base) { - mBase = base; - } - - /** - * Return the base InputConnection that this class is wrapping. - */ - InputConnection getBase() { - return mBase; - } - - public CharSequence getTextBeforeCursor(int n) { - return mBase.getTextBeforeCursor(n); - } - - public CharSequence getTextAfterCursor(int n) { - return mBase.getTextAfterCursor(n); - } - - public int getCursorCapsMode(int reqModes) { - return mBase.getCursorCapsMode(reqModes); - } - - public ExtractedText getExtractedText(ExtractedTextRequest request, - int flags) { - return mBase.getExtractedText(request, flags); - } - - public boolean deleteSurroundingText(int leftLength, int rightLength) { - return mBase.deleteSurroundingText(leftLength, rightLength); - } - - public boolean setComposingText(CharSequence text, int newCursorPosition) { - return mBase.setComposingText(text, newCursorPosition); - } - - public boolean finishComposingText() { - return mBase.finishComposingText(); - } - - public boolean commitText(CharSequence text, int newCursorPosition) { - return mBase.commitText(text, newCursorPosition); - } - - public boolean commitCompletion(CompletionInfo text) { - return mBase.commitCompletion(text); - } - - public boolean beginBatchEdit() { - return mBase.beginBatchEdit(); - } - - public boolean endBatchEdit() { - return mBase.endBatchEdit(); - } - - public boolean sendKeyEvent(KeyEvent event) { - return mBase.sendKeyEvent(event); - } - - public boolean clearMetaKeyStates(int states) { - return mBase.clearMetaKeyStates(states); - } - - public boolean performPrivateCommand(String action, Bundle data) { - return mBase.performPrivateCommand(action, data); - } - - public boolean showStatusIcon(String packageName, int resId) { - return mBase.showStatusIcon(packageName, resId); - } - - public boolean hideStatusIcon() { - return mBase.hideStatusIcon(); - } -} diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java index c0e6590..740dca8 100644 --- a/core/java/android/view/inputmethod/InputMethod.java +++ b/core/java/android/view/inputmethod/InputMethod.java @@ -113,12 +113,15 @@ public interface InputMethod { * is ready for this input method to process received events and send result * text back to the application. * - * @param attribute The attribute of the text box (typically, a EditText) + * @param inputConnection Optional specific input connection for + * communicating with the text box; if null, you should use the generic + * bound input connection. + * @param info Information about the text box (typically, an EditText) * that requests input. * * @see EditorInfo */ - public void startInput(EditorInfo attribute); + public void startInput(InputConnection inputConnection, EditorInfo info); /** * This method is called when the state of this input method needs to be @@ -128,12 +131,15 @@ public interface InputMethod { * Typically, this method is called when the input focus is moved from one * text box to another. * + * @param inputConnection Optional specific input connection for + * communicating with the text box; if null, you should use the generic + * bound input connection. * @param attribute The attribute of the text box (typically, a EditText) * that requests input. * * @see EditorInfo */ - public void restartInput(EditorInfo attribute); + public void restartInput(InputConnection inputConnection, EditorInfo attribute); /** * Create a new {@link InputMethodSession} that can be handed to client @@ -173,6 +179,13 @@ public interface InputMethod { public static final int SHOW_EXPLICIT = 0x00001; /** + * Flag for {@link #showSoftInput(int)}: this show has been forced to + * happen by the user. If set, the input method should remain visible + * until deliberated dismissed by the user in its UI. + */ + public static final int SHOW_FORCED = 0x00002; + + /** * Request that any soft input part of the input method be shown to the user. * * @param flags Provide additional information about the show request. diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index a9a9594..99d5aa5 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -26,11 +26,14 @@ import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; +import android.util.PrintWriterPrinter; +import android.util.Printer; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewRoot; +import com.android.internal.os.HandlerCaller; import com.android.internal.view.IInputConnectionWrapper; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodCallback; @@ -39,7 +42,11 @@ import com.android.internal.view.IInputMethodManager; import com.android.internal.view.IInputMethodSession; import com.android.internal.view.InputBindResult; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * Central system API to the overall input method framework (IMF) architecture, @@ -199,8 +206,7 @@ public final class InputMethodManager { // global lock. final H mH; - // The currently active input connection. - final MutableInputConnectionWrapper mInputConnectionWrapper; + // Our generic input connection if the current target does not have its own. final IInputContext mIInputContext; /** @@ -208,11 +214,6 @@ public final class InputMethodManager { */ boolean mActive = false; - /** - * The current base input connection, used when mActive is true. - */ - InputConnection mCurrentInputConnection; - // ----------------------------------------------------------- /** @@ -270,7 +271,7 @@ public final class InputMethodManager { // ----------------------------------------------------------- - static final int MSG_CHECK_FOCUS = 1; + static final int MSG_DUMP = 1; class H extends Handler { H(Looper looper) { @@ -280,85 +281,55 @@ public final class InputMethodManager { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_CHECK_FOCUS: - checkFocus(); + case MSG_DUMP: { + HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj; + try { + doDump((FileDescriptor)args.arg1, + (PrintWriter)args.arg2, (String[])args.arg3); + } catch (RuntimeException e) { + ((PrintWriter)args.arg2).println("Exception: " + e); + } + synchronized (args.arg4) { + ((CountDownLatch)args.arg4).countDown(); + } return; + } } } } - static class NoOpInputConnection implements InputConnection { - - public boolean clearMetaKeyStates(int states) { - return false; - } - - public boolean beginBatchEdit() { - return false; - } - - public boolean endBatchEdit() { - return false; - } - - public boolean commitCompletion(CompletionInfo text) { - return false; - } - - public boolean commitText(CharSequence text, int newCursorPosition) { - return false; - } - - public boolean deleteSurroundingText(int leftLength, int rightLength) { - return false; - } - - public int getCursorCapsMode(int reqModes) { - return 0; - } - - public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { - return null; - } - - public CharSequence getTextAfterCursor(int n) { - return null; + class ControlledInputConnectionWrapper extends IInputConnectionWrapper { + public ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn) { + super(mainLooper, conn); } - public CharSequence getTextBeforeCursor(int n) { - return null; - } - - public boolean hideStatusIcon() { - return false; - } - - public boolean performPrivateCommand(String action, Bundle data) { - return false; - } - - public boolean sendKeyEvent(KeyEvent event) { - return false; - } - - public boolean setComposingText(CharSequence text, int newCursorPosition) { - return false; - } - - public boolean finishComposingText() { - return false; - } - - public boolean showStatusIcon(String packageName, int resId) { - return false; + public boolean isActive() { + return mActive; } } - final NoOpInputConnection mNoOpInputConnection = new NoOpInputConnection(); - final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() { - public void setUsingInputMethod(boolean state) { + @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { + // No need to check for dump permission, since we only give this + // interface to the system. + CountDownLatch latch = new CountDownLatch(1); + HandlerCaller.SomeArgs sargs = new HandlerCaller.SomeArgs(); + sargs.arg1 = fd; + sargs.arg2 = fout; + sargs.arg3 = args; + sargs.arg4 = latch; + mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs)); + try { + if (!latch.await(5, TimeUnit.SECONDS)) { + fout.println("Timeout waiting for dump"); + } + } catch (InterruptedException e) { + fout.println("Interrupted waiting for dump"); + } + } + + public void setUsingInputMethod(boolean state) { } public void onBindMethod(InputBindResult res) { @@ -403,62 +374,17 @@ public final class InputMethodManager { public void setActive(boolean active) { mActive = active; - mInputConnectionWrapper.setBaseInputConnection(active - ? mCurrentInputConnection : mNoOpInputConnection); } }; - final InputConnection mDummyInputConnection = new BaseInputConnection(this) { - public boolean beginBatchEdit() { - return false; - } - public boolean endBatchEdit() { - return false; - } - public boolean commitText(CharSequence text, int newCursorPosition) { - return false; - } - public boolean commitCompletion(CompletionInfo text) { - return false; - } - public boolean deleteSurroundingText(int leftLength, int rightLength) { - return false; - } - public ExtractedText getExtractedText(ExtractedTextRequest request, - int flags) { - return null; - } - public CharSequence getTextAfterCursor(int n) { - return null; - } - public CharSequence getTextBeforeCursor(int n) { - return null; - } - public int getCursorCapsMode(int reqModes) { - return 0; - } - public boolean clearMetaKeyStates(int states) { - return false; - } - public boolean performPrivateCommand(String action, Bundle data) { - return false; - } - public boolean setComposingText(CharSequence text, int newCursorPosition) { - return false; - } - public boolean finishComposingText() { - return false; - } - }; + final InputConnection mDummyInputConnection = new BaseInputConnection(this, true); InputMethodManager(IInputMethodManager service, Looper looper) { mService = service; mMainLooper = looper; mH = new H(looper); - mInputConnectionWrapper = new MutableInputConnectionWrapper(mNoOpInputConnection); - mIInputContext = new IInputConnectionWrapper(looper, - mInputConnectionWrapper); - setCurrentInputConnection(mDummyInputConnection); + mIInputContext = new ControlledInputConnectionWrapper(looper, + mDummyInputConnection); if (mInstance == null) { mInstance = this; @@ -563,22 +489,12 @@ public final class InputMethodManager { } /** - * Record the desired input connection, but only set it if mActive is true. - */ - void setCurrentInputConnection(InputConnection connection) { - mCurrentInputConnection = connection; - mInputConnectionWrapper.setBaseInputConnection(mActive - ? connection : mNoOpInputConnection); - } - - /** * Reset all of the state associated with a served view being connected * to an input method */ void clearConnectionLocked() { mCurrentTextBoxAttribute = null; mServedInputConnection = null; - setCurrentInputConnection(mDummyInputConnection); } /** @@ -659,13 +575,20 @@ public final class InputMethodManager { } /** - * Flag for {@link #showSoftInput} to indicate that the this is an implicit + * Flag for {@link #showSoftInput} to indicate that this is an implicit * request to show the input window, not as the result of a direct request * by the user. The window may not be shown in this case. */ public static final int SHOW_IMPLICIT = 0x0001; /** + * Flag for {@link #showSoftInput} to indicate that the user has forced + * the input method open (such as by long-pressing menu) so it should + * not be closed until they explicitly do so. + */ + public static final int SHOW_FORCED = 0x0002; + + /** * Explicitly request that the current input method's soft input area be * shown to the user, if needed. Call this if the user interacts with * your view in such a way that they have expressed they would like to @@ -689,6 +612,14 @@ public final class InputMethodManager { } } + /** @hide */ + public void showSoftInputUnchecked(int flags) { + try { + mService.showSoftInput(mClient, flags); + } catch (RemoteException e) { + } + } + /** * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft * input window should only be hidden if it was not explicitly shown @@ -697,6 +628,13 @@ public final class InputMethodManager { public static final int HIDE_IMPLICIT_ONLY = 0x0001; /** + * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft + * input window should normally be hidden, unless it was originally + * shown with {@link #SHOW_FORCED}. + */ + public static final int HIDE_NOT_ALWAYS = 0x0002; + + /** * Request to hide the soft input window from the context of the window * that is currently accepting input. This should be called as a result * of the user doing some actually than fairly explicitly requests to @@ -779,6 +717,8 @@ public final class InputMethodManager { // do its stuff. // Life is good: let's hook everything up! EditorInfo tba = new EditorInfo(); + tba.packageName = view.getContext().getPackageName(); + tba.fieldId = view.getId(); InputConnection ic = view.onCreateInputConnection(tba); if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic); @@ -801,22 +741,23 @@ public final class InputMethodManager { mCurrentTextBoxAttribute = tba; mServedConnecting = false; mServedInputConnection = ic; + IInputContext servedContext; if (ic != null) { mCursorSelStart = tba.initialSelStart; mCursorSelEnd = tba.initialSelEnd; mCursorCandStart = -1; mCursorCandEnd = -1; mCursorRect.setEmpty(); - setCurrentInputConnection(ic); + servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic); } else { - setCurrentInputConnection(mDummyInputConnection); + servedContext = null; } try { if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic=" + ic + " tba=" + tba + " initial=" + initial); - InputBindResult res = mService.startInput(mClient, tba, initial, - mCurMethod == null); + InputBindResult res = mService.startInput(mClient, + servedContext, tba, initial, mCurMethod == null); if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); if (res != null) { if (res.id != null) { @@ -885,10 +826,20 @@ public final class InputMethodManager { + " winFocus=" + view.hasWindowFocus()); if (mServedView == view) { ic = mServedInputConnection; - if (view.hasWindowFocus()) { + // The following code would auto-hide the IME if we end up + // with no more views with focus. This can happen, however, + // whenever we go into touch mode, so it ends up hiding + // at times when we don't really want it to. For now it + // seems better to just turn it all off. + if (false && view.hasWindowFocus()) { mLastServedView = view; - mH.removeMessages(MSG_CHECK_FOCUS); - mH.sendEmptyMessage(MSG_CHECK_FOCUS); + Handler vh = view.getHandler(); + if (vh != null) { + // This will result in a call to checkFocus() below. + vh.removeMessages(ViewRoot.CHECK_FOCUS); + vh.sendMessage(vh.obtainMessage(ViewRoot.CHECK_FOCUS, + view)); + } } } } @@ -898,8 +849,14 @@ public final class InputMethodManager { } } - void checkFocus() { + /** + * @hide + */ + public void checkFocus(View view) { synchronized (mH) { + if (view != mLastServedView) { + return; + } if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView + " last=" + mLastServedView); if (mServedView == mLastServedView) { @@ -915,7 +872,7 @@ public final class InputMethodManager { void closeCurrentInput() { try { - mService.hideSoftInput(mClient, 0); + mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS); } catch (RemoteException e) { } } @@ -1006,6 +963,32 @@ public final class InputMethodManager { } /** + * Call {@link InputMethodSession#appPrivateCommand(String, Bundle) + * InputMethodSession.appPrivateCommand()} on the current Input Method. + * @param view Optional View that is sending the command, or null if + * you want to send the command regardless of the view that is attached + * to the input method. + * @param action Name of the command to be performed. This <em>must</em> + * be a scoped name, i.e. prefixed with a package name you own, so that + * different developers will not create conflicting commands. + * @param data Any data to include with the command. + */ + public void sendAppPrivateCommand(View view, String action, Bundle data) { + synchronized (mH) { + if ((view != null && mServedView != view) + || mCurrentTextBoxAttribute == null || mCurMethod == null) { + return; + } + try { + if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data); + mCurMethod.appPrivateCommand(action, data); + } catch (RemoteException e) { + Log.w(TAG, "IME died: " + mCurId, e); + } + } + } + + /** * Force switch to a new input method component. This can only be called * from the currently active input method, as validated by the given token. * @param token Supplies the identifying token given to an input method @@ -1048,7 +1031,7 @@ public final class InputMethodManager { synchronized (mH) { if (DEBUG) Log.d(TAG, "dispatchKeyEvent"); - if (mCurMethod == null || mCurrentTextBoxAttribute == null) { + if (mCurMethod == null) { try { callback.finishedEvent(seq, false); } catch (RemoteException e) { @@ -1116,4 +1099,33 @@ public final class InputMethodManager { } } } + + void doDump(FileDescriptor fd, PrintWriter fout, String[] args) { + final Printer p = new PrintWriterPrinter(fout); + p.println("Input method client state for " + this + ":"); + + p.println(" mService=" + mService); + p.println(" mMainLooper=" + mMainLooper); + p.println(" mIInputContext=" + mIInputContext); + p.println(" mActive=" + mActive + + " mBindSequence=" + mBindSequence + + " mCurId=" + mCurId); + p.println(" mCurMethod=" + mCurMethod); + p.println(" mServedView=" + mServedView); + p.println(" mLastServedView=" + mLastServedView); + p.println(" mServedConnecting=" + mServedConnecting); + if (mCurrentTextBoxAttribute != null) { + p.println(" mCurrentTextBoxAttribute:"); + mCurrentTextBoxAttribute.dump(p, " "); + } else { + p.println(" mCurrentTextBoxAttribute: null"); + } + p.println(" mServedInputConnection=" + mServedInputConnection); + p.println(" mCompletions=" + mCompletions); + p.println(" mCursorRect=" + mCursorRect); + p.println(" mCursorSelStart=" + mCursorSelStart + + " mCursorSelEnd=" + mCursorSelEnd + + " mCursorCandStart=" + mCursorCandStart + + " mCursorCandEnd=" + mCursorCandEnd); + } } diff --git a/core/java/android/view/inputmethod/MutableInputConnectionWrapper.java b/core/java/android/view/inputmethod/MutableInputConnectionWrapper.java deleted file mode 100644 index 025a059..0000000 --- a/core/java/android/view/inputmethod/MutableInputConnectionWrapper.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2007-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 android.view.inputmethod; - - -/** - * Special version of {@link InputConnectionWrapper} that allows the base - * input connection to be modified after it is initially set. - */ -public class MutableInputConnectionWrapper extends InputConnectionWrapper { - public MutableInputConnectionWrapper(InputConnection base) { - super(base); - } - - /** - * Change the base InputConnection for this wrapper. All calls will then be - * delegated to the base input connection. - * - * @param base The new base InputConnection for this wrapper. - */ - public void setBaseInputConnection(InputConnection base) { - mBase = base; - } -} diff --git a/core/java/android/webkit/ByteArrayBuilder.java b/core/java/android/webkit/ByteArrayBuilder.java index 16d663c..806b458 100644 --- a/core/java/android/webkit/ByteArrayBuilder.java +++ b/core/java/android/webkit/ByteArrayBuilder.java @@ -94,6 +94,14 @@ class ByteArrayBuilder { return mChunks.isEmpty(); } + public synchronized void clear() { + Chunk c = getFirstChunk(); + while (c != null) { + releaseChunk(c); + c = getFirstChunk(); + } + } + private Chunk appendChunk(int length) { if (length < mMinCapacity) { length = mMinCapacity; diff --git a/core/java/android/webkit/FileLoader.java b/core/java/android/webkit/FileLoader.java index 10343b2..54a4c1d 100644 --- a/core/java/android/webkit/FileLoader.java +++ b/core/java/android/webkit/FileLoader.java @@ -76,8 +76,12 @@ class FileLoader extends StreamLoader { protected boolean setupStreamAndSendStatus() { try { if (mIsAsset) { - mDataStream = mContext.getAssets().open(mPath, - AssetManager.ACCESS_STREAMING); + try { + mDataStream = mContext.getAssets().open(mPath); + } catch (java.io.FileNotFoundException ex) { + // try the rest files included in the package + mDataStream = mContext.getAssets().openNonAsset(mPath); + } } else { if (!mAllowFileAccess) { mHandler.error(EventHandler.FILE_ERROR, diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java index 7a3bbe6..5e323eb 100644 --- a/core/java/android/webkit/FrameLoader.java +++ b/core/java/android/webkit/FrameLoader.java @@ -220,6 +220,7 @@ class FrameLoader { // Tell the Listener respond with the cache file CacheLoader cacheLoader = new CacheLoader(mListener, result); + mListener.setCacheLoader(cacheLoader); cacheLoader.load(); } diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java index 3f2bbe5..3694969 100644 --- a/core/java/android/webkit/LoadListener.java +++ b/core/java/android/webkit/LoadListener.java @@ -33,6 +33,8 @@ import android.util.Config; import android.util.Log; import android.webkit.CacheManager.CacheResult; +import com.android.internal.R; + import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -56,6 +58,9 @@ class LoadListener extends Handler implements EventHandler { private static final int MSG_CONTENT_ERROR = 130; private static final int MSG_LOCATION_CHANGED = 140; private static final int MSG_LOCATION_CHANGED_REQUEST = 150; + private static final int MSG_STATUS = 160; + private static final int MSG_SSL_CERTIFICATE = 170; + private static final int MSG_SSL_ERROR = 180; // Standard HTTP status codes in a more representative format private static final int HTTP_OK = 200; @@ -166,9 +171,7 @@ class LoadListener extends Handler implements EventHandler { * available. The headers are sent onto WebCore to see what we * should do with them. */ - if (mNativeLoader != 0) { - commitHeadersCheckRedirect(); - } + handleHeaders((Headers) msg.obj); break; case MSG_CONTENT_DATA: @@ -177,7 +180,7 @@ class LoadListener extends Handler implements EventHandler { * in it's data buffer. This data buffer could be filled from a * file (this thread) or from http (Network thread). */ - if (mNativeLoader != 0) { + if (mNativeLoader != 0 && !ignoreCallbacks()) { commitLoad(); } break; @@ -189,7 +192,7 @@ class LoadListener extends Handler implements EventHandler { * error. * */ - tearDown(); + handleEndData(); break; case MSG_CONTENT_ERROR: @@ -197,8 +200,7 @@ class LoadListener extends Handler implements EventHandler { * This message is sent when a load error has occured. The * LoadListener will clean itself up. */ - notifyError(); - tearDown(); + handleError(msg.arg1, (String) msg.obj); break; case MSG_LOCATION_CHANGED: @@ -224,6 +226,33 @@ class LoadListener extends Handler implements EventHandler { stopMsg, contMsg); break; + case MSG_STATUS: + /* + * This message is sent from the network thread when the http + * stack has received the status response from the server. + */ + HashMap status = (HashMap) msg.obj; + handleStatus(((Integer) status.get("major")).intValue(), + ((Integer) status.get("minor")).intValue(), + ((Integer) status.get("code")).intValue(), + (String) status.get("reason")); + break; + + case MSG_SSL_CERTIFICATE: + /* + * This message is sent when the network thread receives a ssl + * certificate. + */ + handleCertificate((SslCertificate) msg.obj); + break; + + case MSG_SSL_ERROR: + /* + * This message is sent when the network thread encounters a + * ssl error. + */ + handleSslError((SslError) msg.obj); + break; } } @@ -257,6 +286,11 @@ class LoadListener extends Handler implements EventHandler { */ public void headers(Headers headers) { if (Config.LOGV) Log.v(LOGTAG, "LoadListener.headers"); + sendMessageInternal(obtainMessage(MSG_CONTENT_HEADERS, headers)); + } + + // Does the header parsing work on the WebCore thread. + private void handleHeaders(Headers headers) { if (mCancelled) return; mHeaders = headers; mMimeType = ""; @@ -375,7 +409,7 @@ class LoadListener extends Handler implements EventHandler { mCacheResult.encoding = mEncoding; } } - sendMessageInternal(obtainMessage(MSG_CONTENT_HEADERS)); + commitHeadersCheckRedirect(); } /** @@ -404,11 +438,20 @@ class LoadListener extends Handler implements EventHandler { + " code: " + code + " reason: " + reasonPhrase); } + HashMap status = new HashMap(); + status.put("major", majorVersion); + status.put("minor", minorVersion); + status.put("code", code); + status.put("reason", reasonPhrase); + sendMessageInternal(obtainMessage(MSG_STATUS, status)); + } + // Handle the status callback on the WebCore thread. + private void handleStatus(int major, int minor, int code, String reason) { if (mCancelled) return; mStatusCode = code; - mStatusText = reasonPhrase; + mStatusText = reason; mPermanent = false; } @@ -418,8 +461,15 @@ class LoadListener extends Handler implements EventHandler { * connection. In this context, can be called multiple * times if we have redirects * @param certificate The SSL certifcate + * IMPORTANT: as this is called from network thread, can't call native + * directly */ public void certificate(SslCertificate certificate) { + sendMessageInternal(obtainMessage(MSG_SSL_CERTIFICATE, certificate)); + } + + // Handle the certificate on the WebCore thread. + private void handleCertificate(SslCertificate certificate) { // if this is the top-most main-frame page loader if (mIsMainPageLoader) { // update the browser frame (ie, the main frame) @@ -436,14 +486,20 @@ class LoadListener extends Handler implements EventHandler { * directly */ public void error(int id, String description) { - mErrorID = id; - mErrorDescription = description; - sendMessageInternal(obtainMessage(MSG_CONTENT_ERROR)); if (Config.LOGV) { Log.v(LOGTAG, "LoadListener.error url:" + url() + " id:" + id + " description:" + description); } + sendMessageInternal(obtainMessage(MSG_CONTENT_ERROR, id, 0, description)); + } + + // Handle the error on the WebCore thread. + private void handleError(int id, String description) { + mErrorID = id; + mErrorDescription = description; detachRequestHandle(); + notifyError(); + tearDown(); } /** @@ -453,11 +509,15 @@ class LoadListener extends Handler implements EventHandler { * @param length The length of data. * IMPORTANT: as this is called from network thread, can't call native * directly + * XXX: Unlike the other network thread methods, this method can do the + * work of decoding the data and appending it to the data builder because + * mDataBuilder is a thread-safe structure. */ public void data(byte[] data, int length) { if (Config.LOGV) { Log.v(LOGTAG, "LoadListener.data(): url: " + url()); } + // Decode base64 data // Note: It's fine that we only decode base64 here and not in the other // data call because the only caller of the stream version is not @@ -479,7 +539,7 @@ class LoadListener extends Handler implements EventHandler { sendMessage = mDataBuilder.isEmpty(); mDataBuilder.append(data, 0, length); } - if (sendMessage && !ignoreCallbacks()) { + if (sendMessage) { // Send a message whenever data comes in after a write to WebCore sendMessageInternal(obtainMessage(MSG_CONTENT_DATA)); } @@ -495,7 +555,11 @@ class LoadListener extends Handler implements EventHandler { if (Config.LOGV) { Log.v(LOGTAG, "LoadListener.endData(): url: " + url()); } + sendMessageInternal(obtainMessage(MSG_CONTENT_FINISHED)); + } + // Handle the end of data. + private void handleEndData() { if (mCancelled) return; switch (mStatusCode) { @@ -505,17 +569,13 @@ class LoadListener extends Handler implements EventHandler { case HTTP_FOUND: case HTTP_SEE_OTHER: case HTTP_TEMPORARY_REDIRECT: - if (mMethod == null && mRequestHandle == null) { - Log.e(LOGTAG, "LoadListener.endData(): method is null!"); - Log.e(LOGTAG, "LoadListener.endData(): url = " + url()); - } // 301, 302, 303, and 307 - redirect if (mStatusCode == HTTP_TEMPORARY_REDIRECT) { if (mRequestHandle != null && mRequestHandle.getMethod().equals("POST")) { sendMessageInternal(obtainMessage( MSG_LOCATION_CHANGED_REQUEST)); - } else if (mMethod != null && mMethod.equals("POST")) { + } else if (mMethod.equals("POST")) { sendMessageInternal(obtainMessage( MSG_LOCATION_CHANGED_REQUEST)); } else { @@ -558,9 +618,14 @@ class LoadListener extends Handler implements EventHandler { default: break; } - - sendMessageInternal(obtainMessage(MSG_CONTENT_FINISHED)); detachRequestHandle(); + tearDown(); + } + + /* This method is called from CacheLoader when the initial request is + * serviced by the Cache. */ + /* package */ void setCacheLoader(CacheLoader c) { + mCacheLoader = c; } /** @@ -574,9 +639,16 @@ class LoadListener extends Handler implements EventHandler { CacheResult result = CacheManager.getCacheFile(url(), headers); + // Go ahead and set the cache loader to null in case the result is + // null. + mCacheLoader = null; + if (result != null) { - CacheLoader cacheLoader = - new CacheLoader(this, result); + // The contents of the cache may need to be revalidated so just + // remember the cache loader in the case that the server responds + // positively to the cached content. This is also used to detect if + // a redirect came from the cache. + mCacheLoader = new CacheLoader(this, result); // If I got a cachedUrl and the revalidation header was not // added, then the cached content valid, we should use it. @@ -589,14 +661,8 @@ class LoadListener extends Handler implements EventHandler { "and usable: " + url()); } // Load the cached file - cacheLoader.load(); + mCacheLoader.load(); return true; - } else { - // The contents of the cache need to be revalidated - // so just provide the listener with the cache loader - // in the case that the server response positively to - // the cached content. - setCacheLoader(cacheLoader); } } return false; @@ -615,7 +681,11 @@ class LoadListener extends Handler implements EventHandler { " primary error: " + error.getPrimaryError() + " certificate: " + error.getCertificate()); } + sendMessageInternal(obtainMessage(MSG_SSL_ERROR, error)); + } + // Handle the ssl error on the WebCore thread. + private void handleSslError(SslError error) { if (!mCancelled) { mSslError = error; Network.getInstance(mContext).handleSslErrorRequest(this); @@ -705,14 +775,6 @@ class LoadListener extends Handler implements EventHandler { } /** - * Set the CacheLoader for the case where we might want to load from cache - * @param result - */ - void setCacheLoader(CacheLoader result) { - mCacheLoader = result; - } - - /** * This is called when a request can be satisfied by the cache, however, * the cache result could be a redirect. In this case we need to issue * the network request. @@ -1002,6 +1064,11 @@ class LoadListener extends Handler implements EventHandler { clearNativeLoader(); } + // This count is transferred from RequestHandle to LoadListener when + // loading from the cache so that we can detect redirect loops that switch + // between the network and the cache. + private int mCacheRedirectCount; + /* * Perform the actual redirection. This involves setting up the new URL, * informing WebCore and then telling the Network to start loading again. @@ -1014,6 +1081,14 @@ class LoadListener extends Handler implements EventHandler { return; } + // Do the same check for a redirect loop that + // RequestHandle.setupRedirect does. + if (mCacheRedirectCount >= RequestHandle.MAX_REDIRECT_COUNT) { + handleError(EventHandler.ERROR_REDIRECT_LOOP, mContext.getString( + R.string.httpErrorRedirectLoop)); + return; + } + String redirectTo = mHeaders.getLocation(); if (redirectTo != null) { int nativeResponse = createNativeResponse(); @@ -1031,7 +1106,7 @@ class LoadListener extends Handler implements EventHandler { return; } else if (!URLUtil.isNetworkUrl(redirectTo)) { final String text = mContext - .getString(com.android.internal.R.string.open_permission_deny) + .getString(R.string.open_permission_deny) + "\n" + redirectTo; nativeAddData(text.getBytes(), text.length()); nativeFinished(); @@ -1057,6 +1132,13 @@ class LoadListener extends Handler implements EventHandler { if (mRequestHeaders == null) { mRequestHeaders = new HashMap<String, String>(); } + boolean fromCache = false; + if (mCacheLoader != null) { + // This is a redirect from the cache loader. Increment the + // redirect count to avoid redirect loops. + mCacheRedirectCount++; + fromCache = true; + } if (!checkCache(mRequestHeaders)) { // mRequestHandle can be null when the request was satisfied // by the cache, and the cache returned a redirect @@ -1064,23 +1146,37 @@ class LoadListener extends Handler implements EventHandler { mRequestHandle.setupRedirect(redirectTo, mStatusCode, mRequestHeaders); } else { - String method = mMethod; - - if (method == null) { + // If the original request came from the cache, there is no + // RequestHandle, we have to create a new one through + // Network.requestURL. + Network network = Network.getInstance(getContext()); + if (!network.requestURL(mMethod, mRequestHeaders, + mPostData, this, mIsHighPriority)) { + // Signal a bad url error if we could not load the + // redirection. + handleError(EventHandler.ERROR_BAD_URL, + mContext.getString(R.string.httpErrorBadUrl)); return; } - - Network network = Network.getInstance(getContext()); - network.requestURL(method, mRequestHeaders, - mPostData, this, mIsHighPriority); } + if (fromCache) { + // If we are coming from a cache load, we need to transfer + // the redirect count to the new (or old) RequestHandle to + // keep the redirect count in sync. + mRequestHandle.setRedirectCount(mCacheRedirectCount); + } + } else if (!fromCache) { + // Switching from network to cache means we need to grab the + // redirect count from the RequestHandle to keep the count in + // sync. Add 1 to account for the current redirect. + mCacheRedirectCount = mRequestHandle.getRedirectCount() + 1; } + // Clear the buffered data since the redirect is valid. + mDataBuilder.clear(); } else { - // With a null redirect, commit the original headers, the buffered - // data, and then finish the load. commitHeaders(); commitLoad(); - nativeFinished(); + tearDown(); } if (Config.LOGV) { @@ -1167,14 +1263,16 @@ class LoadListener extends Handler implements EventHandler { quoted = !quoted; } else { if (!quoted) { - if (header.startsWith( - HttpAuthHeader.BASIC_TOKEN, i)) { + if (header.regionMatches(true, i, + HttpAuthHeader.BASIC_TOKEN, 0, + HttpAuthHeader.BASIC_TOKEN.length())) { pos[posLen++] = i; continue; } - if (header.startsWith( - HttpAuthHeader.DIGEST_TOKEN, i)) { + if (header.regionMatches(true, i, + HttpAuthHeader.DIGEST_TOKEN, 0, + HttpAuthHeader.DIGEST_TOKEN.length())) { pos[posLen++] = i; continue; } @@ -1186,8 +1284,9 @@ class LoadListener extends Handler implements EventHandler { if (posLen > 0) { // consider all digest schemes first (if any) for (int i = 0; i < posLen; i++) { - if (header.startsWith(HttpAuthHeader.DIGEST_TOKEN, - pos[i])) { + if (header.regionMatches(true, pos[i], + HttpAuthHeader.DIGEST_TOKEN, 0, + HttpAuthHeader.DIGEST_TOKEN.length())) { String sub = header.substring(pos[i], (i + 1 < posLen ? pos[i + 1] : headerLen)); @@ -1201,7 +1300,9 @@ class LoadListener extends Handler implements EventHandler { // ...then consider all basic schemes (if any) for (int i = 0; i < posLen; i++) { - if (header.startsWith(HttpAuthHeader.BASIC_TOKEN, pos[i])) { + if (header.regionMatches(true, pos[i], + HttpAuthHeader.BASIC_TOKEN, 0, + HttpAuthHeader.BASIC_TOKEN.length())) { String sub = header.substring(pos[i], (i + 1 < posLen ? pos[i + 1] : headerLen)); @@ -1262,9 +1363,8 @@ class LoadListener extends Handler implements EventHandler { // type (implying text/plain). if (URLUtil.isDataUrl(mUrl) && mMimeType.length() != 0) { cancel(); - final String text = mContext.getString( - com.android.internal.R.string.httpErrorBadUrl); - error(EventHandler.ERROR_BAD_URL, text); + final String text = mContext.getString(R.string.httpErrorBadUrl); + handleError(EventHandler.ERROR_BAD_URL, text); } else { // Note: This is ok because this is used only for the main content // of frames. If no content-type was specified, it is fine to diff --git a/core/java/android/webkit/MimeTypeMap.java b/core/java/android/webkit/MimeTypeMap.java index 685e352..c9cc208 100644 --- a/core/java/android/webkit/MimeTypeMap.java +++ b/core/java/android/webkit/MimeTypeMap.java @@ -480,6 +480,7 @@ public /* package */ class MimeTypeMap { sMimeTypeMap.loadEntry("video/mpeg", "mpg", false); sMimeTypeMap.loadEntry("video/mpeg", "mpe", false); sMimeTypeMap.loadEntry("video/mp4", "mp4", false); + sMimeTypeMap.loadEntry("video/mpeg", "VOB", false); sMimeTypeMap.loadEntry("video/quicktime", "qt", false); sMimeTypeMap.loadEntry("video/quicktime", "mov", false); sMimeTypeMap.loadEntry("video/vnd.mpegurl", "mxu", false); diff --git a/core/java/android/webkit/TextDialog.java b/core/java/android/webkit/TextDialog.java index b7b40b1..9af30c5 100644 --- a/core/java/android/webkit/TextDialog.java +++ b/core/java/android/webkit/TextDialog.java @@ -33,7 +33,6 @@ import android.text.Selection; import android.text.Spannable; import android.text.TextPaint; import android.text.TextUtils; -import android.text.method.MetaKeyKeyListener; import android.text.method.MovementMethod; import android.text.method.PasswordTransformationMethod; import android.text.method.TextKeyListener; @@ -44,7 +43,6 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.View.MeasureSpec; import android.view.ViewConfiguration; import android.widget.AbsoluteLayout.LayoutParams; import android.widget.ArrayAdapter; @@ -69,10 +67,8 @@ import java.util.ArrayList; // on the enter key. The method for blocking unmatched key ups prevents // the shift key from working properly. private boolean mGotEnterDown; - // Determines whether we allow calls to requestRectangleOnScreen to - // propagate. We only want to scroll if the user is typing. If the - // user is simply navigating through a textfield, we do not want to - // scroll. + // mScrollToAccommodateCursor being set to false prevents us from scrolling + // the cursor on screen when using the trackball to select a textfield. private boolean mScrollToAccommodateCursor; private int mMaxLength; // Keep track of the text before the change so we know whether we actually @@ -183,6 +179,11 @@ import java.util.ArrayList; mWebView.shortPressOnTextField(); return true; } + // If we reached here, then this is a single line textfield, and + // the user pressed ENTER. In this case, we want to hide the + // soft input method. + InputMethodManager.getInstance(mContext) + .hideSoftInputFromWindow(getWindowToken(), 0); sendDomEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); sendDomEvent(event); } @@ -405,6 +406,12 @@ import java.util.ArrayList; //mWebView.setSelection(start, end); return true; } + // If the user is in a textfield, and the movement method is not + // handling the trackball events, it means they are at the end of the + // field and continuing to move the trackball. In this case, we should + // not scroll the cursor on screen bc the user may be attempting to + // scroll the page, possibly in the opposite direction of the cursor. + mScrollToAccommodateCursor = false; return false; } @@ -419,6 +426,17 @@ import java.util.ArrayList; mHandler.removeMessages(LONGPRESS); mWebView.removeView(this); mWebView.requestFocus(); + mScrollToAccommodateCursor = false; + } + + /* package */ void enableScrollOnScreen(boolean enable) { + mScrollToAccommodateCursor = enable; + } + + /* package */ void bringIntoView() { + if (getLayout() != null) { + bringPointIntoView(Selection.getSelectionEnd(getText())); + } } @Override @@ -437,8 +455,16 @@ import java.util.ArrayList; mWebView.passToJavaScript(getText().toString(), event); } + /** + * Always use this instead of setAdapter, as this has features specific to + * the TextDialog. + */ public void setAdapterCustom(AutoCompleteAdapter adapter) { - adapter.setTextView(this); + if (adapter != null) { + adapter.setTextView(this); + } else { + setInputType(EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE); + } super.setAdapter(adapter); } @@ -483,12 +509,12 @@ import java.util.ArrayList; PasswordTransformationMethod method; if (inPassword) { method = PasswordTransformationMethod.getInstance(); + setInputType(EditorInfo.TYPE_CLASS_TEXT|EditorInfo. + TYPE_TEXT_VARIATION_PASSWORD); } else { method = null; } setTransformationMethod(method); - setInputType(inPassword ? EditorInfo.TYPE_TEXT_VARIATION_PASSWORD : - EditorInfo.TYPE_CLASS_TEXT); } /* package */ void setMaxLength(int maxLength) { @@ -539,7 +565,6 @@ import java.util.ArrayList; // Set up a measure spec so a layout can always be recreated. mWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); mHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); - mScrollToAccommodateCursor = false; requestFocus(); } @@ -547,18 +572,23 @@ import java.util.ArrayList; * Set whether this is a single-line textfield or a multi-line textarea. * Textfields scroll horizontally, and do not handle the enter key. * Textareas behave oppositely. + * Do NOT call this after calling setInPassword(true). This will result in + * removing the password input type. */ public void setSingleLine(boolean single) { if (mSingle != single) { TextKeyListener.Capitalize cap; + int inputType = EditorInfo.TYPE_CLASS_TEXT; if (single) { cap = TextKeyListener.Capitalize.NONE; } else { cap = TextKeyListener.Capitalize.SENTENCES; + inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; } setKeyListener(TextKeyListener.getInstance(!single, cap)); mSingle = single; setHorizontallyScrolling(single); + setInputType(inputType); } } diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 1a7c4ff..5c470cf 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -316,10 +316,15 @@ public class WebSettings { buffer.append("en"); } - final String device = Build.DEVICE; - if (device.length() > 0) { + final String model = Build.MODEL; + if (model.length() > 0) { buffer.append("; "); - buffer.append(device); + buffer.append(model); + } + final String id = Build.ID; + if (id.length() > 0) { + buffer.append(" Build/"); + buffer.append(id); } final String base = mContext.getResources().getText( com.android.internal.R.string.web_user_agent).toString(); diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index cab278be..3306700 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -68,6 +68,8 @@ import android.widget.RelativeLayout; import android.widget.Scroller; import android.widget.Toast; import android.widget.ZoomControls; +import android.widget.ZoomRingController; +import android.widget.FrameLayout; import android.widget.AdapterView.OnItemClickListener; import java.io.File; @@ -107,17 +109,14 @@ public class WebView extends AbsoluteLayout static final boolean DEBUG = false; static final boolean LOGV_ENABLED = DEBUG ? Config.LOGD : Config.LOGV; - private class ExtendedZoomControls extends RelativeLayout { + private class ExtendedZoomControls extends FrameLayout { public ExtendedZoomControls(Context context, AttributeSet attrs) { super(context, attrs); - LayoutInflater inflater = (LayoutInflater) context - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater.inflate(com.android.internal.R.layout.zoom_magnify, this - , true); - mZoomControls = (ZoomControls) findViewById - (com.android.internal.R.id.zoomControls); - mZoomMagnify = (ImageView) findViewById - (com.android.internal.R.id.zoomMagnify); + LayoutInflater inflater = (LayoutInflater) + context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(com.android.internal.R.layout.zoom_magnify, this, true); + mZoomControls = (ZoomControls) findViewById(com.android.internal.R.id.zoomControls); + mZoomMagnify = (ImageView) findViewById(com.android.internal.R.id.zoomMagnify); } public void show(boolean showZoom, boolean canZoomOut) { @@ -258,6 +257,27 @@ public class WebView extends AbsoluteLayout // Whether to forward the touch events to WebCore private boolean mForwardTouchEvents = false; + // Whether we are in the drag tap mode, which exists starting at the second + // tap's down, through its move, and includes its up. These events should be + // given to the method on the zoom controller. + private boolean mInZoomTapDragMode; + + // The event time of the previous touch up. + private long mPreviousUpTime; + + private Runnable mRemoveReleaseSingleTap = new Runnable() { + public void run() { + mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP); + mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); + mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); + } + }; + + // Whether to prevent drag during touch. The initial value depends on + // mForwardTouchEvents. If WebCore wants touch events, we assume it will + // take control of touch events unless it says no for touch down event. + private boolean mPreventDrag; + // If updateTextEntry gets called while we are out of focus, use this // variable to remember to do it next time we gain focus. private boolean mNeedsUpdateTextEntry = false; @@ -269,8 +289,7 @@ public class WebView extends AbsoluteLayout * Customizable constant */ // pre-computed square of ViewConfiguration.getTouchSlop() - private static final int TOUCH_SLOP_SQUARE = - ViewConfiguration.getTouchSlop() * ViewConfiguration.getTouchSlop(); + private int mTouchSlopSquare; // This should be ViewConfiguration.getTapTimeout() // But system time out is 100ms, which is too short for the browser. // In the browser, if it switches out of tap too soon, jump tap won't work. @@ -360,6 +379,8 @@ public class WebView extends AbsoluteLayout static final int LONG_PRESS_ENTER = 23; static final int PREVENT_TOUCH_ID = 24; static final int WEBCORE_NEED_TOUCH_EVENTS = 25; + // obj=Rect in doc coordinates + static final int INVAL_RECT_MSG_ID = 26; // width which view is considered to be fully zoomed out static final int ZOOM_OUT_WIDTH = 1024; @@ -373,6 +394,9 @@ public class WebView extends AbsoluteLayout // initial scale in percent. 0 means using default. private int mInitialScale = 0; + // set to true temporarily while the zoom control is being dragged + private boolean mPreviewZoomOnly = false; + // computed scale and inverse, from mZoomWidth. private float mActualScale = 1; private float mInvActualScale = 1; @@ -490,7 +514,73 @@ public class WebView extends AbsoluteLayout return mExtra; } } + + private ZoomRingController mZoomRingController; + + private ZoomRingController.OnZoomListener mZoomListener = + new ZoomRingController.OnZoomListener() { + + public void onCenter(int x, int y) { + // Don't translate when the control is invoked, hence we do nothing + // in this callback + } + + public boolean onPan(int deltaX, int deltaY) { + return pinScrollBy(deltaX, deltaY, false, 0); + } + + public void onVisibilityChanged(boolean visible) { + if (visible) { + mZoomControls.show(false, canZoomScrollOut()); + } else { + mZoomControls.hide(); + } + } + + public void onBeginDrag(float startAngle) { + mPreviewZoomOnly = true; + } + + public void onEndDrag(float endAngle) { + mPreviewZoomOnly = false; + setNewZoomScale(mActualScale, true); + } + + public boolean onDragZoom(int deltaZoomLevel, int centerX, + int centerY, float startAngle, float curAngle) { + + if (mZoomScale == mMinZoomScale && deltaZoomLevel < 0 || + mZoomScale == mMaxZoomScale && deltaZoomLevel > 0 || + deltaZoomLevel == 0) { + return false; + } + + int deltaX = centerX - getViewWidth() / 2; + int deltaY = centerY - getViewHeight() / 2; + pinScrollBy(deltaX, deltaY, false, 0); + + while (deltaZoomLevel != 0) { + if (deltaZoomLevel > 0) { + if (!zoomIn()) return false; + deltaZoomLevel--; + } else { + if (!zoomOut()) return false; + deltaZoomLevel++; + } + } + + pinScrollBy(-deltaX, -deltaY, false, 0); + + return true; + } + + public void onSimpleZoom(boolean zoomIn) { + if (zoomIn) zoomIn(); + else zoomOut(); + } + }; + /** * Construct a new WebView with a Context object. * @param context A Context object used to access application assets. @@ -518,11 +608,6 @@ public class WebView extends AbsoluteLayout super(context, attrs, defStyle); init(); - TypedArray a = context.obtainStyledAttributes( - com.android.internal.R.styleable.View); - initializeScrollbars(a); - a.recycle(); - mCallbackProxy = new CallbackProxy(context, this); mWebViewCore = new WebViewCore(context, this, mCallbackProxy); mDatabase = WebViewDatabase.getInstance(context); @@ -532,6 +617,8 @@ public class WebView extends AbsoluteLayout mFocusData.mX = 0; mFocusData.mY = 0; mScroller = new Scroller(context); + mZoomRingController = new ZoomRingController(context, this); + mZoomRingController.setCallback(mZoomListener); } private void init() { @@ -541,9 +628,9 @@ public class WebView extends AbsoluteLayout setClickable(true); setLongClickable(true); - // should be conditional on if we're in the browser activity? - setHorizontalScrollBarEnabled(true); - setVerticalScrollBarEnabled(true); + final int slop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); + mTouchSlopSquare = slop * slop; + mMinLockSnapReverseDistance = slop; } /* package */ boolean onSavePassword(String schemePlusHost, String username, @@ -648,7 +735,7 @@ public class WebView extends AbsoluteLayout * to. */ private int getViewWidth() { - if (mOverlayVerticalScrollbar) { + if (!isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) { return getWidth(); } else { return getWidth() - getVerticalScrollbarWidth(); @@ -660,7 +747,7 @@ public class WebView extends AbsoluteLayout * to. */ private int getViewHeight() { - if (mOverlayHorizontalScrollbar) { + if (!isHorizontalScrollBarEnabled() || mOverlayHorizontalScrollbar) { return getHeight(); } else { return getHeight() - getHorizontalScrollbarHeight(); @@ -732,7 +819,6 @@ public class WebView extends AbsoluteLayout */ public void destroy() { clearTextEntry(); - getViewTreeObserver().removeOnGlobalFocusChangeListener(this); if (mWebViewCore != null) { // Set the handlers to null before destroying WebViewCore so no // more messages will be posted. @@ -1165,7 +1251,7 @@ public class WebView extends AbsoluteLayout nativeClearFocus(-1, -1); if (top) { // go to the top of the document - return pinScrollTo(mScrollX, 0, true); + return pinScrollTo(mScrollX, 0, true, 0); } // Page up int h = getHeight(); @@ -1176,7 +1262,7 @@ public class WebView extends AbsoluteLayout y = -h / 2; } mUserScroll = true; - return mScroller.isFinished() ? pinScrollBy(0, y, true) + return mScroller.isFinished() ? pinScrollBy(0, y, true, 0) : extendScroll(y); } @@ -1191,7 +1277,7 @@ public class WebView extends AbsoluteLayout } nativeClearFocus(-1, -1); if (bottom) { - return pinScrollTo(mScrollX, mContentHeight, true); + return pinScrollTo(mScrollX, mContentHeight, true, 0); } // Page down. int h = getHeight(); @@ -1202,7 +1288,7 @@ public class WebView extends AbsoluteLayout y = h / 2; } mUserScroll = true; - return mScroller.isFinished() ? pinScrollBy(0, y, true) + return mScroller.isFinished() ? pinScrollBy(0, y, true, 0) : extendScroll(y); } @@ -1485,12 +1571,14 @@ public class WebView extends AbsoluteLayout if (mDrawHistory) { // If history Picture is drawn, don't update scroll. They will // be updated when we get out of that mode. - if (scale != mActualScale) { + if (scale != mActualScale && !mPreviewZoomOnly) { mCallbackProxy.onScaleChanged(mActualScale, scale); } mActualScale = scale; mInvActualScale = 1 / scale; - sendViewSizeZoom(); + if (!mPreviewZoomOnly) { + sendViewSizeZoom(); + } } else { // update our scroll so we don't appear to jump // i.e. keep the center of the doc in the center of the view @@ -1502,7 +1590,7 @@ public class WebView extends AbsoluteLayout float sy = ratio * oldY + (ratio - 1) * getViewHeight() * 0.5f; // now update our new scale and inverse - if (scale != mActualScale) { + if (scale != mActualScale && !mPreviewZoomOnly) { mCallbackProxy.onScaleChanged(mActualScale, scale); } mActualScale = scale; @@ -1514,8 +1602,10 @@ public class WebView extends AbsoluteLayout mScrollX = pinLocX(Math.round(sx)); mScrollY = pinLocY(Math.round(sy)); - sendViewSizeZoom(); - sendOurVisibleRect(); + if (!mPreviewZoomOnly) { + sendViewSizeZoom(); + sendOurVisibleRect(); + } } } } @@ -1716,8 +1806,8 @@ public class WebView extends AbsoluteLayout */ public void clearFormData() { if (inEditingMode()) { - ArrayAdapter<String> adapter = null; - mTextEntry.setAdapter(adapter); + AutoCompleteAdapter adapter = null; + mTextEntry.setAdapterCustom(adapter); } } @@ -1812,7 +1902,7 @@ public class WebView extends AbsoluteLayout nativeSetFindIsDown(); // Now that the dialog has been removed, ensure that we scroll to a // location that is not beyond the end of the page. - pinScrollTo(mScrollX, mScrollY, false); + pinScrollTo(mScrollX, mScrollY, false, 0); invalidate(); } @@ -1855,13 +1945,13 @@ public class WebView extends AbsoluteLayout // helper to pin the scrollBy parameters (already in view coordinates) // returns true if the scroll was changed - private boolean pinScrollBy(int dx, int dy, boolean animate) { - return pinScrollTo(mScrollX + dx, mScrollY + dy, animate); + private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) { + return pinScrollTo(mScrollX + dx, mScrollY + dy, animate, animationDuration); } // helper to pin the scrollTo parameters (already in view coordinates) // returns true if the scroll was changed - private boolean pinScrollTo(int x, int y, boolean animate) { + private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) { x = pinLocX(x); y = pinLocY(y); int dx = x - mScrollX; @@ -1875,7 +1965,7 @@ public class WebView extends AbsoluteLayout // Log.d(LOGTAG, "startScroll: " + dx + " " + dy); mScroller.startScroll(mScrollX, mScrollY, dx, dy, - computeDuration(dx, dy)); + animationDuration > 0 ? animationDuration : computeDuration(dx, dy)); invalidate(); } else { mScroller.abortAnimation(); // just in case @@ -1910,10 +2000,10 @@ public class WebView extends AbsoluteLayout // vertical scroll? // Log.d(LOGTAG, "setContentScrollBy cy=" + cy); if (cy == 0 && cx != 0) { - pinScrollBy(cx, 0, true); + pinScrollBy(cx, 0, true, 0); } } else { - pinScrollBy(cx, cy, true); + pinScrollBy(cx, cy, true, 0); } } @@ -1933,7 +2023,7 @@ public class WebView extends AbsoluteLayout int vy = contentToView(cy); // Log.d(LOGTAG, "content scrollTo [" + cx + " " + cy + "] view=[" + // vx + " " + vy + "]"); - pinScrollTo(vx, vy, false); + pinScrollTo(vx, vy, false, 0); if (mScrollX != vx || mScrollY != vy) { return true; } else { @@ -1950,7 +2040,7 @@ public class WebView extends AbsoluteLayout } int vx = contentToView(cx); int vy = contentToView(cy); - pinScrollTo(vx, vy, true); + pinScrollTo(vx, vy, true, 0); } /** @@ -2692,12 +2782,9 @@ public class WebView extends AbsoluteLayout InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(mTextEntry, 0); + mTextEntry.enableScrollOnScreen(true); } - // Used to register the global focus change listener one time to avoid - // multiple references to WebView - private boolean mGlobalFocusChangeListenerAdded; - private void updateTextEntry() { if (mTextEntry == null) { mTextEntry = new TextDialog(mContext, WebView.this); @@ -2730,9 +2817,6 @@ public class WebView extends AbsoluteLayout // Note that sendOurVisibleRect calls viewToContent, so the coordinates // should be in content coordinates. if (!Rect.intersects(node.mBounds, visibleRect)) { - if (alreadyThere) { - mTextEntry.remove(); - } // Node is not on screen, so do not bother. return; } @@ -2785,21 +2869,32 @@ public class WebView extends AbsoluteLayout } } mTextEntry.setMaxLength(maxLength); - ArrayAdapter<String> adapter = null; - mTextEntry.setAdapter(adapter); - mTextEntry.setInPassword(node.mIsPassword); + AutoCompleteAdapter adapter = null; + mTextEntry.setAdapterCustom(adapter); mTextEntry.setSingleLine(node.mIsTextField); + mTextEntry.setInPassword(node.mIsPassword); if (null == text) { mTextEntry.setText("", 0, 0); } else { - mTextEntry.setText(text, 0, text.length()); + // Change to true to enable the old style behavior, where + // entering a textfield/textarea always set the selection to the + // whole field. This was desirable for the case where the user + // intends to scroll past the field using the trackball. + // However, it causes a problem when replying to emails - the + // user expects the cursor to be at the beginning of the + // textarea. Testing out a new behavior, where textfields set + // selection at the end, and textareas at the beginning. + if (false) { + mTextEntry.setText(text, 0, text.length()); + } else if (node.mIsTextField) { + int length = text.length(); + mTextEntry.setText(text, length, length); + } else { + mTextEntry.setText(text, 0, 0); + } } mTextEntry.requestFocus(); } - if (!mGlobalFocusChangeListenerAdded) { - getViewTreeObserver().addOnGlobalFocusChangeListener(this); - mGlobalFocusChangeListenerAdded = true; - } } private class UpdateTextEntryAdapter implements Runnable { @@ -3114,24 +3209,17 @@ public class WebView extends AbsoluteLayout // Implementation for OnHierarchyChangeListener public void onChildViewAdded(View parent, View child) {} - // When we are removed, remove this as a global focus change listener. public void onChildViewRemoved(View p, View child) { if (child == this) { - p.getViewTreeObserver().removeOnGlobalFocusChangeListener(this); - mGlobalFocusChangeListenerAdded = false; + if (inEditingMode()) { + clearTextEntry(); + mNeedsUpdateTextEntry = true; + } } } - - // Use this to know when the textview has lost focus, and something other - // than the webview has gained focus. Stop drawing the focus ring, remove - // the TextView, and set a flag to put it back when we regain focus. + + @Deprecated public void onGlobalFocusChanged(View oldFocus, View newFocus) { - if (oldFocus == mTextEntry && newFocus != this) { - mDrawFocusRing = false; - mTextEntry.updateCachedTextfield(); - removeView(mTextEntry); - mNeedsUpdateTextEntry = true; - } } // To avoid drawing the focus ring, and remove the TextView when our window @@ -3153,16 +3241,10 @@ public class WebView extends AbsoluteLayout mDrawFocusRing = false; } } else { - // If our window has lost focus, stop drawing the focus ring, and - // remove the TextView if displayed, and flag it to be added when - // we regain focus. + // If our window has lost focus, stop drawing the focus ring mDrawFocusRing = false; mGotKeyDown = false; mShiftIsPressed = false; - if (inEditingMode()) { - clearTextEntry(); - mNeedsUpdateTextEntry = true; - } } invalidate(); super.onWindowFocusChanged(hasWindowFocus); @@ -3245,9 +3327,8 @@ public class WebView extends AbsoluteLayout // 3. If there is a same direction back and forth, lock it. // adjustable parameters + private int mMinLockSnapReverseDistance; private static final float MAX_SLOPE_FOR_DIAG = 1.5f; - private static final int MIN_LOCK_SNAP_REVERSE_DISTANCE = - ViewConfiguration.getTouchSlop(); private static final int MIN_BREAK_SNAP_CROSS_DISTANCE = 80; @Override @@ -3261,6 +3342,27 @@ public class WebView extends AbsoluteLayout + mTouchMode); } + if (mZoomRingController.isVisible()) { + if (mInZoomTapDragMode) { + mZoomRingController.handleDoubleTapEvent(ev); + if (ev.getAction() == MotionEvent.ACTION_UP) { + // Just released the second tap, no longer in tap-drag mode + mInZoomTapDragMode = false; + } + return true; + } else { + // TODO: properly do this. + /* + * When the zoom widget is showing, the user can tap outside of + * it to dismiss it. Furthermore, he can drag outside of it to + * pan the browser. However, we do not want a tap on a link to + * open the link. + */ + post(mRemoveReleaseSingleTap); + // Continue through to normal processing + } + } + int action = ev.getAction(); float x = ev.getX(); float y = ev.getY(); @@ -3315,8 +3417,20 @@ public class WebView extends AbsoluteLayout nativeMoveSelection(viewToContent(mSelectX) , viewToContent(mSelectY), false); mTouchSelection = mExtendSelection = true; + } else if (!ZoomRingController.useOldZoom(mContext) && + eventTime - mPreviousUpTime < DOUBLE_TAP_TIMEOUT && + getSettings().supportZoom() && + mMinZoomScale < mMaxZoomScale) { + // Found doubletap, invoke the zoom controller + mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); + mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); + mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP); + mZoomRingController.setVisible(true); + mInZoomTapDragMode = true; + mZoomRingController.handleDoubleTapEvent(ev); } else { mTouchMode = TOUCH_INIT_MODE; + mPreventDrag = mForwardTouchEvents; } if (mTouchMode == TOUCH_INIT_MODE) { mPrivateHandler.sendMessageDelayed(mPrivateHandler @@ -3359,8 +3473,8 @@ public class WebView extends AbsoluteLayout invalidate(); break; } - if ((deltaX * deltaX + deltaY * deltaY) - < TOUCH_SLOP_SQUARE) { + if (mPreventDrag || (deltaX * deltaX + deltaY * deltaY) + < mTouchSlopSquare) { break; } @@ -3371,6 +3485,9 @@ public class WebView extends AbsoluteLayout mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); } + // Prevent double-tap from being invoked + mPreviousUpTime = 0; + // if it starts nearly horizontal or vertical, enforce it int ax = Math.abs(deltaX); int ay = Math.abs(deltaY); @@ -3418,9 +3535,9 @@ public class WebView extends AbsoluteLayout // reverse direction means lock in the snap mode if ((ax > MAX_SLOPE_FOR_DIAG * ay) && ((mSnapPositive && - deltaX < -MIN_LOCK_SNAP_REVERSE_DISTANCE) + deltaX < -mMinLockSnapReverseDistance) || (!mSnapPositive && - deltaX > MIN_LOCK_SNAP_REVERSE_DISTANCE))) { + deltaX > mMinLockSnapReverseDistance))) { mSnapScrollMode = SNAP_X_LOCK; } } else { @@ -3432,9 +3549,9 @@ public class WebView extends AbsoluteLayout // reverse direction means lock in the snap mode if ((ay > MAX_SLOPE_FOR_DIAG * ax) && ((mSnapPositive && - deltaY < -MIN_LOCK_SNAP_REVERSE_DISTANCE) + deltaY < -mMinLockSnapReverseDistance) || (!mSnapPositive && - deltaY > MIN_LOCK_SNAP_REVERSE_DISTANCE))) { + deltaY > mMinLockSnapReverseDistance))) { mSnapScrollMode = SNAP_Y_LOCK; } } @@ -3457,16 +3574,18 @@ public class WebView extends AbsoluteLayout mUserScroll = true; } - boolean showPlusMinus = mMinZoomScale < mMaxZoomScale; - boolean showMagnify = canZoomScrollOut(); - if (mZoomControls != null && (showPlusMinus || showMagnify)) { - if (mZoomControls.getVisibility() == View.VISIBLE) { - mPrivateHandler.removeCallbacks(mZoomControlRunnable); - } else { - mZoomControls.show(showPlusMinus, showMagnify); + if (ZoomRingController.useOldZoom(mContext)) { + boolean showPlusMinus = mMinZoomScale < mMaxZoomScale; + boolean showMagnify = canZoomScrollOut(); + if (mZoomControls != null && (showPlusMinus || showMagnify)) { + if (mZoomControls.getVisibility() == View.VISIBLE) { + mPrivateHandler.removeCallbacks(mZoomControlRunnable); + } else { + mZoomControls.show(showPlusMinus, showMagnify); + } + mPrivateHandler.postDelayed(mZoomControlRunnable, + ZOOM_CONTROLS_TIMEOUT); } - mPrivateHandler.postDelayed(mZoomControlRunnable, - ZOOM_CONTROLS_TIMEOUT); } if (done) { // return false to indicate that we can't pan out of the @@ -3479,7 +3598,8 @@ public class WebView extends AbsoluteLayout switch (mTouchMode) { case TOUCH_INIT_MODE: // tap mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); - if (getSettings().supportZoom()) { + if (getSettings().supportZoom() + && (mMinZoomScale < mMaxZoomScale)) { mPrivateHandler.sendMessageDelayed(mPrivateHandler .obtainMessage(RELEASE_SINGLE_TAP), DOUBLE_TAP_TIMEOUT); @@ -3559,6 +3679,7 @@ public class WebView extends AbsoluteLayout mVelocityTracker.recycle(); mVelocityTracker = null; } + mPreviousUpTime = eventTime; break; } case MotionEvent.ACTION_CANCEL: { @@ -3604,6 +3725,7 @@ public class WebView extends AbsoluteLayout private static final int TRACKBALL_WAIT = 100; private static final int TRACKBALL_SCALE = 400; private static final int TRACKBALL_SCROLL_COUNT = 5; + private static final int TRACKBALL_MOVE_COUNT = 10; private static final int TRACKBALL_MULTIPLIER = 3; private static final int SELECT_CURSOR_OFFSET = 16; private int mSelectX = 0; @@ -3658,7 +3780,11 @@ public class WebView extends AbsoluteLayout mTrackballDown = false; mTrackballUpTime = time; if (mShiftIsPressed) { - mExtendSelection = true; + if (mExtendSelection) { + commitCopy(); + } else { + mExtendSelection = true; + } } if (LOGV_ENABLED) { Log.v(LOGTAG, "onTrackballEvent up ev=" + ev @@ -3735,7 +3861,7 @@ public class WebView extends AbsoluteLayout int scrollY = mSelectY < mScrollY ? -SELECT_CURSOR_OFFSET : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET : 0; - pinScrollBy(scrollX, scrollY, true); + pinScrollBy(scrollX, scrollY, true, 0); Rect select = new Rect(mSelectX, mSelectY, mSelectX + 1, mSelectY + 1); requestRectangleOnScreen(select); invalidate(); @@ -3841,6 +3967,7 @@ public class WebView extends AbsoluteLayout KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN : mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT : KeyEvent.KEYCODE_DPAD_RIGHT; + count = Math.min(count, TRACKBALL_MOVE_COUNT); if (LOGV_ENABLED) { Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode + " count=" + count @@ -3870,7 +3997,7 @@ public class WebView extends AbsoluteLayout yMove = 0; } if (xMove != 0 || yMove != 0) { - pinScrollBy(xMove, yMove, true); + pinScrollBy(xMove, yMove, true, 0); } mUserScroll = true; } @@ -4056,7 +4183,7 @@ public class WebView extends AbsoluteLayout View v = mTextEntry; int x = viewToContent((v.getLeft() + v.getRight()) >> 1); int y = viewToContent((v.getTop() + v.getBottom()) >> 1); - int contentSize = ViewConfiguration.getTouchSlop(); + int contentSize = ViewConfiguration.get(getContext()).getScaledTouchSlop(); nativeMotionUp(x, y, contentSize, true); } } @@ -4066,21 +4193,15 @@ public class WebView extends AbsoluteLayout return; } switchOutDrawHistory(); - // call uiOverride to check whether it is a special node, - // phone/email/address, which are not handled by WebKit + // FIXME: we don't know if the current (x,y) is on a focus node or + // not -- so playing the sound effect here is premature if (nativeUpdateFocusNode()) { - FocusNode node = mFocusNode; - if (!node.mIsTextField && !node.mIsTextArea) { - if (mCallbackProxy.uiOverrideUrlLoading(node.mText)) { - return; - } - } playSoundEffect(SoundEffectConstants.CLICK); } // mLastTouchX and mLastTouchY are the point in the current viewport int contentX = viewToContent((int) mLastTouchX + mScrollX); int contentY = viewToContent((int) mLastTouchY + mScrollY); - int contentSize = ViewConfiguration.getTouchSlop(); + int contentSize = ViewConfiguration.get(getContext()).getScaledTouchSlop(); nativeMotionUp(contentX, contentY, contentSize, true); } @@ -4208,7 +4329,7 @@ public class WebView extends AbsoluteLayout } if ((scrollYDelta | scrollXDelta) != 0) { - return pinScrollBy(scrollXDelta, scrollYDelta, !immediate); + return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0); } return false; @@ -4443,6 +4564,15 @@ public class WebView extends AbsoluteLayout } break; case UPDATE_TEXT_ENTRY_MSG_ID: + // this is sent after finishing resize in WebViewCore. Make + // sure the text edit box is still on the screen. + boolean alreadyThere = inEditingMode(); + if (alreadyThere && nativeUpdateFocusNode()) { + FocusNode node = mFocusNode; + if (node.mIsTextField || node.mIsTextArea) { + mTextEntry.bringIntoView(); + } + } updateTextEntry(); break; case RECOMPUTE_FOCUS_MSG_ID: @@ -4450,6 +4580,17 @@ public class WebView extends AbsoluteLayout nativeRecomputeFocus(); } break; + case INVAL_RECT_MSG_ID: { + Rect r = (Rect)msg.obj; + if (r == null) { + invalidate(); + } else { + // we need to scale r from content into view coords, + // which viewInvalidate() does for us + viewInvalidate(r.left, r.top, r.right, r.bottom); + } + break; + } case UPDATE_TEXT_ENTRY_ADAPTER: HashMap data = (HashMap) msg.obj; if (mTextEntry.isSameTextField(msg.arg1)) { @@ -4494,12 +4635,12 @@ public class WebView extends AbsoluteLayout break; case PREVENT_TOUCH_ID: - // update may have already been paused by touch; restore since - // this effectively aborts touch and skips logic in touch up - if (mTouchMode == TOUCH_DRAG_MODE) { - WebViewCore.resumeUpdate(mWebViewCore); + if (msg.arg1 == MotionEvent.ACTION_DOWN) { + mPreventDrag = msg.arg2 == 1; + if (mPreventDrag) { + mTouchMode = TOUCH_DONE_MODE; + } } - mTouchMode = TOUCH_DONE_MODE; break; default: @@ -4665,6 +4806,12 @@ public class WebView extends AbsoluteLayout listView.setSelection(mSelection); } } + dialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + mWebViewCore.sendMessage( + EventHub.SINGLE_LISTBOX_CHOICE, -2, 0); + } + }); dialog.show(); } } @@ -4809,11 +4956,11 @@ public class WebView extends AbsoluteLayout // FIXME: Necessary because ScrollView/ListView do not scroll left/right int maxH = Math.min(viewFocus.right - visRect.right, maxXScroll); if (maxH > 0) { - pinScrollBy(maxH, 0, true); + pinScrollBy(maxH, 0, true, 0); } else { maxH = Math.max(viewFocus.left - visRect.left, -maxXScroll); if (maxH < 0) { - pinScrollBy(maxH, 0, true); + pinScrollBy(maxH, 0, true, 0); } } if (mLastFocusBounds.isEmpty()) return keyHandled; diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 323b44d..8f78887 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -799,12 +799,11 @@ final class WebViewCore { case TOUCH_EVENT: { TouchEventData ted = (TouchEventData) msg.obj; - if (nativeHandleTouchEvent(ted.mAction, ted.mX, - ted.mY)) { - Message.obtain(mWebView.mPrivateHandler, - WebView.PREVENT_TOUCH_ID) - .sendToTarget(); - } + Message.obtain( + mWebView.mPrivateHandler, + WebView.PREVENT_TOUCH_ID, ted.mAction, + nativeHandleTouchEvent(ted.mAction, ted.mX, + ted.mY) ? 1 : 0).sendToTarget(); break; } @@ -1434,10 +1433,15 @@ final class WebViewCore { } } - // called by JNI + /* Called by JNI. The coordinates are in doc coordinates, so they need to + be scaled before they can be used by the view system, which happens + in WebView since it (and its thread) know the current scale factor. + */ private void sendViewInvalidate(int left, int top, int right, int bottom) { if (mWebView != null) { - mWebView.postInvalidate(left, top, right, bottom); + Message.obtain(mWebView.mPrivateHandler, + WebView.INVAL_RECT_MSG_ID, + new Rect(left, top, right, bottom)).sendToTarget(); } } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 19ec77d..378d218 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -369,7 +369,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te private int mLastTouchMode = TOUCH_MODE_UNKNOWN; - // TODO: REMOVE WHEN WE'RE DONE WITH PROFILING private static final boolean PROFILE_SCROLLING = false; private boolean mScrollProfilingStarted = false; @@ -423,6 +422,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te */ private FastScroller mFastScroller; + private int mTouchSlop; + /** * Interface definition for a callback to be invoked when the list or grid * has been scrolled. @@ -558,6 +559,15 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te public boolean isFastScrollEnabled() { return mFastScrollEnabled; } + + /** + * If fast scroll is visible, then don't draw the vertical scrollbar. + * @hide + */ + @Override + protected boolean isVerticalScrollBarHidden() { + return mFastScroller != null ? mFastScroller.isVisible() : false; + } /** * When smooth scrollbar is enabled, the position and size of the scrollbar thumb @@ -696,6 +706,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te setWillNotDraw(false); setAlwaysDrawnWithCacheEnabled(false); setScrollingCacheEnabled(true); + + mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); } private void useDefaultSelector() { @@ -1758,8 +1770,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // Check if we have moved far enough that it looks more like a // scroll than a tap final int distance = Math.abs(deltaY); - int touchSlop = ViewConfiguration.getTouchSlop(); - if (distance > touchSlop) { + if (distance > mTouchSlop) { createScrollingCache(); mTouchMode = TOUCH_MODE_SCROLL; mMotionCorrection = deltaY; @@ -1979,8 +1990,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te velocityTracker.computeCurrentVelocity(1000); int initialVelocity = (int)velocityTracker.getYVelocity(); - if ((Math.abs(initialVelocity) > ViewConfiguration.getMinimumFlingVelocity()) && - (getChildCount() > 0)){ + if ((Math.abs(initialVelocity) > + ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) && + (getChildCount() > 0)) { if (mFlingRunnable == null) { mFlingRunnable = new FlingRunnable(); } @@ -2714,7 +2726,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te int screenHeight = WindowManagerImpl.getDefault().getDefaultDisplay().getHeight(); final int[] xy = new int[2]; getLocationOnScreen(xy); - int bottomGap = screenHeight - xy[1] - getHeight() + 20; + // TODO: The 20 below should come from the theme and be expressed in dip + final float scale = getContext().getResources().getDisplayMetrics().density; + int bottomGap = screenHeight - xy[1] - getHeight() + (int) (scale * 20); mPopup.showAtLocation(this, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, xy[0], bottomGap); // Make sure we get focus if we are showing the popup diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java index fbb0105..cf9c588 100644 --- a/core/java/android/widget/AnalogClock.java +++ b/core/java/android/widget/AnalogClock.java @@ -28,6 +28,7 @@ import android.os.Handler; import android.text.format.Time; import android.util.AttributeSet; import android.view.View; +import android.widget.RemoteViews.RemoteView; import java.util.TimeZone; @@ -35,6 +36,7 @@ import java.util.TimeZone; * This widget display an analogic clock with two hands for hours and * minutes. */ +@RemoteView public class AnalogClock extends View { private Time mCalendar; diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index 7d52901..7a51676 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -110,8 +110,14 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe private Validator mValidator = null; + private boolean mBlockCompletion; + private AutoCompleteTextView.ListSelectorHider mHideSelector; + // Indicates whether this AutoCompleteTextView is attached to a window or not + // The widget is attached to a window when mAttachCount > 0 + private int mAttachCount; + public AutoCompleteTextView(Context context) { this(context, null); } @@ -145,19 +151,13 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe mHintResource = a.getResourceId(R.styleable.AutoCompleteTextView_completionHintView, R.layout.simple_dropdown_hint); - // A little trickiness for backwards compatibility: if the app - // didn't specify an explicit content type, then we will fill in the - // auto complete flag for them. - int contentType = a.getInt( - R.styleable.AutoCompleteTextView_inputType, - EditorInfo.TYPE_NULL); - if (contentType == EditorInfo.TYPE_NULL) { - contentType = getInputType(); - if ((contentType&EditorInfo.TYPE_MASK_CLASS) - == EditorInfo.TYPE_CLASS_TEXT) { - contentType |= EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE; - setRawInputType(contentType); - } + // Always turn on the auto complete input type flag, since it + // makes no sense to use this widget without it. + int inputType = getInputType(); + if ((inputType&EditorInfo.TYPE_MASK_CLASS) + == EditorInfo.TYPE_CLASS_TEXT) { + inputType |= EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE; + setRawInputType(inputType); } a.recycle(); @@ -300,6 +300,15 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe /** * <p>Changes the list of data used for auto completion. The provided list * must be a filterable list adapter.</p> + * + * <p>The caller is still responsible for managing any resources used by the adapter. + * Notably, when the AutoCompleteTextView is closed or released, the adapter is not notified. + * A common case is the use of {@link android.widget.CursorAdapter}, which + * contains a {@link android.database.Cursor} that must be closed. This can be done + * automatically (see + * {@link android.app.Activity#startManagingCursor(android.database.Cursor) + * startManagingCursor()}), + * or by manually closing the cursor when the AutoCompleteTextView is dismissed.</p> * * @param adapter the adapter holding the auto completion data * @@ -368,7 +377,10 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe && keyCode != KeyEvent.KEYCODE_DPAD_CENTER))) { int curIndex = mDropDownList.getSelectedItemPosition(); boolean consumed; - if (keyCode == KeyEvent.KEYCODE_DPAD_UP && curIndex <= 0) { + final boolean below = !mPopup.isAboveAnchor(); + if ((below && keyCode == KeyEvent.KEYCODE_DPAD_UP && curIndex <= 0) || + (!below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN && curIndex >= + mDropDownList.getAdapter().getCount() - 1)) { // When the selection is at the top, we block the key // event to prevent focus from moving. mDropDownList.hideSelector(); @@ -409,13 +421,15 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe return true; } } else { - if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { + if (below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { // when the selection is at the bottom, we block the // event to avoid going to the next focusable widget Adapter adapter = mDropDownList.getAdapter(); if (adapter != null && curIndex == adapter.getCount() - 1) { return true; } + } else if (!below && keyCode == KeyEvent.KEYCODE_DPAD_UP && curIndex == 0) { + return true; } } } @@ -462,6 +476,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } void doBeforeTextChanged() { + if (mBlockCompletion) return; + // when text is changed, inserted or deleted, we attempt to show // the drop down mOpenBefore = isPopupShowing(); @@ -469,6 +485,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } void doAfterTextChanged() { + if (mBlockCompletion) return; + // if the list was open before the keystroke, but closed afterwards, // then something in the keystroke processing (an input filter perhaps) // called performCompletion() and we shouldn't do any more processing. @@ -579,9 +597,13 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe performCompletion(null, -1, -1); } - @Override public void onCommitCompletion(CompletionInfo completion) { + @Override + public void onCommitCompletion(CompletionInfo completion) { if (isPopupShowing()) { + mBlockCompletion = true; replaceText(completion.getText()); + mBlockCompletion = false; + if (mItemClickListener != null) { final DropDownListView list = mDropDownList; // Note that we don't have a View here, so we will need to @@ -604,7 +626,10 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe Log.w(TAG, "performCompletion: no selected item"); return; } + + mBlockCompletion = true; replaceText(convertSelectionToString(selectedItem)); + mBlockCompletion = false; if (mItemClickListener != null) { final DropDownListView list = mDropDownList; @@ -620,6 +645,14 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe dismissDropDown(); } + + /** + * Identifies whether the view is currently performing a text completion, so subclasses + * can decide whether to respond to text changed events. + */ + public boolean isPerformingCompletion() { + return mBlockCompletion; + } /** * <p>Performs the text completion by replacing the current text by the @@ -636,6 +669,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } public void onFilterComplete(int count) { + if (mAttachCount <= 0) return; + /* * This checks enoughToFilter() again because filtering requests * are asynchronous, so the result may come back after enough text @@ -671,8 +706,15 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mAttachCount++; + } + + @Override protected void onDetachedFromWindow() { dismissDropDown(); + mAttachCount--; super.onDetachedFromWindow(); } @@ -694,7 +736,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe boolean result = super.setFrame(l, t, r, b); if (mPopup.isShowing()) { - mPopup.update(this, getMeasuredWidth() - mPaddingLeft - mPaddingRight, -1); + mPopup.update(this, r - l, -1); } return result; @@ -707,10 +749,10 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe int height = buildDropDown(); if (mPopup.isShowing()) { mPopup.update(this, mDropDownHorizontalOffset, mDropDownVerticalOffset, - getMeasuredWidth() - mPaddingLeft - mPaddingRight, height); + getWidth(), height); } else { mPopup.setWindowLayoutMode(0, ViewGroup.LayoutParams.WRAP_CONTENT); - mPopup.setWidth(getMeasuredWidth() - mPaddingLeft - mPaddingRight); + mPopup.setWidth(getWidth()); mPopup.setHeight(height); mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); mPopup.setOutsideTouchable(true); @@ -739,7 +781,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe int N = mAdapter.getCount(); if (N > 20) N = 20; CompletionInfo[] completions = new CompletionInfo[N]; - for (int i=0; i<N; i++) { + for (int i = 0; i < N; i++) { Object item = mAdapter.getItem(i); long id = mAdapter.getItemId(i); completions[i] = new CompletionInfo(id, i, @@ -783,7 +825,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe // measure the hint's height to find how much more vertical space // we need to add to the drop down's height - int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST); + int widthSpec = MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST); int heightSpec = MeasureSpec.UNSPECIFIED; hintView.measure(widthSpec, heightSpec); @@ -807,8 +849,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } // Max height available on the screen for a popup anchored to us - final int maxHeight = mPopup.getMaxAvailableHeight(this); - otherHeights += dropDownView.getPaddingTop() + dropDownView.getPaddingBottom(); + final int maxHeight = mPopup.getMaxAvailableHeight(this, mDropDownVerticalOffset); + //otherHeights += dropDownView.getPaddingTop() + dropDownView.getPaddingBottom(); return mDropDownList.measureHeightOfChildren(MeasureSpec.UNSPECIFIED, 0, ListView.NO_POSITION, maxHeight - otherHeights, 2) + otherHeights; diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index e56a741..d4482dc 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -251,7 +251,12 @@ public abstract class CompoundButton extends Button implements Checkable { invalidate(); } } - + + @Override + protected boolean verifyDrawable(Drawable who) { + return super.verifyDrawable(who) || who == mButtonDrawable; + } + static class SavedState extends BaseSavedState { boolean checked; diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java index 3de561a..0fc8f49 100644 --- a/core/java/android/widget/ExpandableListView.java +++ b/core/java/android/widget/ExpandableListView.java @@ -187,6 +187,9 @@ public class ExpandableListView extends ListView { private Drawable mChildDivider; private boolean mClipChildDivider; + // Bounds of the indicator to be drawn + private final Rect mIndicatorRect = new Rect(); + public ExpandableListView(Context context) { this(context, null); } @@ -247,17 +250,16 @@ public class ExpandableListView extends ListView { final int myB = mBottom; - PositionMetadata pos = null; + PositionMetadata pos; View item; Drawable indicator; int t, b; // Start at a value that is neither child nor group int lastItemType = ~(ExpandableListPosition.CHILD | ExpandableListPosition.GROUP); - - // Bounds of the indicator to be drawn - Rect indicatorRect = new Rect(); - + + final Rect indicatorRect = mIndicatorRect; + // The "child" mentioned in the following two lines is this // View's child, not referring to an expandable list's // notion of a child (as opposed to a group) @@ -303,11 +305,11 @@ public class ExpandableListView extends ListView { // Use item's full height + the divider height if (mStackFromBottom) { // See ListView#dispatchDraw - indicatorRect.top = t - mDividerHeight; + indicatorRect.top = t;// - mDividerHeight; indicatorRect.bottom = b; } else { indicatorRect.top = t; - indicatorRect.bottom = b + mDividerHeight; + indicatorRect.bottom = b;// + mDividerHeight; } // Get the indicator (with its state set to the item's state) diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java index bdcfeef..57e21e4 100644 --- a/core/java/android/widget/FastScroller.java +++ b/core/java/android/widget/FastScroller.java @@ -34,7 +34,8 @@ import android.view.MotionEvent; */ class FastScroller { - + // Minimum number of pages to justify showing a fast scroll thumb + private static int MIN_PAGES = 4; // Scroll thumb not showing private static final int STATE_NONE = 0; // Not implemented yet - fade-in transition @@ -154,6 +155,10 @@ class FastScroller { setState(STATE_NONE); } + boolean isVisible() { + return !(mState == STATE_NONE); + } + public void draw(Canvas canvas) { if (mState == STATE_NONE) { @@ -214,7 +219,13 @@ class FastScroller { void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { - + // Are there enough pages to require fast scroll? + if (visibleItemCount > 0 && totalItemCount / visibleItemCount < MIN_PAGES) { + if (mState != STATE_NONE) { + setState(STATE_NONE); + } + return; + } if (totalItemCount - visibleItemCount > 0 && mState != STATE_DRAGGING ) { mThumbY = ((mList.getHeight() - mThumbH) * firstVisibleItem) / (totalItemCount - visibleItemCount); diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java index 7b9735c..ffabb02 100644 --- a/core/java/android/widget/Gallery.java +++ b/core/java/android/widget/Gallery.java @@ -180,7 +180,7 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList public Gallery(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - mGestureDetector = new GestureDetector(this); + mGestureDetector = new GestureDetector(context, this); mGestureDetector.setIsLongpressEnabled(true); TypedArray a = context.obtainStyledAttributes( diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java new file mode 100644 index 0000000..96fe595 --- /dev/null +++ b/core/java/android/widget/HorizontalScrollView.java @@ -0,0 +1,1197 @@ +/* + * 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. + */ + +package android.widget; + +import android.util.AttributeSet; +import android.graphics.Rect; +import android.view.View; +import android.view.VelocityTracker; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.KeyEvent; +import android.view.FocusFinder; +import android.view.MotionEvent; +import android.view.ViewParent; +import android.view.animation.AnimationUtils; +import android.content.Context; +import android.content.res.TypedArray; + +import java.util.List; + +/** + * Layout container for a view hierarchy that can be scrolled by the user, + * allowing it to be larger than the physical display. A HorizontalScrollView + * is a {@link FrameLayout}, meaning you should place one child in it + * containing the entire contents to scroll; this child may itself be a layout + * manager with a complex hierarchy of objects. A child that is often used + * is a {@link LinearLayout} in a horizontal orientation, presenting a horizontal + * array of top-level items that the user can scroll through. + * + * <p>You should never use a HorizontalScrollView with a {@link ListView}, since + * ListView takes care of its own scrolling. Most importantly, doing this + * defeats all of the important optimizations in ListView for dealing with + * large lists, since it effectively forces the ListView to display its entire + * list of items to fill up the infinite container supplied by HorizontalScrollView. + * + * <p>The {@link TextView} class also + * takes care of its own scrolling, so does not require a ScrollView, but + * using the two together is possible to achieve the effect of a text view + * within a larger container. + * + * <p>HorizontalScrollView only supports horizontal scrolling. + */ +public class HorizontalScrollView extends FrameLayout { + private static final int ANIMATED_SCROLL_GAP = ScrollView.ANIMATED_SCROLL_GAP; + + private static final float MAX_SCROLL_FACTOR = ScrollView.MAX_SCROLL_FACTOR; + + + private long mLastScroll; + + private final Rect mTempRect = new Rect(); + private Scroller mScroller; + + /** + * Flag to indicate that we are moving focus ourselves. This is so the + * code that watches for focus changes initiated outside this ScrollView + * knows that it does not have to do anything. + */ + private boolean mScrollViewMovedFocus; + + /** + * Position of the last motion event. + */ + private float mLastMotionX; + + /** + * True when the layout has changed but the traversal has not come through yet. + * Ideally the view hierarchy would keep track of this for us. + */ + private boolean mIsLayoutDirty = true; + + /** + * The child to give focus to in the event that a child has requested focus while the + * layout is dirty. This prevents the scroll from being wrong if the child has not been + * laid out before requesting focus. + */ + private View mChildToScrollTo = null; + + /** + * True if the user is currently dragging this ScrollView around. This is + * not the same as 'is being flinged', which can be checked by + * mScroller.isFinished() (flinging begins when the user lifts his finger). + */ + private boolean mIsBeingDragged = false; + + /** + * Determines speed during touch scrolling + */ + private VelocityTracker mVelocityTracker; + + /** + * When set to true, the scroll view measure its child to make it fill the currently + * visible area. + */ + private boolean mFillViewport; + + /** + * Whether arrow scrolling is animated. + */ + private boolean mSmoothScrollingEnabled = true; + + private int mTouchSlop; + + public HorizontalScrollView(Context context) { + this(context, null); + } + + public HorizontalScrollView(Context context, AttributeSet attrs) { + this(context, attrs, com.android.internal.R.attr.horizontalScrollViewStyle); + } + + public HorizontalScrollView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initScrollView(); + + TypedArray a = context.obtainStyledAttributes(attrs, + android.R.styleable.HorizontalScrollView, defStyle, 0); + + setFillViewport(a.getBoolean(android.R.styleable.HorizontalScrollView_fillViewport, false)); + + a.recycle(); + } + + @Override + protected float getLeftFadingEdgeStrength() { + if (getChildCount() == 0) { + return 0.0f; + } + + final int length = getHorizontalFadingEdgeLength(); + if (mScrollX < length) { + return mScrollX / (float) length; + } + + return 1.0f; + } + + @Override + protected float getRightFadingEdgeStrength() { + if (getChildCount() == 0) { + return 0.0f; + } + + final int length = getHorizontalFadingEdgeLength(); + final int rightEdge = getWidth() - mPaddingRight; + final int span = getChildAt(0).getRight() - mScrollX - rightEdge; + if (span < length) { + return span / (float) length; + } + + return 1.0f; + } + + /** + * @return The maximum amount this scroll view will scroll in response to + * an arrow event. + */ + public int getMaxScrollAmount() { + return (int) (MAX_SCROLL_FACTOR * (mRight - mLeft)); + } + + + private void initScrollView() { + mScroller = new Scroller(getContext()); + setFocusable(true); + setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); + setWillNotDraw(false); + mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); + } + + @Override + public void addView(View child) { + if (getChildCount() > 0) { + throw new IllegalStateException("HorizontalScrollView can host only one direct child"); + } + + super.addView(child); + } + + @Override + public void addView(View child, int index) { + if (getChildCount() > 0) { + throw new IllegalStateException("HorizontalScrollView can host only one direct child"); + } + + super.addView(child, index); + } + + @Override + public void addView(View child, ViewGroup.LayoutParams params) { + if (getChildCount() > 0) { + throw new IllegalStateException("HorizontalScrollView can host only one direct child"); + } + + super.addView(child, params); + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + if (getChildCount() > 0) { + throw new IllegalStateException("HorizontalScrollView can host only one direct child"); + } + + super.addView(child, index, params); + } + + /** + * @return Returns true this HorizontalScrollView can be scrolled + */ + private boolean canScroll() { + View child = getChildAt(0); + if (child != null) { + int childWidth = child.getWidth(); + return getWidth() < childWidth + mPaddingLeft + mPaddingRight ; + } + return false; + } + + /** + * Indicates whether this ScrollView's content is stretched to fill the viewport. + * + * @return True if the content fills the viewport, false otherwise. + */ + public boolean isFillViewport() { + return mFillViewport; + } + + /** + * Indicates this ScrollView whether it should stretch its content width to fill + * the viewport or not. + * + * @param fillViewport True to stretch the content's width to the viewport's + * boundaries, false otherwise. + */ + public void setFillViewport(boolean fillViewport) { + if (fillViewport != mFillViewport) { + mFillViewport = fillViewport; + requestLayout(); + } + } + + /** + * @return Whether arrow scrolling will animate its transition. + */ + public boolean isSmoothScrollingEnabled() { + return mSmoothScrollingEnabled; + } + + /** + * Set whether arrow scrolling will animate its transition. + * @param smoothScrollingEnabled whether arrow scrolling will animate its transition + */ + public void setSmoothScrollingEnabled(boolean smoothScrollingEnabled) { + mSmoothScrollingEnabled = smoothScrollingEnabled; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + if (!mFillViewport) { + return; + } + + final int widthMode = MeasureSpec.getMode(widthMeasureSpec); + if (widthMode == MeasureSpec.UNSPECIFIED) { + return; + } + + final View child = getChildAt(0); + int width = getMeasuredWidth(); + if (child.getMeasuredHeight() < width) { + final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams(); + + int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, mPaddingTop + + mPaddingBottom, lp.height); + width -= mPaddingLeft; + width -= mPaddingRight; + int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + } + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + // Let the focused view and/or our descendants get the key first + boolean handled = super.dispatchKeyEvent(event); + if (handled) { + return true; + } + return executeKeyEvent(event); + } + + /** + * You can call this function yourself to have the scroll view perform + * scrolling from a key event, just as if the event had been dispatched to + * it by the view hierarchy. + * + * @param event The key event to execute. + * @return Return true if the event was handled, else false. + */ + public boolean executeKeyEvent(KeyEvent event) { + mTempRect.setEmpty(); + + if (!canScroll()) { + if (isFocused()) { + View currentFocused = findFocus(); + if (currentFocused == this) currentFocused = null; + View nextFocused = FocusFinder.getInstance().findNextFocus(this, + currentFocused, View.FOCUS_RIGHT); + return nextFocused != null && nextFocused != this && + nextFocused.requestFocus(View.FOCUS_RIGHT); + } + return false; + } + + boolean handled = false; + if (event.getAction() == KeyEvent.ACTION_DOWN) { + switch (event.getKeyCode()) { + case KeyEvent.KEYCODE_DPAD_LEFT: + if (!event.isAltPressed()) { + handled = arrowScroll(View.FOCUS_LEFT); + } else { + handled = fullScroll(View.FOCUS_LEFT); + } + break; + case KeyEvent.KEYCODE_DPAD_RIGHT: + if (!event.isAltPressed()) { + handled = arrowScroll(View.FOCUS_RIGHT); + } else { + handled = fullScroll(View.FOCUS_RIGHT); + } + break; + case KeyEvent.KEYCODE_SPACE: + pageScroll(event.isShiftPressed() ? View.FOCUS_LEFT : View.FOCUS_RIGHT); + break; + } + } + + return handled; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + /* + * This method JUST determines whether we want to intercept the motion. + * If we return true, onMotionEvent will be called and we do the actual + * scrolling there. + */ + + /* + * Shortcut the most recurring case: the user is in the dragging + * state and he is moving his finger. We want to intercept this + * motion. + */ + final int action = ev.getAction(); + if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) { + return true; + } + + if (!canScroll()) { + mIsBeingDragged = false; + return false; + } + + final float x = ev.getX(); + + switch (action) { + case MotionEvent.ACTION_MOVE: + /* + * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check + * whether the user has moved far enough from his original down touch. + */ + + /* + * Locally do absolute value. mLastMotionX is set to the x value + * of the down event. + */ + final int xDiff = (int) Math.abs(x - mLastMotionX); + if (xDiff > mTouchSlop) { + mIsBeingDragged = true; + if (mParent != null) mParent.requestDisallowInterceptTouchEvent(true); + } + break; + + case MotionEvent.ACTION_DOWN: + /* Remember location of down touch */ + mLastMotionX = x; + + /* + * If being flinged and user touches the screen, initiate drag; + * otherwise don't. mScroller.isFinished should be false when + * being flinged. + */ + mIsBeingDragged = !mScroller.isFinished(); + break; + + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + /* Release the drag */ + mIsBeingDragged = false; + break; + } + + /* + * The only time we want to intercept motion events is if we are in the + * drag mode. + */ + return mIsBeingDragged; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + + if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) { + // Don't handle edge touches immediately -- they may actually belong to one of our + // descendants. + return false; + } + + if (!canScroll()) { + return false; + } + + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + mVelocityTracker.addMovement(ev); + + final int action = ev.getAction(); + final float x = ev.getX(); + + switch (action) { + case MotionEvent.ACTION_DOWN: + /* + * If being flinged and user touches, stop the fling. isFinished + * will be false if being flinged. + */ + if (!mScroller.isFinished()) { + mScroller.abortAnimation(); + } + + // Remember where the motion event started + mLastMotionX = x; + break; + case MotionEvent.ACTION_MOVE: + // Scroll to follow the motion event + final int deltaX = (int) (mLastMotionX - x); + mLastMotionX = x; + + if (deltaX < 0) { + if (mScrollX > 0) { + scrollBy(deltaX, 0); + } + } else if (deltaX > 0) { + final int rightEdge = getWidth() - mPaddingRight; + final int availableToScroll = getChildAt(0).getRight() - mScrollX - rightEdge; + if (availableToScroll > 0) { + scrollBy(Math.min(availableToScroll, deltaX), 0); + } + } + break; + case MotionEvent.ACTION_UP: + final VelocityTracker velocityTracker = mVelocityTracker; + velocityTracker.computeCurrentVelocity(1000); + int initialVelocity = (int) velocityTracker.getXVelocity(); + + if ((Math.abs(initialVelocity) > + ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) && + getChildCount() > 0) { + fling(-initialVelocity); + } + + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + } + return true; + } + + /** + * <p> + * Finds the next focusable component that fits in this View's bounds + * (excluding fading edges) pretending that this View's left is located at + * the parameter left. + * </p> + * + * @param leftFocus look for a candidate is the one at the left of the bounds + * if leftFocus is true, or at the right of the bounds if leftFocus + * is false + * @param left the left offset of the bounds in which a focusable must be + * found (the fading edge is assumed to start at this position) + * @param preferredFocusable the View that has highest priority and will be + * returned if it is within my bounds (null is valid) + * @return the next focusable component in the bounds or null if none can be found + */ + private View findFocusableViewInMyBounds(final boolean leftFocus, + final int left, View preferredFocusable) { + /* + * The fading edge's transparent side should be considered for focus + * since it's mostly visible, so we divide the actual fading edge length + * by 2. + */ + final int fadingEdgeLength = getHorizontalFadingEdgeLength() / 2; + final int leftWithoutFadingEdge = left + fadingEdgeLength; + final int rightWithoutFadingEdge = left + getWidth() - fadingEdgeLength; + + if ((preferredFocusable != null) + && (preferredFocusable.getLeft() < rightWithoutFadingEdge) + && (preferredFocusable.getRight() > leftWithoutFadingEdge)) { + return preferredFocusable; + } + + return findFocusableViewInBounds(leftFocus, leftWithoutFadingEdge, + rightWithoutFadingEdge); + } + + /** + * <p> + * Finds the next focusable component that fits in the specified bounds. + * </p> + * + * @param leftFocus look for a candidate is the one at the left of the bounds + * if leftFocus is true, or at the right of the bounds if + * leftFocus is false + * @param left the left offset of the bounds in which a focusable must be + * found + * @param right the right offset of the bounds in which a focusable must + * be found + * @return the next focusable component in the bounds or null if none can + * be found + */ + private View findFocusableViewInBounds(boolean leftFocus, int left, int right) { + + List<View> focusables = getFocusables(View.FOCUS_FORWARD); + View focusCandidate = null; + + /* + * A fully contained focusable is one where its left is below the bound's + * left, and its right is above the bound's right. A partially + * contained focusable is one where some part of it is within the + * bounds, but it also has some part that is not within bounds. A fully contained + * focusable is preferred to a partially contained focusable. + */ + boolean foundFullyContainedFocusable = false; + + int count = focusables.size(); + for (int i = 0; i < count; i++) { + View view = focusables.get(i); + int viewLeft = view.getLeft(); + int viewRight = view.getRight(); + + if (left < viewRight && viewLeft < right) { + /* + * the focusable is in the target area, it is a candidate for + * focusing + */ + + final boolean viewIsFullyContained = (left < viewLeft) && + (viewRight < right); + + if (focusCandidate == null) { + /* No candidate, take this one */ + focusCandidate = view; + foundFullyContainedFocusable = viewIsFullyContained; + } else { + final boolean viewIsCloserToBoundary = + (leftFocus && viewLeft < focusCandidate.getLeft()) || + (!leftFocus && viewRight > focusCandidate.getRight()); + + if (foundFullyContainedFocusable) { + if (viewIsFullyContained && viewIsCloserToBoundary) { + /* + * We're dealing with only fully contained views, so + * it has to be closer to the boundary to beat our + * candidate + */ + focusCandidate = view; + } + } else { + if (viewIsFullyContained) { + /* Any fully contained view beats a partially contained view */ + focusCandidate = view; + foundFullyContainedFocusable = true; + } else if (viewIsCloserToBoundary) { + /* + * Partially contained view beats another partially + * contained view if it's closer + */ + focusCandidate = view; + } + } + } + } + } + + return focusCandidate; + } + + /** + * <p>Handles scrolling in response to a "page up/down" shortcut press. This + * method will scroll the view by one page left or right and give the focus + * to the leftmost/rightmost component in the new visible area. If no + * component is a good candidate for focus, this scrollview reclaims the + * focus.</p> + * + * @param direction the scroll direction: {@link android.view.View#FOCUS_LEFT} + * to go one page left or {@link android.view.View#FOCUS_RIGHT} + * to go one page right + * @return true if the key event is consumed by this method, false otherwise + */ + public boolean pageScroll(int direction) { + boolean right = direction == View.FOCUS_RIGHT; + int width = getWidth(); + + if (right) { + mTempRect.left = getScrollX() + width; + int count = getChildCount(); + if (count > 0) { + View view = getChildAt(count - 1); + if (mTempRect.left + width > view.getRight()) { + mTempRect.left = view.getRight() - width; + } + } + } else { + mTempRect.left = getScrollX() - width; + if (mTempRect.left < 0) { + mTempRect.left = 0; + } + } + mTempRect.right = mTempRect.left + width; + + return scrollAndFocus(direction, mTempRect.left, mTempRect.right); + } + + /** + * <p>Handles scrolling in response to a "home/end" shortcut press. This + * method will scroll the view to the left or right and give the focus + * to the leftmost/rightmost component in the new visible area. If no + * component is a good candidate for focus, this scrollview reclaims the + * focus.</p> + * + * @param direction the scroll direction: {@link android.view.View#FOCUS_LEFT} + * to go the left of the view or {@link android.view.View#FOCUS_RIGHT} + * to go the right + * @return true if the key event is consumed by this method, false otherwise + */ + public boolean fullScroll(int direction) { + boolean right = direction == View.FOCUS_RIGHT; + int width = getWidth(); + + mTempRect.left = 0; + mTempRect.right = width; + + if (right) { + int count = getChildCount(); + if (count > 0) { + View view = getChildAt(count - 1); + mTempRect.right = view.getRight(); + mTempRect.left = mTempRect.right - width; + } + } + + return scrollAndFocus(direction, mTempRect.left, mTempRect.right); + } + + /** + * <p>Scrolls the view to make the area defined by <code>left</code> and + * <code>right</code> visible. This method attempts to give the focus + * to a component visible in this area. If no component can be focused in + * the new visible area, the focus is reclaimed by this scrollview.</p> + * + * @param direction the scroll direction: {@link android.view.View#FOCUS_LEFT} + * to go left {@link android.view.View#FOCUS_RIGHT} to right + * @param left the left offset of the new area to be made visible + * @param right the right offset of the new area to be made visible + * @return true if the key event is consumed by this method, false otherwise + */ + private boolean scrollAndFocus(int direction, int left, int right) { + boolean handled = true; + + int width = getWidth(); + int containerLeft = getScrollX(); + int containerRight = containerLeft + width; + boolean goLeft = direction == View.FOCUS_LEFT; + + View newFocused = findFocusableViewInBounds(goLeft, left, right); + if (newFocused == null) { + newFocused = this; + } + + if (left >= containerLeft && right <= containerRight) { + handled = false; + } else { + int delta = goLeft ? (left - containerLeft) : (right - containerRight); + doScrollX(delta); + } + + if (newFocused != findFocus() && newFocused.requestFocus(direction)) { + mScrollViewMovedFocus = true; + mScrollViewMovedFocus = false; + } + + return handled; + } + + /** + * Handle scrolling in response to a left or right arrow click. + * + * @param direction The direction corresponding to the arrow key that was + * pressed + * @return True if we consumed the event, false otherwise + */ + public boolean arrowScroll(int direction) { + + View currentFocused = findFocus(); + if (currentFocused == this) currentFocused = null; + + View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction); + + final int maxJump = getMaxScrollAmount(); + + if (nextFocused != null && isWithinDeltaOfScreen(nextFocused, maxJump)) { + nextFocused.getDrawingRect(mTempRect); + offsetDescendantRectToMyCoords(nextFocused, mTempRect); + int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); + doScrollX(scrollDelta); + nextFocused.requestFocus(direction); + } else { + // no new focus + int scrollDelta = maxJump; + + if (direction == View.FOCUS_LEFT && getScrollX() < scrollDelta) { + scrollDelta = getScrollX(); + } else if (direction == View.FOCUS_RIGHT) { + + int daRight = getChildAt(getChildCount() - 1).getRight(); + + int screenRight = getScrollX() + getWidth(); + + if (daRight - screenRight < maxJump) { + scrollDelta = daRight - screenRight; + } + } + if (scrollDelta == 0) { + return false; + } + doScrollX(direction == View.FOCUS_RIGHT ? scrollDelta : -scrollDelta); + } + + if (currentFocused != null && currentFocused.isFocused() + && isOffScreen(currentFocused)) { + // previously focused item still has focus and is off screen, give + // it up (take it back to ourselves) + // (also, need to temporarily force FOCUS_BEFORE_DESCENDANTS so we are + // sure to + // get it) + final int descendantFocusability = getDescendantFocusability(); // save + setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); + requestFocus(); + setDescendantFocusability(descendantFocusability); // restore + } + return true; + } + + /** + * @return whether the descendant of this scroll view is scrolled off + * screen. + */ + private boolean isOffScreen(View descendant) { + return !isWithinDeltaOfScreen(descendant, 0); + } + + /** + * @return whether the descendant of this scroll view is within delta + * pixels of being on the screen. + */ + private boolean isWithinDeltaOfScreen(View descendant, int delta) { + descendant.getDrawingRect(mTempRect); + offsetDescendantRectToMyCoords(descendant, mTempRect); + + return (mTempRect.right + delta) >= getScrollX() + && (mTempRect.left - delta) <= (getScrollX() + getWidth()); + } + + /** + * Smooth scroll by a X delta + * + * @param delta the number of pixels to scroll by on the X axis + */ + private void doScrollX(int delta) { + if (delta != 0) { + if (mSmoothScrollingEnabled) { + smoothScrollBy(delta, 0); + } else { + scrollBy(delta, 0); + } + } + } + + /** + * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. + * + * @param dx the number of pixels to scroll by on the X axis + * @param dy the number of pixels to scroll by on the Y axis + */ + public final void smoothScrollBy(int dx, int dy) { + long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll; + if (duration > ANIMATED_SCROLL_GAP) { + mScroller.startScroll(mScrollX, mScrollY, dx, dy); + invalidate(); + } else { + if (!mScroller.isFinished()) { + mScroller.abortAnimation(); + } + scrollBy(dx, dy); + } + mLastScroll = AnimationUtils.currentAnimationTimeMillis(); + } + + /** + * Like {@link #scrollTo}, but scroll smoothly instead of immediately. + * + * @param x the position where to scroll on the X axis + * @param y the position where to scroll on the Y axis + */ + public final void smoothScrollTo(int x, int y) { + smoothScrollBy(x - mScrollX, y - mScrollY); + } + + /** + * <p>The scroll range of a scroll view is the overall width of all of its + * children.</p> + */ + @Override + protected int computeHorizontalScrollRange() { + int count = getChildCount(); + return count == 0 ? getWidth() : getChildAt(0).getRight(); + } + + + @Override + protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { + ViewGroup.LayoutParams lp = child.getLayoutParams(); + + int childWidthMeasureSpec; + int childHeightMeasureSpec; + + childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + + mPaddingBottom, lp.height); + + childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + } + + @Override + protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, + int parentHeightMeasureSpec, int heightUsed) { + final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); + + final int childHeightMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, + mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + + heightUsed, lp.height); + final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( + lp.leftMargin + lp.rightMargin, MeasureSpec.UNSPECIFIED); + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + } + + @Override + public void computeScroll() { + if (mScroller.computeScrollOffset()) { + // This is called at drawing time by ViewGroup. We don't want to + // re-show the scrollbars at this point, which scrollTo will do, + // so we replicate most of scrollTo here. + // + // It's a little odd to call onScrollChanged from inside the drawing. + // + // It is, except when you remember that computeScroll() is used to + // animate scrolling. So unless we want to defer the onScrollChanged() + // until the end of the animated scrolling, we don't really have a + // choice here. + // + // I agree. The alternative, which I think would be worse, is to post + // something and tell the subclasses later. This is bad because there + // will be a window where mScrollX/Y is different from what the app + // thinks it is. + // + int oldX = mScrollX; + int oldY = mScrollY; + int x = mScroller.getCurrX(); + int y = mScroller.getCurrY(); + if (getChildCount() > 0) { + View child = getChildAt(0); + mScrollX = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth()); + mScrollY = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight()); + } else { + mScrollX = x; + mScrollY = y; + } + if (oldX != mScrollX || oldY != mScrollY) { + onScrollChanged(mScrollX, mScrollY, oldX, oldY); + } + + // Keep on drawing until the animation has finished. + postInvalidate(); + } + } + + /** + * Scrolls the view to the given child. + * + * @param child the View to scroll to + */ + private void scrollToChild(View child) { + child.getDrawingRect(mTempRect); + + /* Offset from child's local coordinates to ScrollView coordinates */ + offsetDescendantRectToMyCoords(child, mTempRect); + + int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); + + if (scrollDelta != 0) { + scrollBy(scrollDelta, 0); + } + } + + /** + * If rect is off screen, scroll just enough to get it (or at least the + * first screen size chunk of it) on screen. + * + * @param rect The rectangle. + * @param immediate True to scroll immediately without animation + * @return true if scrolling was performed + */ + private boolean scrollToChildRect(Rect rect, boolean immediate) { + final int delta = computeScrollDeltaToGetChildRectOnScreen(rect); + final boolean scroll = delta != 0; + if (scroll) { + if (immediate) { + scrollBy(delta, 0); + } else { + smoothScrollBy(delta, 0); + } + } + return scroll; + } + + /** + * Compute the amount to scroll in the X direction in order to get + * a rectangle completely on the screen (or, if taller than the screen, + * at least the first screen size chunk of it). + * + * @param rect The rect. + * @return The scroll delta. + */ + protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) { + + int width = getWidth(); + int screenLeft = getScrollX(); + int screenRight = screenLeft + width; + + int fadingEdge = getHorizontalFadingEdgeLength(); + + // leave room for left fading edge as long as rect isn't at very left + if (rect.left > 0) { + screenLeft += fadingEdge; + } + + // leave room for right fading edge as long as rect isn't at very right + if (rect.right < getChildAt(0).getWidth()) { + screenRight -= fadingEdge; + } + + int scrollXDelta = 0; + + if (rect.right > screenRight && rect.left > screenLeft) { + // need to move right to get it in view: move right just enough so + // that the entire rectangle is in view (or at least the first + // screen size chunk). + + if (rect.width() > width) { + // just enough to get screen size chunk on + scrollXDelta += (rect.left - screenLeft); + } else { + // get entire rect at right of screen + scrollXDelta += (rect.right - screenRight); + } + + // make sure we aren't scrolling beyond the end of our content + int right = getChildAt(getChildCount() - 1).getRight(); + int distanceToRight = right - screenRight; + scrollXDelta = Math.min(scrollXDelta, distanceToRight); + + } else if (rect.left < screenLeft && rect.right < screenRight) { + // need to move right to get it in view: move right just enough so that + // entire rectangle is in view (or at least the first screen + // size chunk of it). + + if (rect.width() > width) { + // screen size chunk + scrollXDelta -= (screenRight - rect.right); + } else { + // entire rect at left + scrollXDelta -= (screenLeft - rect.left); + } + + // make sure we aren't scrolling any further than the left our content + scrollXDelta = Math.max(scrollXDelta, -getScrollX()); + } + return scrollXDelta; + } + + @Override + public void requestChildFocus(View child, View focused) { + if (!mScrollViewMovedFocus) { + if (!mIsLayoutDirty) { + scrollToChild(focused); + } else { + // The child may not be laid out yet, we can't compute the scroll yet + mChildToScrollTo = focused; + } + } + super.requestChildFocus(child, focused); + } + + + /** + * When looking for focus in children of a scroll view, need to be a little + * more careful not to give focus to something that is scrolled off screen. + * + * This is more expensive than the default {@link android.view.ViewGroup} + * implementation, otherwise this behavior might have been made the default. + */ + @Override + protected boolean onRequestFocusInDescendants(int direction, + Rect previouslyFocusedRect) { + + // convert from forward / backward notation to up / down / left / right + // (ugh). + if (direction == View.FOCUS_FORWARD) { + direction = View.FOCUS_RIGHT; + } else if (direction == View.FOCUS_BACKWARD) { + direction = View.FOCUS_LEFT; + } + + final View nextFocus = previouslyFocusedRect == null ? + FocusFinder.getInstance().findNextFocus(this, null, direction) : + FocusFinder.getInstance().findNextFocusFromRect(this, + previouslyFocusedRect, direction); + + if (nextFocus == null) { + return false; + } + + if (isOffScreen(nextFocus)) { + return false; + } + + return nextFocus.requestFocus(direction, previouslyFocusedRect); + } + + @Override + public boolean requestChildRectangleOnScreen(View child, Rect rectangle, + boolean immediate) { + // offset into coordinate space of this scroll view + rectangle.offset(child.getLeft() - child.getScrollX(), + child.getTop() - child.getScrollY()); + + return scrollToChildRect(rectangle, immediate); + } + + @Override + public void requestLayout() { + mIsLayoutDirty = true; + super.requestLayout(); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + mIsLayoutDirty = false; + // Give a child focus if it needs it + if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) { + scrollToChild(mChildToScrollTo); + } + mChildToScrollTo = null; + + // Calling this with the present values causes it to re-clam them + scrollTo(mScrollX, mScrollY); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + + View currentFocused = findFocus(); + if (null == currentFocused || this == currentFocused) + return; + + final int maxJump = mRight - mLeft; + + if (isWithinDeltaOfScreen(currentFocused, maxJump)) { + currentFocused.getDrawingRect(mTempRect); + offsetDescendantRectToMyCoords(currentFocused, mTempRect); + int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); + doScrollX(scrollDelta); + } + } + + /** + * Return true if child is an descendant of parent, (or equal to the parent). + */ + private boolean isViewDescendantOf(View child, View parent) { + if (child == parent) { + return true; + } + + final ViewParent theParent = child.getParent(); + return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent); + } + + /** + * Fling the scroll view + * + * @param velocityX The initial velocity in the X direction. Positive + * numbers mean that the finger/curor is moving down the screen, + * which means we want to scroll towards the left. + */ + public void fling(int velocityX) { + int width = getWidth() - mPaddingRight - mPaddingLeft; + int right = getChildAt(0).getWidth(); + + mScroller.fling(mScrollX, mScrollY, velocityX, 0, 0, right - width, 0, 0); + + final boolean movingRight = velocityX > 0; + + View newFocused = findFocusableViewInMyBounds(movingRight, + mScroller.getFinalX(), findFocus()); + + if (newFocused == null) { + newFocused = this; + } + + if (newFocused != findFocus() + && newFocused.requestFocus(movingRight ? View.FOCUS_RIGHT : View.FOCUS_LEFT)) { + mScrollViewMovedFocus = true; + mScrollViewMovedFocus = false; + } + + invalidate(); + } + + /** + * {@inheritDoc} + * + * <p>This version also clamps the scrolling to the bounds of our child. + */ + public void scrollTo(int x, int y) { + // we rely on the fact the View.scrollBy calls scrollTo. + if (getChildCount() > 0) { + View child = getChildAt(0); + x = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth()); + y = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight()); + if (x != mScrollX || y != mScrollY) { + super.scrollTo(x, y); + } + } + } + + private int clamp(int n, int my, int child) { + if (my >= child || n < 0) { + return 0; + } + if ((my + n) > child) { + return child - my; + } + return n; + } +} diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 36ed8bd..85a7339 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -336,7 +336,7 @@ public class LinearLayout extends ViewGroup { // heightMode is either UNSPECIFIED OR AT_MOST, and this child // wanted to stretch to fill available space. Translate that to // WRAP_CONTENT so that it does not end up with a height of 0 - oldHeight = lp.height; + oldHeight = 0; lp.height = LayoutParams.WRAP_CONTENT; } @@ -475,8 +475,6 @@ public class LinearLayout extends ViewGroup { matchWidthLocally ? margin : measuredWidth); allFillParent = allFillParent && lp.width == LayoutParams.FILL_PARENT; - alternativeMaxWidth = Math.max(alternativeMaxWidth, - matchWidthLocally ? margin : measuredWidth); mTotalLength += child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin + getNextLocationOffset(child); @@ -607,7 +605,7 @@ public class LinearLayout extends ViewGroup { // widthMode is either UNSPECIFIED OR AT_MOST, and this child // wanted to stretch to fill available space. Translate that to // WRAP_CONTENT so that it does not end up with a width of 0 - oldWidth = lp.width; + oldWidth = 0; lp.width = LayoutParams.WRAP_CONTENT; } @@ -766,8 +764,6 @@ public class LinearLayout extends ViewGroup { matchHeightLocally ? margin : childHeight); allFillParent = allFillParent && lp.height == LayoutParams.FILL_PARENT; - alternativeMaxHeight = Math.max(alternativeMaxHeight, - matchHeightLocally ? margin : childHeight); if (baselineAligned) { final int childBaseline = child.getBaseline(); @@ -803,8 +799,7 @@ public class LinearLayout extends ViewGroup { maxHeight = Math.max(maxHeight, ascent + descent); } } else { - alternativeMaxHeight = Math.max(alternativeMaxHeight, - weightedMaxHeight); + alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight); } if (!allFillParent && heightMode != MeasureSpec.EXACTLY) { diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index dfc7bc3..9c7f600 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -1718,12 +1718,11 @@ public class ListView extends AbsListView { } /** - * Sets the currently selected item + * Sets the currently selected item. If in touch mode, the item will not be selected + * but it will still be positioned appropriately. If the specified selection position + * is less than 0, then the item at position 0 will be selected. * * @param position Index (starting at 0) of the data item to be selected. - * - * If in touch mode, the item will not be selected but it will still be positioned - * appropriately. */ @Override public void setSelection(int position) { diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 50248c1..dada105 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -632,17 +632,36 @@ public class PopupWindow { WindowManager.LayoutParams p = createPopupLayout(anchor.getWindowToken()); preparePopup(p); + mAboveAnchor = findDropDownPosition(anchor, p, xoff, yoff); + if (mBackground != null) { mPopupView.refreshDrawableState(); } - mAboveAnchor = findDropDownPosition(anchor, p, xoff, yoff); + if (mHeightMode < 0) p.height = mLastHeight = mHeightMode; if (mWidthMode < 0) p.width = mLastWidth = mWidthMode; + p.windowAnimations = computeAnimationResource(); + invokePopup(p); } /** + * Indicates whether the popup is showing above (the y coordinate of the popup's bottom + * is less than the y coordinate of the anchor) or below the anchor view (the y coordinate + * of the popup is greater than y coordinate of the anchor's bottom). + * + * The value returned + * by this method is meaningful only after {@link #showAsDropDown(android.view.View)} + * or {@link #showAsDropDown(android.view.View, int, int)} was invoked. + * + * @return True if this popup is showing above the anchor view, false otherwise. + */ + public boolean isAboveAnchor() { + return mAboveAnchor; + } + + /** * <p>Prepare the popup by embedding in into a new ViewGroup if the * background drawable is not null. If embedding is required, the layout * parameters' height is mnodified to take into account the background's @@ -662,18 +681,6 @@ public class PopupWindow { popupViewContainer.setBackgroundDrawable(mBackground); popupViewContainer.addView(mContentView, listParams); - if (p.height >= 0) { - // accomodate the popup's height to take into account the - // background's padding - p.height += popupViewContainer.getPaddingTop() + - popupViewContainer.getPaddingBottom(); - } - if (p.width >= 0) { - // accomodate the popup's width to take into account the - // background's padding - p.width += popupViewContainer.getPaddingLeft() + - popupViewContainer.getPaddingRight(); - } mPopupView = popupViewContainer; } else { mPopupView = mContentView; @@ -720,7 +727,8 @@ public class PopupWindow { p.flags = computeFlags(p.flags); p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; p.token = token; - + p.setTitle("PopupWindow:" + Integer.toHexString(hashCode())); + return p; } @@ -781,7 +789,9 @@ public class PopupWindow { * * @return true if the popup is translated upwards to fit on screen */ - private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, int xoff, int yoff) { + private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, + int xoff, int yoff) { + anchor.getLocationInWindow(mDrawingLocation); p.x = mDrawingLocation[0] + xoff; p.y = mDrawingLocation[1] + anchor.getMeasuredHeight() + yoff; @@ -795,8 +805,7 @@ public class PopupWindow { anchor.getWindowVisibleDisplayFrame(displayFrame); final View root = anchor.getRootView(); - if (mScreenLocation[1] + anchor.getMeasuredHeight() + yoff + mPopupHeight > displayFrame.bottom - || p.x + mPopupWidth - root.getWidth() > 0) { + if (p.y + mPopupHeight > displayFrame.bottom || p.x + mPopupWidth - root.getWidth() > 0) { // if the drop down disappears at the bottom of the screen. we try to // scroll a parent scrollview or move the drop down back up on top of // the edit box @@ -815,11 +824,11 @@ public class PopupWindow { // determine whether there is more space above or below the anchor anchor.getLocationOnScreen(mScreenLocation); - onTop = (displayFrame.bottom - mScreenLocation[1] - anchor.getMeasuredHeight() - yoff) - < (mScreenLocation[1] - yoff - displayFrame.top); + onTop = (displayFrame.bottom - mScreenLocation[1] - anchor.getMeasuredHeight() - yoff) < + (mScreenLocation[1] - yoff - displayFrame.top); if (onTop) { p.gravity = Gravity.LEFT | Gravity.BOTTOM; - p.y = root.getHeight() - mDrawingLocation[1] - yoff; + p.y = root.getHeight() - mDrawingLocation[1] + yoff; } else { p.y = mDrawingLocation[1] + anchor.getMeasuredHeight() + yoff; } @@ -841,15 +850,30 @@ public class PopupWindow { * shown. */ public int getMaxAvailableHeight(View anchor) { + return getMaxAvailableHeight(anchor, 0); + } + + /** + * Returns the maximum height that is available for the popup to be + * completely shown. It is recommended that this height be the maximum for + * the popup's height, otherwise it is possible that the popup will be + * clipped. + * + * @param anchor The view on which the popup window must be anchored. + * @param yOffset y offset from the view's bottom edge + * @return The maximum available height for the popup to be completely + * shown. + */ + public int getMaxAvailableHeight(View anchor, int yOffset) { final Rect displayFrame = new Rect(); anchor.getWindowVisibleDisplayFrame(displayFrame); final int[] anchorPos = mDrawingLocation; anchor.getLocationOnScreen(anchorPos); - final int distanceToBottom = displayFrame.bottom - - (anchorPos[1] + anchor.getHeight()); - final int distanceToTop = anchorPos[1] - displayFrame.top; + final int distanceToBottom = displayFrame.bottom - + (anchorPos[1] + anchor.getHeight()) - yOffset; + final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset; // anchorPos[1] is distance from anchor to top of screen int returnedHeight = Math.max(distanceToBottom, distanceToTop); @@ -939,10 +963,12 @@ public class PopupWindow { */ public void update(int x, int y, int width, int height) { if (width != -1) { + mLastWidth = width; setWidth(width); } if (height != -1) { + mLastHeight = height; setHeight(height); } @@ -990,22 +1016,6 @@ public class PopupWindow { } if (update) { - if (mPopupView != mContentView) { - final View popupViewContainer = mPopupView; - if (p.height >= 0) { - // accomodate the popup's height to take into account the - // background's padding - p.height += popupViewContainer.getPaddingTop() + - popupViewContainer.getPaddingBottom(); - } - if (p.width >= 0) { - // accomodate the popup's width to take into account the - // background's padding - p.width += popupViewContainer.getPaddingLeft() + - popupViewContainer.getPaddingRight(); - } - } - mWindowManager.updateViewLayout(mPopupView, p); } } diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 2e04b5d..434e9f3 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -761,7 +761,7 @@ public class ProgressBar extends View { if (dr == mProgressDrawable || dr == mIndeterminateDrawable) { final Rect dirty = dr.getBounds(); final int scrollX = mScrollX + mPaddingLeft; - final int scrollY = mScrollY + mPaddingRight; + final int scrollY = mScrollY + mPaddingTop; invalidate(dirty.left + scrollX, dirty.top + scrollY, dirty.right + scrollX, dirty.bottom + scrollY); diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java index ed8df22..393346a 100644 --- a/core/java/android/widget/RadioGroup.java +++ b/core/java/android/widget/RadioGroup.java @@ -122,6 +122,23 @@ public class RadioGroup extends LinearLayout { } } + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + if (child instanceof RadioButton) { + final RadioButton button = (RadioButton) child; + if (button.isChecked()) { + mProtectFromCheckedChange = true; + if (mCheckedId != -1) { + setCheckedStateForView(mCheckedId, false); + } + mProtectFromCheckedChange = false; + setCheckedId(button.getId()); + } + } + + super.addView(child, index, params); + } + /** * <p>Sets the selection to the radio button whose identifier is passed in * parameter. Using -1 as the selection identifier clears the selection; diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index 91d5805..9ded52b 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -152,7 +152,7 @@ public class RelativeLayout extends ViewGroup { private void initFromAttributes(Context context, AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RelativeLayout); - mIgnoreGravity = a.getResourceId(R.styleable.RelativeLayout_ignoreGravity, 0); + mIgnoreGravity = a.getResourceId(R.styleable.RelativeLayout_ignoreGravity, View.NO_ID); mGravity = a.getInt(R.styleable.RelativeLayout_gravity, mGravity); a.recycle(); } @@ -263,7 +263,7 @@ public class RelativeLayout extends ViewGroup { int right = Integer.MIN_VALUE; int bottom = Integer.MIN_VALUE; - if ((horizontalGravity || verticalGravity) && mIgnoreGravity != 0) { + if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) { ignore = findViewById(mIgnoreGravity); } diff --git a/core/java/android/widget/RemoteViews.aidl b/core/java/android/widget/RemoteViews.aidl new file mode 100644 index 0000000..ec86410 --- /dev/null +++ b/core/java/android/widget/RemoteViews.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2007, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.widget; + +parcelable RemoteViews; diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 5721095..a1023bd 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.PorterDuff; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -422,6 +423,131 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Equivalent to calling a combination of {@link Drawable#setAlpha(int)}, + * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}, + * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given view. + * <p> + * These operations will be performed on the {@link Drawable} returned by the + * target {@link View#getBackground()} by default. If targetBackground is false, + * we assume the target is an {@link ImageView} and try applying the operations + * to {@link ImageView#getDrawable()}. + * <p> + * You can omit specific calls by marking their values with null or -1. + */ + private class SetDrawableParameters extends Action { + public SetDrawableParameters(int id, boolean targetBackground, int alpha, + int colorFilter, PorterDuff.Mode mode, int level) { + this.viewId = id; + this.targetBackground = targetBackground; + this.alpha = alpha; + this.colorFilter = colorFilter; + this.filterMode = mode; + this.level = level; + } + + public SetDrawableParameters(Parcel parcel) { + viewId = parcel.readInt(); + targetBackground = parcel.readInt() != 0; + alpha = parcel.readInt(); + colorFilter = parcel.readInt(); + boolean hasMode = parcel.readInt() != 0; + if (hasMode) { + filterMode = PorterDuff.Mode.valueOf(parcel.readString()); + } else { + filterMode = null; + } + level = parcel.readInt(); + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(TAG); + dest.writeInt(viewId); + dest.writeInt(targetBackground ? 1 : 0); + dest.writeInt(alpha); + dest.writeInt(colorFilter); + if (filterMode != null) { + dest.writeInt(1); + dest.writeString(filterMode.toString()); + } else { + dest.writeInt(0); + } + dest.writeInt(level); + } + + @Override + public void apply(View root) { + final View target = root.findViewById(viewId); + if (target == null) { + return; + } + + // Pick the correct drawable to modify for this view + Drawable targetDrawable = null; + if (targetBackground) { + targetDrawable = target.getBackground(); + } else if (target instanceof ImageView) { + ImageView imageView = (ImageView) target; + targetDrawable = imageView.getDrawable(); + } + + // Perform modifications only if values are set correctly + if (alpha != -1) { + targetDrawable.setAlpha(alpha); + } + if (colorFilter != -1 && filterMode != null) { + targetDrawable.setColorFilter(colorFilter, filterMode); + } + if (level != -1) { + targetDrawable.setLevel(level); + } + } + + int viewId; + boolean targetBackground; + int alpha; + int colorFilter; + PorterDuff.Mode filterMode; + int level; + + public final static int TAG = 8; + } + + /** + * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}. + */ + private class SetTextColor extends Action { + public SetTextColor(int id, int color) { + this.viewId = id; + this.color = color; + } + + public SetTextColor(Parcel parcel) { + viewId = parcel.readInt(); + color = parcel.readInt(); + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(TAG); + dest.writeInt(viewId); + dest.writeInt(color); + } + + @Override + public void apply(View root) { + final View target = root.findViewById(viewId); + if (target instanceof TextView) { + final TextView textView = (TextView) target; + textView.setTextColor(color); + } + } + + int viewId; + int color; + + public final static int TAG = 9; + } + + /** * Create a new RemoteViews object that will display the views contained * in the specified layout file. * @@ -471,6 +597,12 @@ public class RemoteViews implements Parcelable, Filter { case SetOnClickPendingIntent.TAG: mActions.add(new SetOnClickPendingIntent(parcel)); break; + case SetDrawableParameters.TAG: + mActions.add(new SetDrawableParameters(parcel)); + break; + case SetTextColor.TAG: + mActions.add(new SetTextColor(parcel)); + break; default: throw new ActionException("Tag " + tag + "not found"); } @@ -595,6 +727,48 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Equivalent to calling a combination of {@link Drawable#setAlpha(int)}, + * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}, + * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given + * view. + * <p> + * You can omit specific calls by marking their values with null or -1. + * + * @param viewId The id of the view that contains the target + * {@link Drawable} + * @param targetBackground If true, apply these parameters to the + * {@link Drawable} returned by + * {@link android.view.View#getBackground()}. Otherwise, assume + * the target view is an {@link ImageView} and apply them to + * {@link ImageView#getDrawable()}. + * @param alpha Specify an alpha value for the drawable, or -1 to leave + * unchanged. + * @param colorFilter Specify a color for a + * {@link android.graphics.ColorFilter} for this drawable, or -1 + * to leave unchanged. + * @param mode Specify a PorterDuff mode for this drawable, or null to leave + * unchanged. + * @param level Specify the level for the drawable, or -1 to leave + * unchanged. + */ + public void setDrawableParameters(int viewId, boolean targetBackground, int alpha, + int colorFilter, PorterDuff.Mode mode, int level) { + addAction(new SetDrawableParameters(viewId, targetBackground, alpha, + colorFilter, mode, level)); + } + + /** + * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}. + * + * @param viewId The id of the view whose text should change + * @param color Sets the text color for all the states (normal, selected, + * focused) to be this color. + */ + public void setTextColor(int viewId, int color) { + addAction(new SetTextColor(viewId, color)); + } + + /** * Inflates the view hierarchy represented by this object and applies * all of the actions. * diff --git a/core/java/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java index 17f9128..3b113ae 100644 --- a/core/java/android/widget/ScrollBarDrawable.java +++ b/core/java/android/widget/ScrollBarDrawable.java @@ -152,10 +152,12 @@ public class ScrollBarDrawable extends Drawable { } else { track = mHorizontalTrack; } - if (mChanged) { - track.setBounds(bounds); + if (track != null) { + if (mChanged) { + track.setBounds(bounds); + } + track.draw(canvas); } - track.draw(canvas); } protected void drawThumb(Canvas canvas, Rect bounds, int offset, int length, boolean vertical) { diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 20166cf..c9b3751 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -44,13 +44,7 @@ import java.util.List; * manager with a complex hierarchy of objects. A child that is often used * is a {@link LinearLayout} in a vertical orientation, presenting a vertical * array of top-level items that the user can scroll through. - * - * <p>You should never use a ScrollView with a {@link ListView}, since - * ListView takes care of its own scrolling. Most importantly, doing this - * defeats all of the important optimizations in ListView for dealing with - * large lists, since it effectively forces the ListView to display its entire - * list of items to fill up the infinite container supplied by ScrollView. - * + * * <p>The {@link TextView} class also * takes care of its own scrolling, so does not require a ScrollView, but * using the two together is possible to achieve the effect of a text view @@ -62,13 +56,9 @@ public class ScrollView extends FrameLayout { static final String TAG = "ScrollView"; static final boolean localLOGV = false || Config.LOGV; - private static final int ANIMATED_SCROLL_GAP = 250; + static final int ANIMATED_SCROLL_GAP = 250; - /** - * When arrow scrolling, ListView will never scroll more than this factor - * times the height of the list. - */ - private static final float MAX_SCROLL_FACTOR = 0.5f; + static final float MAX_SCROLL_FACTOR = 0.5f; private long mLastScroll; @@ -124,6 +114,8 @@ public class ScrollView extends FrameLayout { */ private boolean mSmoothScrollingEnabled = true; + private int mTouchSlop; + public ScrollView(Context context) { this(context, null); } @@ -165,8 +157,8 @@ public class ScrollView extends FrameLayout { } final int length = getVerticalFadingEdgeLength(); - final int bottom = getChildAt(0).getBottom(); - final int span = bottom - mScrollY - getHeight(); + final int bottomEdge = getHeight() - mPaddingBottom; + final int span = getChildAt(0).getBottom() - mScrollY - bottomEdge; if (span < length) { return span / (float) length; } @@ -188,6 +180,7 @@ public class ScrollView extends FrameLayout { setFocusable(true); setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setWillNotDraw(false); + mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } @Override @@ -401,7 +394,7 @@ public class ScrollView extends FrameLayout { * of the down event. */ final int yDiff = (int) Math.abs(y - mLastMotionY); - if (yDiff > ViewConfiguration.getTouchSlop()) { + if (yDiff > mTouchSlop) { mIsBeingDragged = true; } break; @@ -488,8 +481,9 @@ public class ScrollView extends FrameLayout { velocityTracker.computeCurrentVelocity(1000); int initialVelocity = (int) velocityTracker.getYVelocity(); - if ((Math.abs(initialVelocity) > ViewConfiguration.getMinimumFlingVelocity()) && - (getChildCount() > 0)) { + if ((Math.abs(initialVelocity) > + ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) && + getChildCount() > 0) { fling(-initialVelocity); } @@ -812,7 +806,7 @@ public class ScrollView extends FrameLayout { /** * Smooth scroll by a Y delta * - * @param delta the number of pixels to scroll by on the X axis + * @param delta the number of pixels to scroll by on the Y axis */ private void doScrollY(int delta) { if (delta != 0) { @@ -923,8 +917,8 @@ public class ScrollView extends FrameLayout { int y = mScroller.getCurrY(); if (getChildCount() > 0) { View child = getChildAt(0); - mScrollX = clamp(x, this.getWidth(), child.getWidth()); - mScrollY = clamp(y, this.getHeight(), child.getHeight()); + mScrollX = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth()); + mScrollY = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight()); if (localLOGV) Log.v(TAG, "mScrollY=" + mScrollY + " y=" + y + " height=" + this.getHeight() + " child height=" + child.getHeight()); @@ -1167,8 +1161,8 @@ public class ScrollView extends FrameLayout { * which means we want to scroll towards the top. */ public void fling(int velocityY) { - int height = getHeight(); - int bottom = getChildAt(getChildCount() - 1).getBottom(); + int height = getHeight() - mPaddingBottom - mPaddingTop; + int bottom = getChildAt(0).getHeight(); mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0, bottom - height); @@ -1198,8 +1192,8 @@ public class ScrollView extends FrameLayout { // we rely on the fact the View.scrollBy calls scrollTo. if (getChildCount() > 0) { View child = getChildAt(0); - x = clamp(x, this.getWidth(), child.getWidth()); - y = clamp(y, this.getHeight(), child.getHeight()); + x = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth()); + y = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight()); if (x != mScrollX || y != mScrollY) { super.scrollTo(x, y); } diff --git a/core/java/android/widget/Scroller.java b/core/java/android/widget/Scroller.java index fbe5e57..febc956 100644 --- a/core/java/android/widget/Scroller.java +++ b/core/java/android/widget/Scroller.java @@ -132,6 +132,26 @@ public class Scroller { } /** + * Returns the start X offset in the scroll. + * + * @return The start X offset as an absolute distance from the origin. + * @hide pending API council + */ + public final int getStartX() { + return mStartX; + } + + /** + * Returns the start Y offset in the scroll. + * + * @return The start Y offset as an absolute distance from the origin. + * @hide pending API council + */ + public final int getStartY() { + return mStartY; + } + + /** * Returns where the scroll will end. Valid only for "fling" scrolls. * * @return The final X offset as an absolute distance from the origin. diff --git a/core/java/android/widget/SimpleAdapter.java b/core/java/android/widget/SimpleAdapter.java index 261da9f..093c24e 100644 --- a/core/java/android/widget/SimpleAdapter.java +++ b/core/java/android/widget/SimpleAdapter.java @@ -36,12 +36,16 @@ import java.util.Map; * Binding data to views occurs in two phases. First, if a * {@link android.widget.SimpleAdapter.ViewBinder} is available, * {@link ViewBinder#setViewValue(android.view.View, Object, String)} - * is invoked. If the returned value is true, binding has occured. If the - * returned value is false and the view to bind is a TextView, - * {@link #setViewText(TextView, String)} is invoked. If the returned value - * is false and the view to bind is an ImageView, - * {@link #setViewImage(ImageView, int)} or {@link #setViewImage(ImageView, String)} is - * invoked. If no appropriate binding can be found, an {@link IllegalStateException} is thrown. + * is invoked. If the returned value is true, binding has occurred. + * If the returned value is false, the following views are then tried in order: + * <ul> + * <li> A view that implements Checkable (e.g. CheckBox). The expected bind value is a boolean. + * <li> TextView. The expected bind value is a string and {@link #setViewText(TextView, String)} + * is invoked. + * <li> ImageView. The expected bind value is a resource id or a string and + * {@link #setViewImage(ImageView, int)} or {@link #setViewImage(ImageView, String)} is invoked. + * </ul> + * If no appropriate binding can be found, an {@link IllegalStateException} is thrown. */ public class SimpleAdapter extends BaseAdapter implements Filterable { private int[] mTo; @@ -176,7 +180,16 @@ public class SimpleAdapter extends BaseAdapter implements Filterable { } if (!bound) { - if (v instanceof TextView) { + if (v instanceof Checkable) { + if (data instanceof Boolean) { + ((Checkable) v).setChecked((Boolean) data); + } else { + throw new IllegalStateException(v.getClass().getName() + + " should be bound to a Boolean, not a " + data.getClass()); + } + } else if (v instanceof TextView) { + // Note: keep the instanceof TextView check at the bottom of these + // ifs since a lot of views are TextViews (e.g. CheckBoxes). setViewText((TextView) v, text); } else if (v instanceof ImageView) { if (data instanceof Integer) { diff --git a/core/java/com/android/internal/widget/SlidingDrawer.java b/core/java/android/widget/SlidingDrawer.java index a4045d5..e77c4ca 100644 --- a/core/java/com/android/internal/widget/SlidingDrawer.java +++ b/core/java/android/widget/SlidingDrawer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.widget; +package android.widget; import android.view.ViewGroup; import android.view.View; @@ -30,7 +30,7 @@ import android.graphics.Bitmap; import android.os.SystemClock; import android.os.Handler; import android.os.Message; -import com.android.internal.R; +import android.R; /** * SlidingDrawer hides content out of the screen and allows the user to drag a handle @@ -48,7 +48,7 @@ import com.android.internal.R; * content: * * <pre class="prettyprint"> - * <com.android.internal.widget.SlidingDrawer + * <SlidingDrawer * android:id="@+id/drawer" * android:layout_width="fill_parent" * android:layout_height="fill_parent" @@ -66,16 +66,16 @@ import com.android.internal.R; * android:layout_width="fill_parent" * android:layout_height="fill_parent" /> * - * </com.android.internal.widget.SlidingDrawer> + * </SlidingDrawer> * </pre> * - * @attr ref com.android.internal.R.styleable#SlidingDrawer_content - * @attr ref com.android.internal.R.styleable#SlidingDrawer_handle - * @attr ref com.android.internal.R.styleable#SlidingDrawer_topOffset - * @attr ref com.android.internal.R.styleable#SlidingDrawer_bottomOffset - * @attr ref com.android.internal.R.styleable#SlidingDrawer_orientation - * @attr ref com.android.internal.R.styleable#SlidingDrawer_allowSingleTap - * @attr ref com.android.internal.R.styleable#SlidingDrawer_animateOnClick + * @attr ref android.R.styleable#SlidingDrawer_content + * @attr ref android.R.styleable#SlidingDrawer_handle + * @attr ref android.R.styleable#SlidingDrawer_topOffset + * @attr ref android.R.styleable#SlidingDrawer_bottomOffset + * @attr ref android.R.styleable#SlidingDrawer_orientation + * @attr ref android.R.styleable#SlidingDrawer_allowSingleTap + * @attr ref android.R.styleable#SlidingDrawer_animateOnClick */ public class SlidingDrawer extends ViewGroup { public static final int ORIENTATION_HORIZONTAL = 0; @@ -128,6 +128,13 @@ public class SlidingDrawer extends ViewGroup { private boolean mAllowSingleTap; private boolean mAnimateOnClick; + private final int mTapThreshold; + private final int mMaximumTapVelocity; + private final int mMaximumMinorVelocity; + private final int mMaximumMajorVelocity; + private final int mMaximumAcceleration; + private final int mVelocityUnits; + /** * Callback invoked when the drawer is opened. */ @@ -206,6 +213,14 @@ public class SlidingDrawer extends ViewGroup { mHandleId = handleId; mContentId = contentId; + final float density = getResources().getDisplayMetrics().density; + mTapThreshold = (int) (TAP_THRESHOLD * density + 0.5f); + mMaximumTapVelocity = (int) (MAXIMUM_TAP_VELOCITY * density + 0.5f); + mMaximumMinorVelocity = (int) (MAXIMUM_MINOR_VELOCITY * density + 0.5f); + mMaximumMajorVelocity = (int) (MAXIMUM_MAJOR_VELOCITY * density + 0.5f); + mMaximumAcceleration = (int) (MAXIMUM_ACCELERATION * density + 0.5f); + mVelocityUnits = (int) (VELOCITY_UNITS * density + 0.5f); + a.recycle(); setAlwaysDrawnWithCacheEnabled(false); @@ -383,7 +398,7 @@ public class SlidingDrawer extends ViewGroup { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(VELOCITY_UNITS); + velocityTracker.computeCurrentVelocity(mVelocityUnits); float yVelocity = velocityTracker.getYVelocity(); float xVelocity = velocityTracker.getXVelocity(); @@ -395,16 +410,16 @@ public class SlidingDrawer extends ViewGroup { if (xVelocity < 0) { xVelocity = -xVelocity; } - if (xVelocity > MAXIMUM_MINOR_VELOCITY) { - xVelocity = MAXIMUM_MINOR_VELOCITY; + if (xVelocity > mMaximumMinorVelocity) { + xVelocity = mMaximumMinorVelocity; } } else { negative = xVelocity < 0; if (yVelocity < 0) { yVelocity = -yVelocity; } - if (yVelocity > MAXIMUM_MINOR_VELOCITY) { - yVelocity = MAXIMUM_MINOR_VELOCITY; + if (yVelocity > mMaximumMinorVelocity) { + yVelocity = mMaximumMinorVelocity; } } @@ -416,13 +431,13 @@ public class SlidingDrawer extends ViewGroup { final int top = mHandle.getTop(); final int left = mHandle.getLeft(); - if (Math.abs(velocity) < MAXIMUM_TAP_VELOCITY) { - if (vertical ? (mExpanded && top < TAP_THRESHOLD + mTopOffset) || + if (Math.abs(velocity) < mMaximumTapVelocity) { + if (vertical ? (mExpanded && top < mTapThreshold + mTopOffset) || (!mExpanded && top > mBottomOffset + mBottom - mTop - - mHandleHeight - TAP_THRESHOLD) : - (mExpanded && left < TAP_THRESHOLD + mTopOffset) || + mHandleHeight - mTapThreshold) : + (mExpanded && left < mTapThreshold + mTopOffset) || (!mExpanded && left > mBottomOffset + mRight - mLeft - - mHandleWidth - TAP_THRESHOLD)) { + mHandleWidth - mTapThreshold)) { if (mAllowSingleTap) { playSoundEffect(SoundEffectConstants.CLICK); @@ -450,12 +465,12 @@ public class SlidingDrawer extends ViewGroup { private void animateClose(int position) { prepareTracking(position); - performFling(position, 2000.0f, true); + performFling(position, mMaximumAcceleration, true); } private void animateOpen(int position) { prepareTracking(position); - performFling(position, -2000.0f, true); + performFling(position, -mMaximumAcceleration, true); } private void performFling(int position, float velocity, boolean always) { @@ -463,35 +478,35 @@ public class SlidingDrawer extends ViewGroup { mAnimatedVelocity = velocity; if (mExpanded) { - if (always || (velocity > MAXIMUM_MAJOR_VELOCITY || + if (always || (velocity > mMaximumMajorVelocity || (position > mTopOffset + (mVertical ? mHandleHeight : mHandleWidth) && - velocity > -MAXIMUM_MAJOR_VELOCITY))) { + velocity > -mMaximumMajorVelocity))) { // We are expanded, but they didn't move sufficiently to cause // us to retract. Animate back to the expanded position. - mAnimatedAcceleration = MAXIMUM_ACCELERATION; + mAnimatedAcceleration = mMaximumAcceleration; if (velocity < 0) { mAnimatedVelocity = 0; } } else { // We are expanded and are now going to animate away. - mAnimatedAcceleration = -MAXIMUM_ACCELERATION; + mAnimatedAcceleration = -mMaximumAcceleration; if (velocity > 0) { mAnimatedVelocity = 0; } } } else { - if (!always && (velocity > MAXIMUM_MAJOR_VELOCITY || + if (!always && (velocity > mMaximumMajorVelocity || (position > (mVertical ? getHeight() : getWidth()) / 2 && - velocity > -MAXIMUM_MAJOR_VELOCITY))) { + velocity > -mMaximumMajorVelocity))) { // We are collapsed, and they moved enough to allow us to expand. - mAnimatedAcceleration = MAXIMUM_ACCELERATION; + mAnimatedAcceleration = mMaximumAcceleration; if (velocity < 0) { mAnimatedVelocity = 0; } } else { // We are collapsed, but they didn't move sufficiently to cause // us to retract. Animate back to the collapsed position. - mAnimatedAcceleration = -MAXIMUM_ACCELERATION; + mAnimatedAcceleration = -mMaximumAcceleration; if (velocity > 0) { mAnimatedVelocity = 0; } @@ -512,8 +527,8 @@ public class SlidingDrawer extends ViewGroup { mVelocityTracker = VelocityTracker.obtain(); boolean opening = !mExpanded; if (opening) { - mAnimatedAcceleration = MAXIMUM_ACCELERATION; - mAnimatedVelocity = 200; + mAnimatedAcceleration = mMaximumAcceleration; + mAnimatedVelocity = mMaximumMajorVelocity; mAnimationPosition = mBottomOffset + (mVertical ? getHeight() - mHandleHeight : getWidth() - mHandleWidth); moveHandle((int) mAnimationPosition); @@ -782,6 +797,7 @@ public class SlidingDrawer extends ViewGroup { private void closeDrawer() { moveHandle(COLLAPSED_FULL_CLOSED); mContent.setVisibility(View.GONE); + mContent.destroyDrawingCache(); if (!mExpanded) { return; @@ -796,8 +812,6 @@ public class SlidingDrawer extends ViewGroup { private void openDrawer() { moveHandle(EXPANDED_FULL_OPEN); mContent.setVisibility(View.VISIBLE); - // TODO: Should we uncomment to preserve memory, but increase memory churn? - // mContent.destroyDrawingCache(); if (mExpanded) { return; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index aa70663..d21c017 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -17,6 +17,7 @@ package android.widget; import android.content.Context; +import android.content.Intent; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; @@ -42,6 +43,7 @@ import android.text.GraphicsOperations; import android.text.ClipboardManager; import android.text.InputFilter; import android.text.Layout; +import android.text.ParcelableSpan; import android.text.Selection; import android.text.SpanWatcher; import android.text.Spannable; @@ -69,7 +71,6 @@ import android.text.method.TransformationMethod; import android.text.style.ParagraphStyle; import android.text.style.URLSpan; import android.text.style.UpdateAppearance; -import android.text.style.UpdateLayout; import android.text.util.Linkify; import android.util.AttributeSet; import android.util.Log; @@ -168,6 +169,9 @@ import org.xmlpull.v1.XmlPullParserException; */ @RemoteView public class TextView extends View implements ViewTreeObserver.OnPreDrawListener { + static final String TAG = "TextView"; + static final boolean DEBUG_EXTRACT = false; + private static int PRIORITY = 100; private ColorStateList mTextColor; @@ -178,6 +182,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private boolean mFreezesText; private boolean mFrozenWithFocus; + private boolean mEatTouchRelease = false; + private boolean mScrolled = false; + private Editable.Factory mEditableFactory = Editable.Factory.getInstance(); private Spannable.Factory mSpannableFactory = Spannable.Factory.getInstance(); @@ -213,6 +220,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private CharSequence mError; private boolean mErrorWasChanged; private PopupWindow mPopup; + /** + * This flag is set if the TextView tries to display an error before it + * is attached to the window (so its position is still unknown). + * It causes the error to be shown later, when onAttachedToWindow() + * is called. + */ + private boolean mShowErrorAfterAttach; private CharWrapper mCharWrapper = null; @@ -235,7 +249,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener float[] mTmpOffset = new float[2]; ExtractedTextRequest mExtracting; final ExtractedText mTmpExtracted = new ExtractedText(); - boolean mBatchEditing; + int mBatchEditNesting; + boolean mCursorChanged; + boolean mContentChanged; + int mChangedStart, mChangedEnd, mChangedDelta; } InputMethodState mInputMethodState; @@ -2342,9 +2359,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * Sets the string value of the TextView. TextView <em>does not</em> accept * HTML-like formatting, which you can do with text strings in XML resource files. * To style your strings, attach android.text.style.* objects to a - * {@link android.text.SpannableString SpannableString}, or see - * <a href="{@docRoot}reference/available-resources.html#stringresources"> - * String Resources</a> for an example of setting formatted text in the XML resource file. + * {@link android.text.SpannableString SpannableString}, or see the + * <a href="{@docRoot}guide/topics/resources/available-resources.html#stringresources"> + * Available Resource Types</a> documentation for an example of setting + * formatted text in the XML resource file. * * @attr ref android.R.styleable#TextView_text */ @@ -2698,20 +2716,33 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ public void setInputType(int type) { setInputType(type, false); - if ((type&(EditorInfo.TYPE_MASK_CLASS + final boolean isPassword = (type&(EditorInfo.TYPE_MASK_CLASS |EditorInfo.TYPE_MASK_VARIATION)) == (EditorInfo.TYPE_CLASS_TEXT - |EditorInfo.TYPE_TEXT_VARIATION_PASSWORD)) { + |EditorInfo.TYPE_TEXT_VARIATION_PASSWORD); + boolean forceUpdate = false; + if (isPassword) { setTransformationMethod(PasswordTransformationMethod.getInstance()); setTypefaceByIndex(MONOSPACE, 0); + } else if (mTransformation == PasswordTransformationMethod.getInstance()) { + // We need to clean up if we were previously in password mode. + setTypefaceByIndex(-1, -1); + forceUpdate = true; } + boolean multiLine = (type&(EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE)) == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE); - if (mSingleLine == multiLine) { - setSingleLine(!multiLine); - } + + // We need to update the single line mode if it has changed or we + // were previously in password mode. + if (mSingleLine == multiLine || forceUpdate) { + // Change single line mode, but only change the transformation if + // we are not in password mode. + applySingleLine(!multiLine, !isPassword); + } + InputMethodManager imm = InputMethodManager.peekInstance(); if (imm != null) imm.restartInput(this); } @@ -2814,7 +2845,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * given integer is the resource ID of an XML resource holding an * {@link android.R.styleable#InputExtras <input-extras>} XML tree. * - * @see #getInputExtras() + * @see #getInputExtras(boolean) * @see EditorInfo#extras * @attr ref android.R.styleable#TextView_editorExtras */ @@ -2832,7 +2863,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @param create If true, the extras will be created if they don't already * exist. Otherwise, null will be returned if none have been created. - * @see #setInputExtras(int) + * @see #setInputExtras(int)View * @see EditorInfo#extras * @attr ref android.R.styleable#TextView_editorExtras */ @@ -2916,6 +2947,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private void showError() { + if (getWindowToken() == null) { + mShowErrorAfterAttach = true; + return; + } + if (mPopup == null) { LayoutInflater inflater = LayoutInflater.from(getContext()); TextView err = (TextView) inflater.inflate(com.android.internal.R.layout.textview_hint, @@ -2979,6 +3015,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mPopup.dismiss(); } } + + mShowErrorAfterAttach = false; } private void chooseSize(PopupWindow pop, CharSequence text, TextView tv) { @@ -3269,6 +3307,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mShowErrorAfterAttach) { + showError(); + mShowErrorAfterAttach = false; + } + } + + @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); @@ -3311,6 +3359,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override + protected boolean verifyDrawable(Drawable who) { + final boolean verified = super.verifyDrawable(who); + if (!verified && mDrawables != null) { + return who == mDrawables.mDrawableLeft || who == mDrawables.mDrawableTop || + who == mDrawables.mDrawableRight || who == mDrawables.mDrawableBottom; + } + return verified; + } + + @Override protected void onDraw(Canvas canvas) { // Draw the background for this view super.onDraw(canvas); @@ -3505,38 +3563,48 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } */ - InputMethodManager imm = InputMethodManager.peekInstance(); - if (highlight != null && mInputMethodState != null - && !mInputMethodState.mBatchEditing && imm != null) { - if (imm.isActive(this)) { - int candStart = -1; - int candEnd = -1; - if (mText instanceof Spannable) { - Spannable sp = (Spannable)mText; - candStart = EditableInputConnection.getComposingSpanStart(sp); - candEnd = EditableInputConnection.getComposingSpanEnd(sp); + final InputMethodState ims = mInputMethodState; + if (ims != null && ims.mBatchEditNesting == 0) { + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + if (imm.isActive(this)) { + boolean reported = false; + if (ims.mContentChanged) { + // We are in extract mode and the content has changed + // in some way... just report complete new text to the + // input method. + reported = reportExtractedText(); + } + if (!reported && highlight != null) { + int candStart = -1; + int candEnd = -1; + if (mText instanceof Spannable) { + Spannable sp = (Spannable)mText; + candStart = EditableInputConnection.getComposingSpanStart(sp); + candEnd = EditableInputConnection.getComposingSpanEnd(sp); + } + imm.updateSelection(this, selStart, selEnd, candStart, candEnd); + } + } + + if (imm.isWatchingCursor(this) && highlight != null) { + highlight.computeBounds(ims.mTmpRectF, true); + ims.mTmpOffset[0] = ims.mTmpOffset[1] = 0; + + canvas.getMatrix().mapPoints(ims.mTmpOffset); + ims.mTmpRectF.offset(ims.mTmpOffset[0], ims.mTmpOffset[1]); + + ims.mTmpRectF.offset(0, voffsetCursor - voffsetText); + + ims.mCursorRectInWindow.set((int)(ims.mTmpRectF.left + 0.5), + (int)(ims.mTmpRectF.top + 0.5), + (int)(ims.mTmpRectF.right + 0.5), + (int)(ims.mTmpRectF.bottom + 0.5)); + + imm.updateCursor(this, + ims.mCursorRectInWindow.left, ims.mCursorRectInWindow.top, + ims.mCursorRectInWindow.right, ims.mCursorRectInWindow.bottom); } - imm.updateSelection(this, selStart, selEnd, candStart, candEnd); - } - - if (imm.isWatchingCursor(this)) { - final InputMethodState ims = mInputMethodState; - highlight.computeBounds(ims.mTmpRectF, true); - ims.mTmpOffset[0] = ims.mTmpOffset[1] = 0; - - canvas.getMatrix().mapPoints(ims.mTmpOffset); - ims.mTmpRectF.offset(ims.mTmpOffset[0], ims.mTmpOffset[1]); - - ims.mTmpRectF.offset(0, voffsetCursor - voffsetText); - - ims.mCursorRectInWindow.set((int)(ims.mTmpRectF.left + 0.5), - (int)(ims.mTmpRectF.top + 0.5), - (int)(ims.mTmpRectF.right + 0.5), - (int)(ims.mTmpRectF.bottom + 0.5)); - - imm.updateCursor(this, - ims.mCursorRectInWindow.left, ims.mCursorRectInWindow.top, - ims.mCursorRectInWindow.right, ims.mCursorRectInWindow.bottom); } } @@ -3634,7 +3702,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - int which = doKeyDown(keyCode, event); + int which = doKeyDown(keyCode, event, null); if (which == 0) { // Go through default dispatching. return super.onKeyDown(keyCode, event); @@ -3647,12 +3715,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { KeyEvent down = new KeyEvent(event, KeyEvent.ACTION_DOWN); - int which = doKeyDown(keyCode, down); + int which = doKeyDown(keyCode, down, event); if (which == 0) { // Go through default dispatching. return super.onKeyMultiple(keyCode, repeatCount, event); } + if (which == -1) { + // Consumed the whole thing. + return true; + } + repeatCount--; + // We are going to dispatch the remaining events to either the input // or movement method. To do this, we will just send a repeated stream // of down and up events until we have done the complete repeatCount. @@ -3680,7 +3754,34 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return true; } - private int doKeyDown(int keyCode, KeyEvent event) { + /** + * Returns true if pressing ENTER in this field advances focus instead + * of inserting the character. This is true mostly in single-line fields, + * but also in mail addresses and subjects which will display on multiple + * lines but where it doesn't make sense to insert newlines. + */ + private boolean advanceFocusOnEnter() { + if (mInput == null) { + return false; + } + + if (mSingleLine) { + return true; + } + + if ((mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) { + int variation = mInputType & EditorInfo.TYPE_MASK_VARIATION; + + if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS || + variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT) { + return true; + } + } + + return false; + } + + private int doKeyDown(int keyCode, KeyEvent event, KeyEvent otherEvent) { if (!isEnabled()) { return 0; } @@ -3688,7 +3789,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener switch (keyCode) { case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_ENTER: - if (mSingleLine && mInput != null) { + if (advanceFocusOnEnter()) { return 0; } } @@ -3702,20 +3803,63 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ mErrorWasChanged = false; - if (mInput.onKeyDown(this, (Editable) mText, keyCode, event)) { - if (mError != null && !mErrorWasChanged) { - setError(null, null); + boolean doDown = true; + if (otherEvent != null) { + try { + beginBatchEdit(); + boolean handled = mInput.onKeyOther(this, (Editable) mText, + otherEvent); + if (mError != null && !mErrorWasChanged) { + setError(null, null); + } + doDown = false; + if (handled) { + return -1; + } + } catch (AbstractMethodError e) { + // onKeyOther was added after 1.0, so if it isn't + // implemented we need to try to dispatch as a regular down. + } finally { + endBatchEdit(); + } + } + + if (doDown) { + beginBatchEdit(); + if (mInput.onKeyDown(this, (Editable) mText, keyCode, event)) { + endBatchEdit(); + if (mError != null && !mErrorWasChanged) { + setError(null, null); + } + return 1; } - return 1; + endBatchEdit(); } } // bug 650865: sometimes we get a key event before a layout. // don't try to move around if we don't know the layout. - if (mMovement != null && mLayout != null) - if (mMovement.onKeyDown(this, (Spannable)mText, keyCode, event)) - return 2; + if (mMovement != null && mLayout != null) { + boolean doDown = true; + if (otherEvent != null) { + try { + boolean handled = mMovement.onKeyOther(this, (Editable) mText, + otherEvent); + doDown = false; + if (handled) { + return -1; + } + } catch (AbstractMethodError e) { + // onKeyOther was added after 1.0, so if it isn't + // implemented we need to try to dispatch as a regular down. + } + } + if (doDown) { + if (mMovement.onKeyDown(this, (Spannable)mText, keyCode, event)) + return 2; + } + } return 0; } @@ -3728,8 +3872,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener switch (keyCode) { case KeyEvent.KEYCODE_DPAD_CENTER: + /* + * If there is a click listener, just call through to + * super, which will invoke it. + * + * If there isn't a click listener, try to show the soft + * input method. (It will also + * call performClick(), but that won't do anything in + * this case.) + */ + if (mOnClickListener == null) { + if (mMovement != null && mText instanceof Editable + && mLayout != null && onCheckIsTextEditor()) { + InputMethodManager imm = (InputMethodManager) + getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(this, 0); + } + } + return super.onKeyUp(keyCode, event); + case KeyEvent.KEYCODE_ENTER: - if (mSingleLine && mInput != null) { + if (advanceFocusOnEnter()) { /* * If there is a click listener, just call through to * super, which will invoke it. @@ -3803,14 +3966,56 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * If this TextView contains editable content, extract a portion of it * based on the information in <var>request</var> in to <var>outText</var>. - * @return Returns true if the text is editable and was successfully - * extracted, else false. + * @return Returns true if the text was successfully extracted, else false. */ public boolean extractText(ExtractedTextRequest request, ExtractedText outText) { - Editable content = getEditableText(); + return extractTextInternal(request, -1, -1, -1, outText); + } + + boolean extractTextInternal(ExtractedTextRequest request, + int partialStartOffset, int partialEndOffset, int delta, + ExtractedText outText) { + final CharSequence content = mText; if (content != null) { - outText.text = content.subSequence(0, content.length()); + final int N = content.length(); + if (partialStartOffset < 0) { + outText.partialStartOffset = outText.partialEndOffset = -1; + partialStartOffset = 0; + partialEndOffset = N; + } else { + // Adjust offsets to ensure we contain full spans. + if (content instanceof Spanned) { + Spanned spanned = (Spanned)content; + Object[] spans = spanned.getSpans(partialStartOffset, + partialEndOffset, ParcelableSpan.class); + int i = spans.length; + while (i > 0) { + i--; + int j = spanned.getSpanStart(spans[i]); + if (j < partialStartOffset) partialStartOffset = j; + j = spanned.getSpanEnd(spans[i]); + if (j > partialEndOffset) partialEndOffset = j; + } + } + outText.partialStartOffset = partialStartOffset; + outText.partialEndOffset = partialEndOffset; + // Now use the delta to determine the actual amount of text + // we need. + partialEndOffset += delta; + if (partialEndOffset > N) { + partialEndOffset = N; + } else if (partialEndOffset < 0) { + partialEndOffset = 0; + } + } + if ((request.flags&InputConnection.GET_TEXT_WITH_STYLES) != 0) { + outText.text = content.subSequence(partialStartOffset, + partialEndOffset); + } else { + outText.text = TextUtils.substring(content, partialStartOffset, + partialEndOffset); + } outText.startOffset = 0; outText.selectionStart = Selection.getSelectionStart(content); outText.selectionEnd = Selection.getSelectionEnd(content); @@ -3819,19 +4024,45 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return false; } - void reportExtractedText() { - if (mInputMethodState != null) { + boolean reportExtractedText() { + final InputMethodState ims = mInputMethodState; + if (ims != null && ims.mContentChanged) { + ims.mContentChanged = false; final ExtractedTextRequest req = mInputMethodState.mExtracting; if (req != null) { InputMethodManager imm = InputMethodManager.peekInstance(); if (imm != null) { - if (extractText(req, mInputMethodState.mTmpExtracted)) { + if (DEBUG_EXTRACT) Log.v(TAG, "Retrieving extracted start=" + + ims.mChangedStart + " end=" + ims.mChangedEnd + + " delta=" + ims.mChangedDelta); + if (extractTextInternal(req, ims.mChangedStart, ims.mChangedEnd, + ims.mChangedDelta, ims.mTmpExtracted)) { + if (DEBUG_EXTRACT) Log.v(TAG, "Reporting extracted start=" + + ims.mTmpExtracted.partialStartOffset + + " end=" + ims.mTmpExtracted.partialEndOffset + + ": " + ims.mTmpExtracted.text); imm.updateExtractedText(this, req.token, mInputMethodState.mTmpExtracted); + return true; } } } } + return false; + } + + /** + * This is used to remove all style-impacting spans from text before new + * extracted text is being replaced into it, so that we don't have any + * lingering spans applied during the replace. + */ + static void removeParcelableSpans(Spannable spannable, int start, int end) { + Object[] spans = spannable.getSpans(start, end, ParcelableSpan.class); + int i = spans.length; + while (i > 0) { + i--; + spannable.removeSpan(spans[i]); + } } /** @@ -3839,7 +4070,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * returned by {@link #extractText(ExtractedTextRequest, ExtractedText)}. */ public void setExtractedText(ExtractedText text) { - setText(text.text, TextView.BufferType.EDITABLE); + Editable content = getEditableText(); + if (content == null) { + setText(text.text, TextView.BufferType.EDITABLE); + } else if (text.partialStartOffset < 0) { + removeParcelableSpans(content, 0, content.length()); + content.replace(0, content.length(), text.text); + } else { + final int N = content.length(); + int start = text.partialStartOffset; + if (start > N) start = N; + int end = text.partialEndOffset; + if (end > N) end = N; + removeParcelableSpans(content, start, end); + content.replace(start, end, text.text); + } Selection.setSelection((Spannable)getText(), text.selectionStart, text.selectionEnd); } @@ -3866,36 +4111,91 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public void onCommitCompletion(CompletionInfo text) { } + public void beginBatchEdit() { + final InputMethodState ims = mInputMethodState; + if (ims != null) { + int nesting = ++ims.mBatchEditNesting; + if (nesting == 1) { + ims.mCursorChanged = false; + ims.mChangedDelta = 0; + if (ims.mContentChanged) { + // We already have a pending change from somewhere else, + // so turn this into a full update. + ims.mChangedStart = 0; + ims.mChangedEnd = mText.length(); + } else { + ims.mChangedStart = -1; + ims.mChangedEnd = -1; + ims.mContentChanged = false; + } + onBeginBatchEdit(); + } + } + } + + public void endBatchEdit() { + final InputMethodState ims = mInputMethodState; + if (ims != null) { + int nesting = --ims.mBatchEditNesting; + if (nesting == 0) { + finishBatchEdit(ims); + } + } + } + + void ensureEndedBatchEdit() { + final InputMethodState ims = mInputMethodState; + if (ims != null && ims.mBatchEditNesting != 0) { + ims.mBatchEditNesting = 0; + finishBatchEdit(ims); + } + } + + void finishBatchEdit(final InputMethodState ims) { + onEndBatchEdit(); + + if (ims.mContentChanged) { + updateAfterEdit(); + reportExtractedText(); + } else if (ims.mCursorChanged) { + // Cheezy way to get us to report the current cursor location. + invalidateCursor(); + } + } + + void updateAfterEdit() { + invalidate(); + int curs = Selection.getSelectionStart(mText); + + if (curs >= 0 || (mGravity & Gravity.VERTICAL_GRAVITY_MASK) == + Gravity.BOTTOM) { + registerForPreDraw(); + } + + if (curs >= 0) { + mHighlightPathBogus = true; + + if (isFocused()) { + mShowCursor = SystemClock.uptimeMillis(); + makeBlink(); + } + } + + checkForResize(); + } + /** * Called by the framework in response to a request to begin a batch - * of edit operations from the current input method, as a result of - * it calling {@link InputConnection#beginBatchEdit - * InputConnection.beginBatchEdit()}. The default implementation sets - * up the TextView's internal state to take care of this; if overriding - * you should call through to the super class. + * of edit operations through a call to link {@link #beginBatchEdit()}. */ public void onBeginBatchEdit() { - if (mInputMethodState != null) { - // XXX we should be smarter here, such as not doing invalidates - // until all edits are done. - mInputMethodState.mBatchEditing = true; - } } /** * Called by the framework in response to a request to end a batch - * of edit operations from the current input method, as a result of - * it calling {@link InputConnection#endBatchEdit - * InputConnection.endBatchEdit()}. The default implementation sets - * up the TextView's internal state to take care of this; if overriding - * you should call through to the super class. + * of edit operations through a call to link {@link #endBatchEdit}. */ public void onEndBatchEdit() { - if (mInputMethodState != null) { - mInputMethodState.mBatchEditing = false; - // Cheezy way to get us to report the current cursor location. - invalidateCursor(); - } } /** @@ -4542,9 +4842,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Returns true if anything changed. + * Move the point, specified by the offset, into the view if it is needed. + * This has to be called after layout. Returns true if anything changed. */ - private boolean bringPointIntoView(int offset) { + public boolean bringPointIntoView(int offset) { boolean changed = false; int line = mLayout.getLineForOffset(offset); @@ -4794,7 +5095,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_singleLine */ public void setSingleLine(boolean singleLine) { - mSingleLine = singleLine; if ((mInputType&EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) { if (singleLine) { @@ -4803,19 +5103,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mInputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; } } + applySingleLine(singleLine, true); + } + private void applySingleLine(boolean singleLine, boolean applyTransformation) { + mSingleLine = singleLine; if (singleLine) { setLines(1); setHorizontallyScrolling(true); - setTransformationMethod(SingleLineTransformationMethod. - getInstance()); + if (applyTransformation) { + setTransformationMethod(SingleLineTransformationMethod. + getInstance()); + } } else { setMaxLines(Integer.MAX_VALUE); setHorizontallyScrolling(false); - setTransformationMethod(null); + if (applyTransformation) { + setTransformationMethod(null); + } } } - + /** * Causes words in the text that are longer than the view is wide * to be ellipsized instead of broken in the middle. You may also @@ -4938,6 +5246,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener float mScroll; Marquee(TextView v) { + final float density = v.getContext().getResources().getDisplayMetrics().density; + mScrollUnit = (MARQUEE_PIXELS_PER_SECOND * density) / (float) MARQUEE_RESOLUTION; mView = new WeakReference<TextView>(v); } @@ -5006,7 +5316,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (textView != null && textView.mLayout != null) { mStatus = MARQUEE_STARTING; mScroll = 0.0f; - mScrollUnit = MARQUEE_PIXELS_PER_SECOND / (float) MARQUEE_RESOLUTION; mMaxScroll = textView.mLayout.getLineWidth(0) - (textView.getWidth() - textView.getCompoundPaddingLeft() - textView.getCompoundPaddingRight()); textView.invalidate(); @@ -5046,6 +5355,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * This method is called when the selection has changed, in case any + * subclasses would like to know. + * + * @param selStart The new selection start location. + * @param selEnd The new selection end location. + */ + protected void onSelectionChanged(int selStart, int selEnd) { + } + + /** * Adds a TextWatcher to the list of those whose methods are called * whenever this TextView's text changes. * <p> @@ -5123,26 +5442,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ void handleTextChanged(CharSequence buffer, int start, int before, int after) { - invalidate(); - - int curs = Selection.getSelectionStart(buffer); - - if (curs >= 0 || (mGravity & Gravity.VERTICAL_GRAVITY_MASK) == - Gravity.BOTTOM) { - registerForPreDraw(); - } - - if (curs >= 0) { - mHighlightPathBogus = true; - - if (isFocused()) { - mShowCursor = SystemClock.uptimeMillis(); - makeBlink(); + final InputMethodState ims = mInputMethodState; + if (ims == null || ims.mBatchEditNesting == 0) { + updateAfterEdit(); + } + if (ims != null) { + ims.mContentChanged = true; + if (ims.mChangedStart < 0) { + ims.mChangedStart = start; + ims.mChangedEnd = start+before; + } else { + if (ims.mChangedStart > start) ims.mChangedStart = start; + if (ims.mChangedEnd < (start+before)) ims.mChangedEnd = start+before; } + ims.mChangedDelta += after-before; } - - checkForResize(); - + sendOnTextChanged(buffer, start, before, after); onTextChanged(buffer, start, before, after); } @@ -5151,19 +5466,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * Not private so it can be called from an inner class without going * through a thunk. */ - void spanChange(Spanned buf, Object what, int o, int n) { + void spanChange(Spanned buf, Object what, int oldStart, int newStart, + int oldEnd, int newEnd) { // XXX Make the start and end move together if this ends up // spending too much time invalidating. + boolean selChanged = false; + int newSelStart=-1, newSelEnd=-1; + + final InputMethodState ims = mInputMethodState; + if (what == Selection.SELECTION_END) { mHighlightPathBogus = true; + selChanged = true; + newSelEnd = newStart; if (!isFocused()) { mSelectionMoved = true; } - if (o >= 0 || n >= 0) { - invalidateCursor(Selection.getSelectionStart(buf), o, n); + if (oldStart >= 0 || newStart >= 0) { + invalidateCursor(Selection.getSelectionStart(buf), oldStart, newStart); registerForPreDraw(); if (isFocused()) { @@ -5175,28 +5498,81 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (what == Selection.SELECTION_START) { mHighlightPathBogus = true; + selChanged = true; + newSelStart = newStart; if (!isFocused()) { mSelectionMoved = true; } - if (o >= 0 || n >= 0) { - invalidateCursor(Selection.getSelectionEnd(buf), o, n); + if (oldStart >= 0 || newStart >= 0) { + int end = Selection.getSelectionEnd(buf); + invalidateCursor(end, oldStart, newStart); } } + if (selChanged) { + if ((buf.getSpanFlags(what)&Spanned.SPAN_INTERMEDIATE) == 0) { + if (newSelStart < 0) { + newSelStart = Selection.getSelectionStart(buf); + } + if (newSelEnd < 0) { + newSelEnd = Selection.getSelectionEnd(buf); + } + onSelectionChanged(newSelStart, newSelEnd); + } + } + if (what instanceof UpdateAppearance || what instanceof ParagraphStyle) { - invalidate(); - mHighlightPathBogus = true; - checkForResize(); + if (ims == null || ims.mBatchEditNesting == 0) { + invalidate(); + mHighlightPathBogus = true; + checkForResize(); + } else { + ims.mContentChanged = true; + } } if (MetaKeyKeyListener.isMetaTracker(buf, what)) { mHighlightPathBogus = true; if (Selection.getSelectionStart(buf) >= 0) { - invalidateCursor(); + if (ims == null || ims.mBatchEditNesting == 0) { + invalidateCursor(); + } else { + ims.mCursorChanged = true; + } + } + } + + if (what instanceof ParcelableSpan) { + // If this is a span that can be sent to a remote process, + // the current extract editor would be interested in it. + if (ims != null && ims.mExtracting != null) { + if (ims.mBatchEditNesting != 0) { + if (oldStart >= 0) { + if (ims.mChangedStart > oldStart) { + ims.mChangedStart = oldStart; + } + if (ims.mChangedStart > oldEnd) { + ims.mChangedStart = oldEnd; + } + } + if (newStart >= 0) { + if (ims.mChangedStart > newStart) { + ims.mChangedStart = newStart; + } + if (ims.mChangedStart > newEnd) { + ims.mChangedStart = newEnd; + } + } + } else { + if (DEBUG_EXTRACT) Log.v(TAG, "Span change outside of batch: " + + oldStart + "-" + oldEnd + "," + + newStart + "-" + newEnd + what); + ims.mContentChanged = true; + } } } } @@ -5205,36 +5581,45 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener implements TextWatcher, SpanWatcher { public void beforeTextChanged(CharSequence buffer, int start, int before, int after) { + if (DEBUG_EXTRACT) Log.v(TAG, "beforeTextChanged start=" + start + + " before=" + before + " after=" + after + ": " + buffer); TextView.this.sendBeforeTextChanged(buffer, start, before, after); } public void onTextChanged(CharSequence buffer, int start, int before, int after) { + if (DEBUG_EXTRACT) Log.v(TAG, "onTextChanged start=" + start + + " before=" + before + " after=" + after + ": " + buffer); TextView.this.handleTextChanged(buffer, start, before, after); } public void afterTextChanged(Editable buffer) { + if (DEBUG_EXTRACT) Log.v(TAG, "afterTextChanged: " + buffer); TextView.this.sendAfterTextChanged(buffer); if (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) { MetaKeyKeyListener.stopSelecting(TextView.this, buffer); } - - TextView.this.reportExtractedText(); } public void onSpanChanged(Spannable buf, Object what, int s, int e, int st, int en) { - TextView.this.spanChange(buf, what, s, st); + if (DEBUG_EXTRACT) Log.v(TAG, "onSpanChanged s=" + s + " e=" + e + + " st=" + st + " en=" + en + " what=" + what + ": " + buf); + TextView.this.spanChange(buf, what, s, st, e, en); } public void onSpanAdded(Spannable buf, Object what, int s, int e) { - TextView.this.spanChange(buf, what, -1, s); + if (DEBUG_EXTRACT) Log.v(TAG, "onSpanAdded s=" + s + " e=" + e + + " what=" + what + ": " + buf); + TextView.this.spanChange(buf, what, -1, s, -1, e); } public void onSpanRemoved(Spannable buf, Object what, int s, int e) { - TextView.this.spanChange(buf, what, s, -1); + if (DEBUG_EXTRACT) Log.v(TAG, "onSpanRemoved s=" + s + " e=" + e + + " what=" + what + ": " + buf); + TextView.this.spanChange(buf, what, s, -1, e, -1); } } @@ -5258,6 +5643,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { mShowCursor = SystemClock.uptimeMillis(); + ensureEndedBatchEdit(); + if (focused) { int selStart = getSelectionStart(); int selEnd = getSelectionEnd(); @@ -5362,22 +5749,28 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public boolean onTouchEvent(MotionEvent event) { final boolean superResult = super.onTouchEvent(event); + final int action = event.getAction(); + /* * Don't handle the release after a long press, because it will * move the selection away from whatever the menu action was * trying to affect. */ - if (mEatTouchRelease && event.getAction() == MotionEvent.ACTION_UP) { + if (mEatTouchRelease && action == MotionEvent.ACTION_UP) { mEatTouchRelease = false; return superResult; } - if (mMovement != null && mText instanceof Spannable && - mLayout != null) { + if (mMovement != null && mText instanceof Spannable && mLayout != null) { + + if (action == MotionEvent.ACTION_DOWN) { + mScrolled = false; + } + boolean moved = mMovement.onTouchEvent(this, (Spannable) mText, event); if (mText instanceof Editable && onCheckIsTextEditor()) { - if (event.getAction() == MotionEvent.ACTION_UP && isFocused()) { + if (action == MotionEvent.ACTION_UP && isFocused() && !mScrolled) { InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(this, 0); @@ -5393,6 +5786,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override + public void cancelLongPress() { + super.cancelLongPress(); + mScrolled = true; + } + + @Override public boolean onTrackballEvent(MotionEvent event) { if (mMovement != null && mText instanceof Spannable && mLayout != null) { @@ -5568,28 +5967,28 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener switch (keyCode) { case KeyEvent.KEYCODE_A: if (canSelectAll()) { - return onMenu(ID_SELECT_ALL); + return onTextContextMenuItem(ID_SELECT_ALL); } break; case KeyEvent.KEYCODE_X: if (canCut()) { - return onMenu(ID_CUT); + return onTextContextMenuItem(ID_CUT); } break; case KeyEvent.KEYCODE_C: if (canCopy()) { - return onMenu(ID_COPY); + return onTextContextMenuItem(ID_COPY); } break; case KeyEvent.KEYCODE_V: if (canPaste()) { - return onMenu(ID_PASTE); + return onTextContextMenuItem(ID_PASTE); } break; @@ -5655,6 +6054,38 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return false; } + /** + * Returns a word to add to the dictionary from the context menu, + * or null if there is no cursor or no word at the cursor. + */ + private String getWordForDictionary() { + int end = getSelectionEnd(); + + if (end < 0) { + return null; + } + + int start = end; + char c; + int len = mText.length(); + + while (start > 0 && (((c = mTransformed.charAt(start - 1)) == '\'') || + (Character.isLetterOrDigit(c)))) { + start--; + } + + while (end < len && (((c = mTransformed.charAt(end)) == '\'') || + (Character.isLetterOrDigit(c)))) { + end++; + } + + if (start == end) { + return null; + } + + return TextUtils.substring(mTransformed, start, end); + } + @Override protected void onCreateContextMenu(ContextMenu menu) { super.onCreateContextMenu(menu); @@ -5696,7 +6127,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setOnMenuItemClickListener(handler); added = true; } else { - menu.add(0, ID_SELECT_TEXT, 0, + menu.add(0, ID_START_SELECTING_TEXT, 0, com.android.internal.R.string.selectText). setOnMenuItemClickListener(handler); added = true; @@ -5755,34 +6186,60 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null && imm.isActive(this)) { - menu.add(1, ID_SWITCH_IME, 0, com.android.internal.R.string.inputMethod). + if (isInputMethodTarget()) { + menu.add(1, ID_SWITCH_INPUT_METHOD, 0, com.android.internal.R.string.inputMethod). setOnMenuItemClickListener(handler); added = true; } + String word = getWordForDictionary(); + if (word != null) { + menu.add(1, ID_ADD_TO_DICTIONARY, 0, + getContext().getString(com.android.internal.R.string.addToDictionary, word)). + setOnMenuItemClickListener(handler); + added = true; + + } + if (added) { menu.setHeaderTitle(com.android.internal.R.string.editTextMenuTitle); } } - private static final int ID_SELECT_ALL = com.android.internal.R.id.selectAll; - private static final int ID_SELECT_TEXT = com.android.internal.R.id.selectText; - private static final int ID_STOP_SELECTING_TEXT = com.android.internal.R.id.stopSelectingText; - private static final int ID_CUT = com.android.internal.R.id.cut; - private static final int ID_COPY = com.android.internal.R.id.copy; - private static final int ID_PASTE = com.android.internal.R.id.paste; - private static final int ID_COPY_URL = com.android.internal.R.id.copyUrl; - private static final int ID_SWITCH_IME = com.android.internal.R.id.inputMethod; + /** + * Returns whether this text view is a current input method target. The + * default implementation just checks with {@link InputMethodManager}. + */ + public boolean isInputMethodTarget() { + InputMethodManager imm = InputMethodManager.peekInstance(); + return imm != null && imm.isActive(this); + } + + private static final int ID_SELECT_ALL = android.R.id.selectAll; + private static final int ID_START_SELECTING_TEXT = android.R.id.startSelectingText; + private static final int ID_STOP_SELECTING_TEXT = android.R.id.stopSelectingText; + private static final int ID_CUT = android.R.id.cut; + private static final int ID_COPY = android.R.id.copy; + private static final int ID_PASTE = android.R.id.paste; + private static final int ID_COPY_URL = android.R.id.copyUrl; + private static final int ID_SWITCH_INPUT_METHOD = android.R.id.switchInputMethod; + private static final int ID_ADD_TO_DICTIONARY = android.R.id.addToDictionary; private class MenuHandler implements MenuItem.OnMenuItemClickListener { public boolean onMenuItemClick(MenuItem item) { - return onMenu(item.getItemId()); + return onTextContextMenuItem(item.getItemId()); } } - private boolean onMenu(int id) { + /** + * Called when a context menu option for the text view is selected. Currently + * this will be one of: {@link android.R.id#selectAll}, + * {@link android.R.id#startSelectingText}, {@link android.R.id#stopSelectingText}, + * {@link android.R.id#cut}, {@link android.R.id#copy}, + * {@link android.R.id#paste}, {@link android.R.id#copyUrl}, + * or {@link android.R.id#switchInputMethod}. + */ + public boolean onTextContextMenuItem(int id) { int selStart = getSelectionStart(); int selEnd = getSelectionEnd(); @@ -5807,10 +6264,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener switch (id) { case ID_SELECT_ALL: Selection.setSelection((Spannable) mText, 0, - mText.length()); + mText.length()); return true; - case ID_SELECT_TEXT: + case ID_START_SELECTING_TEXT: MetaKeyKeyListener.startSelecting(this, (Spannable) mText); return true; @@ -5865,12 +6322,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return true; - case ID_SWITCH_IME: + case ID_SWITCH_INPUT_METHOD: InputMethodManager imm = InputMethodManager.peekInstance(); if (imm != null) { imm.showInputMethodPicker(); } return true; + + case ID_ADD_TO_DICTIONARY: + String word = getWordForDictionary(); + + if (word != null) { + Intent i = new Intent("com.android.settings.USER_DICTIONARY_INSERT"); + i.putExtra("word", word); + getContext().startActivity(i); + } + + return true; } return false; @@ -5885,8 +6353,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return false; } - private boolean mEatTouchRelease = false; - @ViewDebug.ExportedProperty private CharSequence mText; private CharSequence mTransformed; diff --git a/core/java/android/widget/ViewAnimator.java b/core/java/android/widget/ViewAnimator.java index acc9c46..8c652e5 100644 --- a/core/java/android/widget/ViewAnimator.java +++ b/core/java/android/widget/ViewAnimator.java @@ -144,6 +144,56 @@ public class ViewAnimator extends FrameLayout { } } + @Override + public void removeAllViews() { + super.removeAllViews(); + mWhichChild = 0; + mFirstTime = true; + } + + @Override + public void removeView(View view) { + final int index = indexOfChild(view); + if (index >= 0) { + removeViewAt(index); + } + } + + @Override + public void removeViewAt(int index) { + super.removeViewAt(index); + final int childCount = getChildCount(); + if (childCount == 0) { + mWhichChild = 0; + mFirstTime = true; + } else if (mWhichChild >= childCount) { + // Displayed is above child count, so float down to top of stack + setDisplayedChild(childCount - 1); + } else if (mWhichChild == index) { + // Displayed was removed, so show the new child living in its place + setDisplayedChild(mWhichChild); + } + } + + public void removeViewInLayout(View view) { + removeView(view); + } + + public void removeViews(int start, int count) { + super.removeViews(start, count); + if (getChildCount() == 0) { + mWhichChild = 0; + mFirstTime = true; + } else if (mWhichChild >= start && mWhichChild < start + count) { + // Try showing new displayed child, wrapping if needed + setDisplayedChild(mWhichChild); + } + } + + public void removeViewsInLayout(int start, int count) { + removeViews(start, count); + } + /** * Returns the View corresponding to the currently displayed child. * diff --git a/core/java/android/widget/ZoomButton.java b/core/java/android/widget/ZoomButton.java index 5df8c8a..df3f307 100644 --- a/core/java/android/widget/ZoomButton.java +++ b/core/java/android/widget/ZoomButton.java @@ -54,7 +54,7 @@ public class ZoomButton extends ImageButton implements OnLongClickListener { public ZoomButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mHandler = new Handler(); - mGestureDetector = new GestureDetector(new SimpleOnGestureListener() { + mGestureDetector = new GestureDetector(context, new SimpleOnGestureListener() { @Override public void onLongPress(MotionEvent e) { onLongClick(ZoomButton.this); diff --git a/core/java/android/widget/ZoomControls.java b/core/java/android/widget/ZoomControls.java index 1fd662c..fdc05b6 100644 --- a/core/java/android/widget/ZoomControls.java +++ b/core/java/android/widget/ZoomControls.java @@ -81,7 +81,9 @@ public class ZoomControls extends LinearLayout { } public void show() { - fade(View.VISIBLE, 0.0f, 1.0f); + if (ZoomRingController.useOldZoom(mContext)) { + fade(View.VISIBLE, 0.0f, 1.0f); + } } public void hide() { diff --git a/core/java/android/widget/ZoomRing.java b/core/java/android/widget/ZoomRing.java new file mode 100644 index 0000000..20d6056 --- /dev/null +++ b/core/java/android/widget/ZoomRing.java @@ -0,0 +1,423 @@ +package android.widget; + +import com.android.internal.R; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.util.AttributeSet; +import android.util.Log; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; + +/** + * @hide + */ +public class ZoomRing extends View { + + // TODO: move to ViewConfiguration? + private static final int DOUBLE_TAP_DISMISS_TIMEOUT = ViewConfiguration.getJumpTapTimeout(); + // TODO: get from theme + private static final int DISABLED_ALPHA = 160; + + private static final String TAG = "ZoomRing"; + + // TODO: xml + private static final int THUMB_DISTANCE = 63; + + /** To avoid floating point calculations, we multiply radians by this value. */ + public static final int RADIAN_INT_MULTIPLIER = 100000000; + /** PI using our multiplier. */ + public static final int PI_INT_MULTIPLIED = (int) (Math.PI * RADIAN_INT_MULTIPLIER); + /** PI/2 using our multiplier. */ + private static final int HALF_PI_INT_MULTIPLIED = PI_INT_MULTIPLIED / 2; + + private static final int THUMB_GRAB_SLOP = PI_INT_MULTIPLIED / 4; + + /** The cached X of our center. */ + private int mCenterX; + /** The cached Y of our center. */ + private int mCenterY; + + /** The angle of the thumb (in int radians) */ + private int mThumbAngle; + private boolean mIsThumbAngleValid; + private int mThumbCenterX; + private int mThumbCenterY; + private int mThumbHalfWidth; + private int mThumbHalfHeight; + + private int mCallbackThreshold = Integer.MAX_VALUE; + + /** The accumulated amount of drag for the thumb (in int radians). */ + private int mAcculumalatedThumbDrag = 0; + + /** The inner radius of the track. */ + private int mBoundInnerRadiusSquared = 0; + /** The outer radius of the track. */ + private int mBoundOuterRadiusSquared = Integer.MAX_VALUE; + + private int mPreviousWidgetDragX; + private int mPreviousWidgetDragY; + + private boolean mDrawThumb = true; + private Drawable mThumbDrawable; + + private static final int MODE_IDLE = 0; + private static final int MODE_DRAG_THUMB = 1; + private static final int MODE_MOVE_ZOOM_RING = 2; + private static final int MODE_TAP_DRAG = 3; + private int mMode; + + private long mPreviousTapTime; + + private Handler mHandler = new Handler(); + + private Disabler mDisabler = new Disabler(); + + private OnZoomRingCallback mCallback; + + private boolean mResetThumbAutomatically = true; + private int mThumbDragStartAngle; + + public ZoomRing(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + // TODO get drawable from style instead + Resources res = context.getResources(); + mThumbDrawable = res.getDrawable(R.drawable.zoom_ring_thumb); + + // TODO: add padding to drawable + setBackgroundResource(R.drawable.zoom_ring_track); + // TODO get from style + setBounds(30, Integer.MAX_VALUE); + + mThumbHalfHeight = mThumbDrawable.getIntrinsicHeight() / 2; + mThumbHalfWidth = mThumbDrawable.getIntrinsicWidth() / 2; + + mCallbackThreshold = PI_INT_MULTIPLIED / 6; + } + + public ZoomRing(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ZoomRing(Context context) { + this(context, null); + } + + public void setCallback(OnZoomRingCallback callback) { + mCallback = callback; + } + + // TODO: rename + public void setCallbackThreshold(int callbackThreshold) { + mCallbackThreshold = callbackThreshold; + } + + // TODO: from XML too + public void setBounds(int innerRadius, int outerRadius) { + mBoundInnerRadiusSquared = innerRadius * innerRadius; + if (mBoundInnerRadiusSquared < innerRadius) { + // Prevent overflow + mBoundInnerRadiusSquared = Integer.MAX_VALUE; + } + + mBoundOuterRadiusSquared = outerRadius * outerRadius; + if (mBoundOuterRadiusSquared < outerRadius) { + // Prevent overflow + mBoundOuterRadiusSquared = Integer.MAX_VALUE; + } + } + + public void setThumbAngle(int angle) { + mThumbAngle = angle; + mThumbCenterX = (int) (Math.cos(1f * angle / RADIAN_INT_MULTIPLIER) * THUMB_DISTANCE) + + mCenterX; + mThumbCenterY = (int) (Math.sin(1f * angle / RADIAN_INT_MULTIPLIER) * THUMB_DISTANCE) + * -1 + mCenterY; + invalidate(); + } + + public void resetThumbAngle() { + if (mResetThumbAutomatically) { + setThumbAngle(HALF_PI_INT_MULTIPLIED); + } + } + + public void setResetThumbAutomatically(boolean resetThumbAutomatically) { + mResetThumbAutomatically = resetThumbAutomatically; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec), + resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec)); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, + int bottom) { + super.onLayout(changed, left, top, right, bottom); + + // Cache the center point + mCenterX = (right - left) / 2; + mCenterY = (bottom - top) / 2; + + // Done here since we now have center, which is needed to calculate some + // aux info for thumb angle + if (mThumbAngle == Integer.MIN_VALUE) { + resetThumbAngle(); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + return handleTouch(event.getAction(), event.getEventTime(), + (int) event.getX(), (int) event.getY(), (int) event.getRawX(), + (int) event.getRawY()); + } + + private void resetState() { + mMode = MODE_IDLE; + mPreviousWidgetDragX = mPreviousWidgetDragY = Integer.MIN_VALUE; + mAcculumalatedThumbDrag = 0; + mIsThumbAngleValid = false; + } + + public void setTapDragMode(boolean tapDragMode, int x, int y) { + resetState(); + mMode = tapDragMode ? MODE_TAP_DRAG : MODE_IDLE; + mIsThumbAngleValid = false; + + if (tapDragMode && mCallback != null) { + onThumbDragStarted(getAngle(x - mCenterX, y - mCenterY)); + } + } + + public boolean handleTouch(int action, long time, int x, int y, int rawX, int rawY) { + switch (action) { + + case MotionEvent.ACTION_DOWN: + if (mPreviousTapTime + DOUBLE_TAP_DISMISS_TIMEOUT >= time) { + if (mCallback != null) { + mCallback.onZoomRingDismissed(); + } + } else { + mPreviousTapTime = time; + } + resetState(); + return true; + + case MotionEvent.ACTION_MOVE: + // Fall through to code below switch + break; + + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + if (mCallback != null) { + if (mMode == MODE_MOVE_ZOOM_RING) { + mCallback.onZoomRingMovingStopped(); + } else if (mMode == MODE_DRAG_THUMB || mMode == MODE_TAP_DRAG) { + onThumbDragStopped(getAngle(x - mCenterX, y - mCenterY)); + } + } + mDisabler.setEnabling(true); + return true; + + default: + return false; + } + + // local{X,Y} will be where the center of the widget is (0,0) + int localX = x - mCenterX; + int localY = y - mCenterY; + boolean isTouchingThumb = true; + boolean isInBounds = true; + int touchAngle = getAngle(localX, localY); + + int radiusSquared = localX * localX + localY * localY; + if (radiusSquared < mBoundInnerRadiusSquared || + radiusSquared > mBoundOuterRadiusSquared) { + // Out-of-bounds + isTouchingThumb = false; + isInBounds = false; + } + + int deltaThumbAndTouch = getDelta(touchAngle, mThumbAngle); + int absoluteDeltaThumbAndTouch = deltaThumbAndTouch >= 0 ? + deltaThumbAndTouch : -deltaThumbAndTouch; + if (isTouchingThumb && + absoluteDeltaThumbAndTouch > THUMB_GRAB_SLOP) { + // Didn't grab close enough to the thumb + isTouchingThumb = false; + } + + if (mMode == MODE_IDLE) { + mMode = isTouchingThumb ? MODE_DRAG_THUMB : MODE_MOVE_ZOOM_RING; + + if (mCallback != null) { + if (mMode == MODE_DRAG_THUMB) { + onThumbDragStarted(touchAngle); + } else if (mMode == MODE_MOVE_ZOOM_RING) { + mCallback.onZoomRingMovingStarted(); + } + } + } + + if (mMode == MODE_DRAG_THUMB || mMode == MODE_TAP_DRAG) { + if (isInBounds) { + onThumbDragged(touchAngle, mIsThumbAngleValid ? deltaThumbAndTouch : 0); + } else { + mIsThumbAngleValid = false; + } + } else if (mMode == MODE_MOVE_ZOOM_RING) { + onZoomRingMoved(rawX, rawY); + } + + return true; + } + + private int getDelta(int angle1, int angle2) { + int delta = angle1 - angle2; + + // Assume this is a result of crossing over the discontinuous 0 -> 2pi + if (delta > PI_INT_MULTIPLIED || delta < -PI_INT_MULTIPLIED) { + // Bring both the radians and previous angle onto a continuous range + if (angle1 < HALF_PI_INT_MULTIPLIED) { + // Same as deltaRadians = (radians + 2PI) - previousAngle + delta += PI_INT_MULTIPLIED * 2; + } else if (angle2 < HALF_PI_INT_MULTIPLIED) { + // Same as deltaRadians = radians - (previousAngle + 2PI) + delta -= PI_INT_MULTIPLIED * 2; + } + } + + return delta; + } + + private void onThumbDragStarted(int startAngle) { + mThumbDragStartAngle = startAngle; + mCallback.onZoomRingThumbDraggingStarted(startAngle); + } + + private void onThumbDragged(int touchAngle, int deltaAngle) { + mAcculumalatedThumbDrag += deltaAngle; + if (mAcculumalatedThumbDrag > mCallbackThreshold + || mAcculumalatedThumbDrag < -mCallbackThreshold) { + if (mCallback != null) { + boolean canStillZoom = mCallback.onZoomRingThumbDragged( + mAcculumalatedThumbDrag / mCallbackThreshold, + mAcculumalatedThumbDrag, mThumbDragStartAngle, touchAngle); + mDisabler.setEnabling(canStillZoom); + } + mAcculumalatedThumbDrag = 0; + } + + setThumbAngle(touchAngle); + mIsThumbAngleValid = true; + } + + private void onThumbDragStopped(int stopAngle) { + mCallback.onZoomRingThumbDraggingStopped(stopAngle); + } + + private void onZoomRingMoved(int x, int y) { + if (mPreviousWidgetDragX != Integer.MIN_VALUE) { + int deltaX = x - mPreviousWidgetDragX; + int deltaY = y - mPreviousWidgetDragY; + + if (mCallback != null) { + mCallback.onZoomRingMoved(deltaX, deltaY); + } + } + + mPreviousWidgetDragX = x; + mPreviousWidgetDragY = y; + } + + @Override + public void onWindowFocusChanged(boolean hasWindowFocus) { + super.onWindowFocusChanged(hasWindowFocus); + + if (!hasWindowFocus && mCallback != null) { + mCallback.onZoomRingDismissed(); + } + } + + private int getAngle(int localX, int localY) { + int radians = (int) (Math.atan2(localY, localX) * RADIAN_INT_MULTIPLIER); + + // Convert from [-pi,pi] to {0,2pi] + if (radians < 0) { + return -radians; + } else if (radians > 0) { + return 2 * PI_INT_MULTIPLIED - radians; + } else { + return 0; + } + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (mDrawThumb) { + mThumbDrawable.setBounds(mThumbCenterX - mThumbHalfWidth, mThumbCenterY + - mThumbHalfHeight, mThumbCenterX + mThumbHalfWidth, mThumbCenterY + + mThumbHalfHeight); + mThumbDrawable.draw(canvas); + } + } + + private class Disabler implements Runnable { + private static final int DELAY = 15; + private static final float ENABLE_RATE = 1.05f; + private static final float DISABLE_RATE = 0.95f; + + private int mAlpha = 255; + private boolean mEnabling; + + public int getAlpha() { + return mAlpha; + } + + public void setEnabling(boolean enabling) { + if ((enabling && mAlpha != 255) || (!enabling && mAlpha != DISABLED_ALPHA)) { + mEnabling = enabling; + post(this); + } + } + + public void run() { + mAlpha *= mEnabling ? ENABLE_RATE : DISABLE_RATE; + if (mAlpha < DISABLED_ALPHA) { + mAlpha = DISABLED_ALPHA; + } else if (mAlpha > 255) { + mAlpha = 255; + } else { + // Still more to go + postDelayed(this, DELAY); + } + + getBackground().setAlpha(mAlpha); + invalidate(); + } + } + + public interface OnZoomRingCallback { + void onZoomRingMovingStarted(); + boolean onZoomRingMoved(int deltaX, int deltaY); + void onZoomRingMovingStopped(); + + void onZoomRingThumbDraggingStarted(int startAngle); + boolean onZoomRingThumbDragged(int numLevels, int dragAmount, int startAngle, int curAngle); + void onZoomRingThumbDraggingStopped(int endAngle); + + void onZoomRingDismissed(); + } + +} diff --git a/core/java/android/widget/ZoomRingController.java b/core/java/android/widget/ZoomRingController.java new file mode 100644 index 0000000..2ca0374 --- /dev/null +++ b/core/java/android/widget/ZoomRingController.java @@ -0,0 +1,819 @@ +/* + * 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 android.widget; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Message; +import android.os.SystemClock; +import android.provider.Settings; +import android.util.Log; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; + +// TODO: make sure no px values exist, only dip (scale if necessary from Viewconfiguration) + +/** + * TODO: Docs + * @hide + */ +public class ZoomRingController implements ZoomRing.OnZoomRingCallback, + View.OnTouchListener, View.OnKeyListener { + + private static final int SHOW_TUTORIAL_TOAST_DELAY = 1000; + + private static final int ZOOM_RING_RECENTERING_DURATION = 500; + + private static final String TAG = "ZoomRing"; + + public static final boolean USE_OLD_ZOOM = false; + public static boolean useOldZoom(Context context) { + return Settings.System.getInt(context.getContentResolver(), "zoom", 1) == 0; + } + + private static final int ZOOM_CONTROLS_TIMEOUT = + (int) ViewConfiguration.getZoomControlsTimeout(); + + // TODO: move these to ViewConfiguration or re-use existing ones + // TODO: scale px values based on latest from ViewConfiguration + private static final int SECOND_TAP_TIMEOUT = 500; + private static final int ZOOM_RING_DISMISS_DELAY = SECOND_TAP_TIMEOUT / 2; + private static final int SECOND_TAP_SLOP = 70; + private static final int SECOND_TAP_MOVE_SLOP = 15; + private static final int MAX_PAN_GAP = 30; + + private static final String SETTING_NAME_SHOWN_TOAST = "shown_zoom_ring_toast"; + + private Context mContext; + private WindowManager mWindowManager; + + /** + * The view that is being zoomed by this zoom ring. + */ + private View mOwnerView; + + /** + * The bounds of the owner view in global coordinates. This is recalculated + * each time the zoom ring is shown. + */ + private Rect mOwnerViewBounds = new Rect(); + + /** + * The container that is added as a window. + */ + private FrameLayout mContainer; + private LayoutParams mContainerLayoutParams; + + /* + * Tap-drag is an interaction where the user first taps and then (quickly) + * does the clockwise or counter-clockwise drag. In reality, this is: (down, + * up, down, move in circles, up). This differs from the usual events of: + * (down, up, down, up, down, move in circles, up). While the only + * difference is the omission of an (up, down), for power-users this is a + * pretty big improvement as it now only requires them to focus on the + * screen once (for the first tap down) instead of twice (for the first tap + * down and then to grab the thumb). + */ + private int mTapDragStartX; + private int mTapDragStartY; + + private static final int TOUCH_MODE_IDLE = 0; + private static final int TOUCH_MODE_WAITING_FOR_SECOND_TAP = 1; + private static final int TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT = 2; + private static final int TOUCH_MODE_FORWARDING_FOR_TAP_DRAG = 3; + private int mTouchMode; + + private boolean mIsZoomRingVisible; + + private ZoomRing mZoomRing; + private int mZoomRingWidth; + private int mZoomRingHeight; + + /** Invokes panning of owner view if the zoom ring is touching an edge. */ + private Panner mPanner = new Panner(); + + private ImageView mPanningArrows; + private Animation mPanningArrowsEnterAnimation; + private Animation mPanningArrowsExitAnimation; + + private Rect mTempRect = new Rect(); + + private OnZoomListener mCallback; + + private ViewConfiguration mViewConfig; + + /** + * When the zoom ring is centered on screen, this will be the x value used + * for the container's layout params. + */ + private int mCenteredContainerX = Integer.MIN_VALUE; + + /** + * When the zoom ring is centered on screen, this will be the y value used + * for the container's layout params. + */ + private int mCenteredContainerY = Integer.MIN_VALUE; + + /** + * Scroller used to re-center the zoom ring if the user had dragged it to a + * corner and then double-taps any point on the owner view (the owner view + * will center the double-tapped point, but we should re-center the zoom + * ring). + * <p> + * The (x,y) of the scroller is the (x,y) of the container's layout params. + */ + private Scroller mScroller; + + /** + * When showing the zoom ring, we add the view as a new window. However, + * there is logic that needs to know the size of the zoom ring which is + * determined after it's laid out. Therefore, we must post this logic onto + * the UI thread so it will be exceuted AFTER the layout. This is the logic. + */ + private Runnable mPostedVisibleInitializer; + + // TODO: need a better way to persist this value, becuase right now this + // requires the WRITE_SETTINGS perimssion which the app may not have +// private Runnable mShowTutorialToast = new Runnable() { +// public void run() { +// if (Settings.System.getInt(mContext.getContentResolver(), +// SETTING_NAME_SHOWN_TOAST, 0) == 1) { +// return; +// } +// try { +// Settings.System.putInt(mContext.getContentResolver(), SETTING_NAME_SHOWN_TOAST, 1); +// } catch (SecurityException e) { +// // The app does not have permission to clear this flag, oh well! +// } +// +// Toast.makeText(mContext, +// com.android.internal.R.string.tutorial_double_tap_to_zoom_message, +// Toast.LENGTH_LONG).show(); +// } +// }; + + private IntentFilter mConfigurationChangedFilter = + new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED); + + private BroadcastReceiver mConfigurationChangedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (!mIsZoomRingVisible) return; + + mHandler.removeMessages(MSG_POST_CONFIGURATION_CHANGED); + mHandler.sendEmptyMessage(MSG_POST_CONFIGURATION_CHANGED); + } + }; + + /** Keeps the scroller going (or starts it). */ + private static final int MSG_SCROLLER_TICK = 1; + /** When configuration changes, this is called after the UI thread is idle. */ + private static final int MSG_POST_CONFIGURATION_CHANGED = 2; + /** Used to delay the zoom ring dismissal. */ + private static final int MSG_DISMISS_ZOOM_RING = 3; + + private Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SCROLLER_TICK: + onScrollerTick(); + break; + + case MSG_POST_CONFIGURATION_CHANGED: + onPostConfigurationChanged(); + break; + + case MSG_DISMISS_ZOOM_RING: + setVisible(false); + break; + } + + } + }; + + public ZoomRingController(Context context, View ownerView) { + mContext = context; + mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + + mOwnerView = ownerView; + + mZoomRing = new ZoomRing(context); + mZoomRing.setLayoutParams(new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.WRAP_CONTENT, + FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER)); + mZoomRing.setCallback(this); + + createPanningArrows(); + + mContainer = new FrameLayout(context); + mContainer.setMeasureAllChildren(true); + mContainer.setOnTouchListener(this); + + mContainer.addView(mZoomRing); + mContainer.addView(mPanningArrows); + mContainer.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + + mContainerLayoutParams = new LayoutParams(); + mContainerLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; + mContainerLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL | + LayoutParams.FLAG_NOT_FOCUSABLE | + LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | LayoutParams.FLAG_LAYOUT_NO_LIMITS; + mContainerLayoutParams.height = LayoutParams.WRAP_CONTENT; + mContainerLayoutParams.width = LayoutParams.WRAP_CONTENT; + mContainerLayoutParams.type = LayoutParams.TYPE_APPLICATION_PANEL; + mContainerLayoutParams.format = PixelFormat.TRANSLUCENT; + // TODO: make a new animation for this + mContainerLayoutParams.windowAnimations = com.android.internal.R.style.Animation_Dialog; + + mScroller = new Scroller(context, new DecelerateInterpolator()); + + mViewConfig = ViewConfiguration.get(context); + +// mHandler.postDelayed(mShowTutorialToast, SHOW_TUTORIAL_TOAST_DELAY); + } + + private void createPanningArrows() { + // TODO: style + mPanningArrows = new ImageView(mContext); + mPanningArrows.setImageResource(com.android.internal.R.drawable.zoom_ring_arrows); + mPanningArrows.setLayoutParams(new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.WRAP_CONTENT, + FrameLayout.LayoutParams.WRAP_CONTENT, + Gravity.CENTER)); + mPanningArrows.setVisibility(View.GONE); + + mPanningArrowsEnterAnimation = AnimationUtils.loadAnimation(mContext, + com.android.internal.R.anim.fade_in); + mPanningArrowsExitAnimation = AnimationUtils.loadAnimation(mContext, + com.android.internal.R.anim.fade_out); + } + + /** + * Sets the angle (in radians) a user must travel in order for the client to + * get a callback. Once there is a callback, the accumulator resets. For + * example, if you set this to PI/6, it will give a callback every time the + * user moves PI/6 amount on the ring. + * + * @param callbackThreshold The angle for the callback threshold, in radians + */ + public void setZoomCallbackThreshold(float callbackThreshold) { + mZoomRing.setCallbackThreshold((int) (callbackThreshold * ZoomRing.RADIAN_INT_MULTIPLIER)); + } + + public void setCallback(OnZoomListener callback) { + mCallback = callback; + } + + public void setThumbAngle(float angle) { + mZoomRing.setThumbAngle((int) (angle * ZoomRing.RADIAN_INT_MULTIPLIER)); + } + + public void setResetThumbAutomatically(boolean resetThumbAutomatically) { + mZoomRing.setResetThumbAutomatically(resetThumbAutomatically); + } + + public boolean isVisible() { + return mIsZoomRingVisible; + } + + public void setVisible(boolean visible) { + + if (useOldZoom(mContext)) return; + + if (visible) { + dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT); + } else { + mPanner.stop(); + } + + if (mIsZoomRingVisible == visible) { + return; + } + + if (visible) { + if (mContainerLayoutParams.token == null) { + mContainerLayoutParams.token = mOwnerView.getWindowToken(); + } + + mWindowManager.addView(mContainer, mContainerLayoutParams); + + if (mPostedVisibleInitializer == null) { + mPostedVisibleInitializer = new Runnable() { + public void run() { + refreshPositioningVariables(); + resetZoomRing(); + + // TODO: remove this 'update' and just center zoom ring before the + // 'add', but need to make sure we have the width and height (which + // probably can only be retrieved after it's measured, which happens + // after it's added). + mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams); + } + }; + } + + mHandler.post(mPostedVisibleInitializer); + + // Handle configuration changes when visible + mContext.registerReceiver(mConfigurationChangedReceiver, mConfigurationChangedFilter); + + // Steal key events from the owner + mOwnerView.setOnKeyListener(this); + + } else { + // Don't want to steal any more keys + mOwnerView.setOnKeyListener(null); + + // No longer care about configuration changes + mContext.unregisterReceiver(mConfigurationChangedReceiver); + + mWindowManager.removeView(mContainer); + } + + mIsZoomRingVisible = visible; + + if (mCallback != null) { + mCallback.onVisibilityChanged(visible); + } + } + + private void dismissZoomRingDelayed(int delay) { + mHandler.removeMessages(MSG_DISMISS_ZOOM_RING); + mHandler.sendEmptyMessageDelayed(MSG_DISMISS_ZOOM_RING, delay); + } + + private void resetZoomRing() { + mScroller.abortAnimation(); + + mContainerLayoutParams.x = mCenteredContainerX; + mContainerLayoutParams.y = mCenteredContainerY; + + // Reset the thumb + mZoomRing.resetThumbAngle(); + } + + /** + * Should be called by the client for each event belonging to the second tap + * (the down, move, up, and cancel events). + * + * @param event The event belonging to the second tap. + * @return Whether the event was consumed. + */ + public boolean handleDoubleTapEvent(MotionEvent event) { + int action = event.getAction(); + + if (action == MotionEvent.ACTION_DOWN) { + mTouchMode = TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT; + int x = (int) event.getX(); + int y = (int) event.getY(); + + refreshPositioningVariables(); + setVisible(true); + centerPoint(x, y); + ensureZoomRingIsCentered(); + + // Tap drag mode stuff + mTapDragStartX = x; + mTapDragStartY = y; + + } else if (action == MotionEvent.ACTION_CANCEL) { + mTouchMode = TOUCH_MODE_IDLE; + + } else { // action is move or up + switch (mTouchMode) { + case TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT: { + switch (action) { + case MotionEvent.ACTION_MOVE: + int x = (int) event.getX(); + int y = (int) event.getY(); + if (Math.abs(x - mTapDragStartX) > mViewConfig.getScaledTouchSlop() || + Math.abs(y - mTapDragStartY) > + mViewConfig.getScaledTouchSlop()) { + mZoomRing.setTapDragMode(true, x, y); + mTouchMode = TOUCH_MODE_FORWARDING_FOR_TAP_DRAG; + } + return true; + + case MotionEvent.ACTION_UP: + mTouchMode = TOUCH_MODE_IDLE; + break; + } + break; + } + + case TOUCH_MODE_FORWARDING_FOR_TAP_DRAG: { + switch (action) { + case MotionEvent.ACTION_MOVE: + giveTouchToZoomRing(event); + return true; + + case MotionEvent.ACTION_UP: + mTouchMode = TOUCH_MODE_IDLE; + mZoomRing.setTapDragMode(false, (int) event.getX(), (int) event.getY()); + break; + } + break; + } + } + } + + return true; + } + + private void ensureZoomRingIsCentered() { + LayoutParams lp = mContainerLayoutParams; + + if (lp.x != mCenteredContainerX || lp.y != mCenteredContainerY) { + int width = mContainer.getWidth(); + int height = mContainer.getHeight(); + mScroller.startScroll(lp.x, lp.y, mCenteredContainerX - lp.x, + mCenteredContainerY - lp.y, ZOOM_RING_RECENTERING_DURATION); + mHandler.sendEmptyMessage(MSG_SCROLLER_TICK); + } + } + + private void refreshPositioningVariables() { + mZoomRingWidth = mZoomRing.getWidth(); + mZoomRingHeight = mZoomRing.getHeight(); + + // Calculate the owner view's bounds + mOwnerView.getGlobalVisibleRect(mOwnerViewBounds); + + // Get the center + Gravity.apply(Gravity.CENTER, mContainer.getWidth(), mContainer.getHeight(), + mOwnerViewBounds, mTempRect); + mCenteredContainerX = mTempRect.left; + mCenteredContainerY = mTempRect.top; + + } + + // MOVE ALL THIS TO GESTURE DETECTOR +// public boolean onTouch(View v, MotionEvent event) { +// int action = event.getAction(); +// +// if (mListenForInvocation) { +// switch (mTouchMode) { +// case TOUCH_MODE_IDLE: { +// if (action == MotionEvent.ACTION_DOWN) { +// setFirstTap(event); +// } +// break; +// } +// +// case TOUCH_MODE_WAITING_FOR_SECOND_TAP: { +// switch (action) { +// case MotionEvent.ACTION_DOWN: +// if (isSecondTapWithinSlop(event)) { +// handleDoubleTapEvent(event); +// } else { +// setFirstTap(event); +// } +// break; +// +// case MotionEvent.ACTION_MOVE: +// int deltaX = (int) event.getX() - mFirstTapX; +// if (deltaX < -SECOND_TAP_MOVE_SLOP || +// deltaX > SECOND_TAP_MOVE_SLOP) { +// mTouchMode = TOUCH_MODE_IDLE; +// } else { +// int deltaY = (int) event.getY() - mFirstTapY; +// if (deltaY < -SECOND_TAP_MOVE_SLOP || +// deltaY > SECOND_TAP_MOVE_SLOP) { +// mTouchMode = TOUCH_MODE_IDLE; +// } +// } +// break; +// } +// break; +// } +// +// case TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT: +// case TOUCH_MODE_FORWARDING_FOR_TAP_DRAG: { +// handleDoubleTapEvent(event); +// break; +// } +// } +// +// if (action == MotionEvent.ACTION_CANCEL) { +// mTouchMode = TOUCH_MODE_IDLE; +// } +// } +// +// return false; +// } +// +// private void setFirstTap(MotionEvent event) { +// mFirstTapTime = event.getEventTime(); +// mFirstTapX = (int) event.getX(); +// mFirstTapY = (int) event.getY(); +// mTouchMode = TOUCH_MODE_WAITING_FOR_SECOND_TAP; +// } +// +// private boolean isSecondTapWithinSlop(MotionEvent event) { +// return mFirstTapTime + SECOND_TAP_TIMEOUT > event.getEventTime() && +// Math.abs((int) event.getX() - mFirstTapX) < SECOND_TAP_SLOP && +// Math.abs((int) event.getY() - mFirstTapY) < SECOND_TAP_SLOP; +// } + + /** + * Centers the point (in owner view's coordinates). + */ + private void centerPoint(int x, int y) { + if (mCallback != null) { + mCallback.onCenter(x, y); + } + } + + private void giveTouchToZoomRing(MotionEvent event) { + int rawX = (int) event.getRawX(); + int rawY = (int) event.getRawY(); + int x = rawX - mContainerLayoutParams.x - mZoomRing.getLeft(); + int y = rawY - mContainerLayoutParams.y - mZoomRing.getTop(); + mZoomRing.handleTouch(event.getAction(), event.getEventTime(), x, y, rawX, rawY); + } + + public void onZoomRingMovingStarted() { + mHandler.removeMessages(MSG_DISMISS_ZOOM_RING); + mScroller.abortAnimation(); + setPanningArrowsVisible(true); + } + + private void setPanningArrowsVisible(boolean visible) { + mPanningArrows.startAnimation(visible ? mPanningArrowsEnterAnimation + : mPanningArrowsExitAnimation); + mPanningArrows.setVisibility(visible ? View.VISIBLE : View.GONE); + } + + public boolean onZoomRingMoved(int deltaX, int deltaY) { + WindowManager.LayoutParams lp = mContainerLayoutParams; + Rect ownerBounds = mOwnerViewBounds; + + int zoomRingLeft = mZoomRing.getLeft(); + int zoomRingTop = mZoomRing.getTop(); + + int newX = lp.x + deltaX; + int newZoomRingX = newX + zoomRingLeft; + newZoomRingX = (newZoomRingX <= ownerBounds.left) ? ownerBounds.left : + (newZoomRingX + mZoomRingWidth > ownerBounds.right) ? + ownerBounds.right - mZoomRingWidth : newZoomRingX; + lp.x = newZoomRingX - zoomRingLeft; + + int newY = lp.y + deltaY; + int newZoomRingY = newY + zoomRingTop; + newZoomRingY = (newZoomRingY <= ownerBounds.top) ? ownerBounds.top : + (newZoomRingY + mZoomRingHeight > ownerBounds.bottom) ? + ownerBounds.bottom - mZoomRingHeight : newZoomRingY; + lp.y = newZoomRingY - zoomRingTop; + + mWindowManager.updateViewLayout(mContainer, lp); + + // Check for pan + int leftGap = newZoomRingX - ownerBounds.left; + if (leftGap < MAX_PAN_GAP) { + mPanner.setHorizontalStrength(-getStrengthFromGap(leftGap)); + } else { + int rightGap = ownerBounds.right - (lp.x + mZoomRingWidth + zoomRingLeft); + if (rightGap < MAX_PAN_GAP) { + mPanner.setHorizontalStrength(getStrengthFromGap(rightGap)); + } else { + mPanner.setHorizontalStrength(0); + } + } + + int topGap = newZoomRingY - ownerBounds.top; + if (topGap < MAX_PAN_GAP) { + mPanner.setVerticalStrength(-getStrengthFromGap(topGap)); + } else { + int bottomGap = ownerBounds.bottom - (lp.y + mZoomRingHeight + zoomRingTop); + if (bottomGap < MAX_PAN_GAP) { + mPanner.setVerticalStrength(getStrengthFromGap(bottomGap)); + } else { + mPanner.setVerticalStrength(0); + } + } + + return true; + } + + public void onZoomRingMovingStopped() { + dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT); + mPanner.stop(); + setPanningArrowsVisible(false); + } + + private int getStrengthFromGap(int gap) { + return gap > MAX_PAN_GAP ? 0 : + (MAX_PAN_GAP - gap) * 100 / MAX_PAN_GAP; + } + + public void onZoomRingThumbDraggingStarted(int startAngle) { + mHandler.removeMessages(MSG_DISMISS_ZOOM_RING); + if (mCallback != null) { + mCallback.onBeginDrag((float) startAngle / ZoomRing.RADIAN_INT_MULTIPLIER); + } + } + + public boolean onZoomRingThumbDragged(int numLevels, int dragAmount, int startAngle, + int curAngle) { + if (mCallback != null) { + int deltaZoomLevel = -numLevels; + int globalZoomCenterX = mContainerLayoutParams.x + mZoomRing.getLeft() + + mZoomRingWidth / 2; + int globalZoomCenterY = mContainerLayoutParams.y + mZoomRing.getTop() + + mZoomRingHeight / 2; + + return mCallback.onDragZoom(deltaZoomLevel, globalZoomCenterX - mOwnerViewBounds.left, + globalZoomCenterY - mOwnerViewBounds.top, + (float) startAngle / ZoomRing.RADIAN_INT_MULTIPLIER, + (float) curAngle / ZoomRing.RADIAN_INT_MULTIPLIER); + } + + return false; + } + + public void onZoomRingThumbDraggingStopped(int endAngle) { + dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT); + if (mCallback != null) { + mCallback.onEndDrag((float) endAngle / ZoomRing.RADIAN_INT_MULTIPLIER); + } + } + + public void onZoomRingDismissed() { + dismissZoomRingDelayed(ZOOM_RING_DISMISS_DELAY); + } + + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { + // If the user touches outside of the zoom ring, dismiss the zoom ring + dismissZoomRingDelayed(ZOOM_RING_DISMISS_DELAY); + return true; + } + + return false; + } + + /** Steals key events from the owner view. */ + public boolean onKey(View v, int keyCode, KeyEvent event) { + switch (keyCode) { + case KeyEvent.KEYCODE_DPAD_LEFT: + case KeyEvent.KEYCODE_DPAD_RIGHT: + // Eat these + return true; + + case KeyEvent.KEYCODE_DPAD_UP: + case KeyEvent.KEYCODE_DPAD_DOWN: + // Keep the zoom alive a little longer + dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT); + + if (mCallback != null && event.getAction() == KeyEvent.ACTION_DOWN) { + mCallback.onSimpleZoom(keyCode == KeyEvent.KEYCODE_DPAD_UP); + } + + return true; + } + + return false; + } + + private void onScrollerTick() { + if (!mScroller.computeScrollOffset() || !mIsZoomRingVisible) return; + + mContainerLayoutParams.x = mScroller.getCurrX(); + mContainerLayoutParams.y = mScroller.getCurrY(); + mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams); + + mHandler.sendEmptyMessage(MSG_SCROLLER_TICK); + } + + private void onPostConfigurationChanged() { + dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT); + refreshPositioningVariables(); + ensureZoomRingIsCentered(); + } + + private class Panner implements Runnable { + private static final int RUN_DELAY = 15; + private static final float STOP_SLOWDOWN = 0.8f; + + private final Handler mUiHandler = new Handler(); + + private int mVerticalStrength; + private int mHorizontalStrength; + + private boolean mStopping; + + /** The time this current pan started. */ + private long mStartTime; + + /** The time of the last callback to pan the map/browser/etc. */ + private long mPreviousCallbackTime; + + /** -100 (full left) to 0 (none) to 100 (full right) */ + public void setHorizontalStrength(int horizontalStrength) { + if (mHorizontalStrength == 0 && mVerticalStrength == 0 && horizontalStrength != 0) { + start(); + } else if (mVerticalStrength == 0 && horizontalStrength == 0) { + stop(); + } + + mHorizontalStrength = horizontalStrength; + mStopping = false; + } + + /** -100 (full up) to 0 (none) to 100 (full down) */ + public void setVerticalStrength(int verticalStrength) { + if (mHorizontalStrength == 0 && mVerticalStrength == 0 && verticalStrength != 0) { + start(); + } else if (mHorizontalStrength == 0 && verticalStrength == 0) { + stop(); + } + + mVerticalStrength = verticalStrength; + mStopping = false; + } + + private void start() { + mUiHandler.post(this); + mPreviousCallbackTime = 0; + mStartTime = 0; + } + + public void stop() { + mStopping = true; + } + + public void run() { + if (mStopping) { + mHorizontalStrength *= STOP_SLOWDOWN; + mVerticalStrength *= STOP_SLOWDOWN; + } + + if (mHorizontalStrength == 0 && mVerticalStrength == 0) { + return; + } + + boolean firstRun = mPreviousCallbackTime == 0; + long curTime = SystemClock.elapsedRealtime(); + int panAmount = getPanAmount(mStartTime, mPreviousCallbackTime, curTime); + mPreviousCallbackTime = curTime; + + if (firstRun) { + mStartTime = curTime; + } else { + int panX = panAmount * mHorizontalStrength / 100; + int panY = panAmount * mVerticalStrength / 100; + + if (mCallback != null) { + mCallback.onPan(panX, panY); + } + } + + mUiHandler.postDelayed(this, RUN_DELAY); + } + + // TODO make setter for this value so zoom clients can have different pan rates, if they want + private static final int PAN_VELOCITY_PX_S = 30; + private int getPanAmount(long startTime, long previousTime, long currentTime) { + return (int) ((currentTime - previousTime) * PAN_VELOCITY_PX_S / 100); + } + } + + public interface OnZoomListener { + void onBeginDrag(float startAngle); + boolean onDragZoom(int deltaZoomLevel, int centerX, int centerY, float startAngle, + float curAngle); + void onEndDrag(float endAngle); + void onSimpleZoom(boolean deltaZoomLevel); + boolean onPan(int deltaX, int deltaY); + void onCenter(int x, int y); + void onVisibilityChanged(boolean visible); + } +} diff --git a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java new file mode 100644 index 0000000..000f6c4 --- /dev/null +++ b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2007 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.app.AlertDialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.Handler; +import android.os.IMountService; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.Environment; +import android.widget.Toast; +import android.util.Log; + +/** + * This activity is shown to the user to confirm formatting of external media. + * It uses the alert dialog style. It will be launched from a notification, or from settings + */ +public class ExternalMediaFormatActivity extends AlertActivity implements DialogInterface.OnClickListener { + + private static final int POSITIVE_BUTTON = AlertDialog.BUTTON1; + + /** Used to detect when the media state changes, in case we need to call finish() */ + private BroadcastReceiver mStorageReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + Log.d("ExternalMediaFormatActivity", "got action " + action); + + if (action == Intent.ACTION_MEDIA_REMOVED || + action == Intent.ACTION_MEDIA_CHECKING || + action == Intent.ACTION_MEDIA_MOUNTED || + action == Intent.ACTION_MEDIA_SHARED) { + finish(); + } + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Log.d("ExternalMediaFormatActivity", "onCreate!"); + // Set up the "dialog" + final AlertController.AlertParams p = mAlertParams; + p.mIconId = com.android.internal.R.drawable.stat_sys_warning; + p.mTitle = getString(com.android.internal.R.string.extmedia_format_title); + p.mMessage = getString(com.android.internal.R.string.extmedia_format_message); + p.mPositiveButtonText = getString(com.android.internal.R.string.extmedia_format_button_format); + p.mPositiveButtonListener = this; + p.mNegativeButtonText = getString(com.android.internal.R.string.cancel); + p.mNegativeButtonListener = this; + setupAlert(); + } + + @Override + protected void onResume() { + super.onResume(); + + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_MEDIA_REMOVED); + filter.addAction(Intent.ACTION_MEDIA_CHECKING); + filter.addAction(Intent.ACTION_MEDIA_MOUNTED); + filter.addAction(Intent.ACTION_MEDIA_SHARED); + registerReceiver(mStorageReceiver, filter); + } + + @Override + protected void onPause() { + super.onPause(); + + unregisterReceiver(mStorageReceiver); + } + + /** + * {@inheritDoc} + */ + public void onClick(DialogInterface dialog, int which) { + + if (which == POSITIVE_BUTTON) { + IMountService mountService = IMountService.Stub.asInterface(ServiceManager + .getService("mount")); + if (mountService != null) { + try { + mountService.formatMedia(Environment.getExternalStorageDirectory().toString()); + } catch (RemoteException e) { + } + } + } + + // No matter what, finish the activity + finish(); + } +} diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 434850c..eb232c7 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -22,8 +22,14 @@ interface IBatteryStats { BatteryStatsImpl getStatistics(); void noteStartWakelock(int uid, String name, int type); void noteStopWakelock(int uid, String name, int type); - void noteStartSensor(int uid, int sensor); - void noteStopSensor(int uid, int sensor); + void noteStartSensor(int uid, String name, int sensor); + void noteStopSensor(int uid, String name, int sensor); + void noteRequestGpsOn(int uid); + void noteRequestGpsOff(int uid); + void noteStartGps(int uid); + void noteStopGps(int uid); + void noteScreenOn(); + void noteScreenOff(); void setOnBattery(boolean onBattery); long getAwakeTimeBattery(); long getAwakeTimePlugged(); diff --git a/core/java/com/android/internal/app/IUsageStats.aidl b/core/java/com/android/internal/app/IUsageStats.aidl new file mode 100755 index 0000000..6b053d5 --- /dev/null +++ b/core/java/com/android/internal/app/IUsageStats.aidl @@ -0,0 +1,27 @@ +/* + * 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.internal.app; + +import android.content.ComponentName; +import com.android.internal.os.PkgUsageStats; + +interface IUsageStats { + void noteResumeComponent(in ComponentName componentName); + void notePauseComponent(in ComponentName componentName); + PkgUsageStats getPkgUsageStats(in ComponentName componentName); + PkgUsageStats[] getAllPkgUsageStats(); +} diff --git a/core/java/com/android/internal/app/UsbStorageStopActivity.java b/core/java/com/android/internal/app/UsbStorageStopActivity.java new file mode 100644 index 0000000..30523c4 --- /dev/null +++ b/core/java/com/android/internal/app/UsbStorageStopActivity.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2007 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.app.AlertDialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.Handler; +import android.os.IMountService; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.widget.Toast; + +/** + * This activity is shown to the user for him/her to disable USB mass storage. + * It uses the alert dialog style. It will be launched from a notification. + */ +public class UsbStorageStopActivity extends AlertActivity implements DialogInterface.OnClickListener { + + private static final int POSITIVE_BUTTON = AlertDialog.BUTTON1; + + /** Used to detect when the USB cable is unplugged, so we can call finish() */ + private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction() == Intent.ACTION_BATTERY_CHANGED) { + handleBatteryChanged(intent); + } + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Set up the "dialog" + final AlertController.AlertParams p = mAlertParams; + p.mIconId = com.android.internal.R.drawable.stat_sys_warning; + p.mTitle = getString(com.android.internal.R.string.usb_storage_stop_title); + p.mMessage = getString(com.android.internal.R.string.usb_storage_stop_message); + p.mPositiveButtonText = getString(com.android.internal.R.string.usb_storage_stop_button_mount); + p.mPositiveButtonListener = this; + p.mNegativeButtonText = getString(com.android.internal.R.string.usb_storage_stop_button_unmount); + p.mNegativeButtonListener = this; + setupAlert(); + } + + @Override + protected void onResume() { + super.onResume(); + + registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + } + + @Override + protected void onPause() { + super.onPause(); + + unregisterReceiver(mBatteryReceiver); + } + + /** + * {@inheritDoc} + */ + public void onClick(DialogInterface dialog, int which) { + + if (which == POSITIVE_BUTTON) { + stopUsbStorage(); + } + + // No matter what, finish the activity + finish(); + } + + private void stopUsbStorage() { + IMountService mountService = IMountService.Stub.asInterface(ServiceManager + .getService("mount")); + if (mountService == null) { + showStoppingError(); + return; + } + + try { + mountService.setMassStorageEnabled(false); + } catch (RemoteException e) { + showStoppingError(); + return; + } + } + + private void handleBatteryChanged(Intent intent) { + int pluggedType = intent.getIntExtra("plugged", 0); + if (pluggedType == 0) { + // It was disconnected from the plug, so finish + finish(); + } + } + + private void showStoppingError() { + Toast.makeText(this, com.android.internal.R.string.usb_storage_stop_error_message, + Toast.LENGTH_LONG).show(); + } + +} diff --git a/core/java/com/android/internal/gadget/IGadgetHost.aidl b/core/java/com/android/internal/gadget/IGadgetHost.aidl new file mode 100644 index 0000000..a5b8654 --- /dev/null +++ b/core/java/com/android/internal/gadget/IGadgetHost.aidl @@ -0,0 +1,27 @@ +/* + * 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.internal.gadget; + +import android.content.ComponentName; +import android.gadget.GadgetInfo; +import android.widget.RemoteViews; + +/** {@hide} */ +oneway interface IGadgetHost { + void updateGadget(int gadgetId, in RemoteViews views); +} + diff --git a/core/java/com/android/internal/gadget/IGadgetService.aidl b/core/java/com/android/internal/gadget/IGadgetService.aidl index 6f9af04..1b3946f 100644 --- a/core/java/com/android/internal/gadget/IGadgetService.aidl +++ b/core/java/com/android/internal/gadget/IGadgetService.aidl @@ -18,12 +18,32 @@ package com.android.internal.gadget; import android.content.ComponentName; import android.gadget.GadgetInfo; +import com.android.internal.gadget.IGadgetHost; +import android.widget.RemoteViews; /** {@hide} */ interface IGadgetService { - int allocateGadgetId(String hostPackage); + + // + // for GadgetHost + // + int[] startListening(IGadgetHost host, String packageName, int hostId, + out List<RemoteViews> updatedViews); + void stopListening(int hostId); + int allocateGadgetId(String packageName, int hostId); void deleteGadgetId(int gadgetId); - void bindGadgetId(int gadgetId, in ComponentName provider); - GadgetInfo getGadgetInfo(int gadgetId); + void deleteHost(int hostId); + void deleteAllHosts(); + RemoteViews getGadgetViews(int gadgetId); + + // + // for GadgetManager + // + void updateGadgetIds(in int[] gadgetIds, in RemoteViews views); + void updateGadgetProvider(in ComponentName provider, in RemoteViews views); List<GadgetInfo> getInstalledProviders(); + GadgetInfo getGadgetInfo(int gadgetId); + void bindGadgetId(int gadgetId, in ComponentName provider); + } + diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 8912960..cbb65dc 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -17,6 +17,7 @@ package com.android.internal.os; import android.os.BatteryStats; +import android.os.NetStat; import android.os.Parcel; import android.os.ParcelFormatException; import android.os.Parcelable; @@ -28,9 +29,9 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; /** @@ -39,12 +40,14 @@ import java.util.Map; * otherwise. */ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { + + private static final String TAG = "BatteryStatsImpl"; // In-memory Parcel magic number, used to detect attempts to unmarshall bad data private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 13; + private static final int VERSION = 15; private final File mFile; private final File mBackupFile; @@ -77,7 +80,11 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { long mRealtime; long mRealtimeStart; long mLastRealtime; - + + boolean mScreenOn; + long mLastScreenOnTimeMillis; + long mBatteryScreenOnTimeMillis; + long mPluggedScreenOnTimeMillis; /** * These provide time bases that discount the time the device is plugged * in to power. @@ -87,6 +94,11 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { long mTrackBatteryUptimeStart; long mTrackBatteryPastRealtime; long mTrackBatteryRealtimeStart; + + long mUnpluggedBatteryUptime; + long mUnpluggedBatteryRealtime; + + HashSet<Integer> mGpsRequesters = new HashSet<Integer>(); long mLastWriteTime = 0; // Milliseconds @@ -255,14 +267,13 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { long realtime = SystemClock.elapsedRealtime() * 1000; long heldTime = stats.getBatteryUptimeLocked(realtime) - mUpdateTime; if (heldTime > 0) { - mTotalTime += (heldTime * 1000) / mCount; + mTotalTime += heldTime / mCount; } mUpdateTime = stats.getBatteryUptimeLocked(realtime); } private long computeRunTimeLocked(long curBatteryUptime) { - return mTotalTime + - (mNesting > 0 ? ((curBatteryUptime * 1000) - mUpdateTime) / mCount : 0); + return mTotalTime + (mNesting > 0 ? (curBatteryUptime - mUpdateTime) / mCount : 0); } void writeSummaryFromParcelLocked(Parcel out, long curBatteryUptime) { @@ -284,6 +295,15 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } } + public void unplugTcpCounters() { + final int NU = mUidStats.size(); + for (int iu = 0; iu < NU; iu++) { + Uid u = mUidStats.valueAt(iu); + u.mTcpBytesReceivedAtLastUnplug = u.getTcpBytesReceived(STATS_TOTAL); + u.mTcpBytesSentAtLastUnplug = u.getTcpBytesSent(STATS_TOTAL); + } + } + public void unplugTimers() { ArrayList<Timer> timers; @@ -305,6 +325,69 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } } + public void noteStartGps(int uid) { + mGpsRequesters.add(uid); + mUidStats.get(uid).noteStartGps(); + } + + public void noteStopGps(int uid) { + mGpsRequesters.remove(uid); + mUidStats.get(uid).noteStopGps(); + } + + public void noteRequestGpsOn(int uid) { + mGpsRequesters.add(uid); + mUidStats.get(uid).noteStartGps(); + } + + public void noteRequestGpsOff(int uid) { + mGpsRequesters.remove(uid); + mUidStats.get(uid).noteStopGps(); + } + + /** + * When the device screen or battery state changes, update the appropriate "screen on time" + * counter. + */ + private void updateScreenOnTime(boolean screenOn) { + if (!mScreenOn) { + Log.w(TAG, "updateScreenOnTime without mScreenOn, ignored"); + return; + } + long now = SystemClock.elapsedRealtime(); + long elapsed = now - mLastScreenOnTimeMillis; + if (mOnBattery) { + mBatteryScreenOnTimeMillis += elapsed; + } else { + mPluggedScreenOnTimeMillis += elapsed; + } + if (screenOn) { + mLastScreenOnTimeMillis = now; + } + } + + public void noteScreenOn() { + mScreenOn = true; + mLastScreenOnTimeMillis = SystemClock.elapsedRealtime(); + } + + public void noteScreenOff() { + if (!mScreenOn) { + Log.w(TAG, "noteScreenOff without mScreenOn, ignored"); + return; + } + updateScreenOnTime(false); + mScreenOn = false; + } + + @Override public long getBatteryScreenOnTime() { + return mBatteryScreenOnTimeMillis; + } + + @Override public long getPluggedScreenOnTime() { + return mPluggedScreenOnTimeMillis; + } + @Override public SparseArray<? extends BatteryStats.Uid> getUidStats() { return mUidStats; @@ -314,6 +397,14 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { * The statistics associated with a particular uid. */ public final class Uid extends BatteryStats.Uid { + + final int mUid; + long mLoadedTcpBytesReceived; + long mLoadedTcpBytesSent; + long mTcpBytesReceivedAtLastUnplug; + long mTcpBytesSentAtLastUnplug; + + private final byte[] mBuf = new byte[16]; /** * The statistics we have collected for this uid's wake locks. @@ -334,6 +425,10 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { * The statistics we have collected for this uid's processes. */ final HashMap<String, Pkg> mPackageStats = new HashMap<String, Pkg>(); + + public Uid(int uid) { + mUid = uid; + } @Override public Map<String, ? extends BatteryStats.Uid.Wakelock> getWakelockStats() { @@ -354,6 +449,42 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { public Map<String, ? extends BatteryStats.Uid.Pkg> getPackageStats() { return mPackageStats; } + + public int getUid() { + return mUid; + } + + public long getTcpBytesReceived(int which) { + long current = NetStat.getUidRxBytes(mUid); + + if (which == STATS_CURRENT) { + return current; + } else if (which == STATS_LAST) { + return mLoadedTcpBytesReceived; + } else if (which == STATS_UNPLUGGED) { + return current - mTcpBytesReceivedAtLastUnplug; + } else if (which == STATS_TOTAL) { + return mLoadedTcpBytesReceived + current; + } else { + throw new IllegalArgumentException("which = " + which); + } + } + + public long getTcpBytesSent(int which) { + long current = NetStat.getUidTxBytes(mUid); + + if (which == STATS_CURRENT) { + return current; + } else if (which == STATS_LAST) { + return mLoadedTcpBytesSent; + } else if (which == STATS_UNPLUGGED) { + return current - mTcpBytesSentAtLastUnplug; + } else if (which == STATS_TOTAL) { + return mLoadedTcpBytesSent + current; + } else { + throw new IllegalArgumentException("which = " + which); + } + } void writeToParcelLocked(Parcel out) { out.writeInt(mWakelockStats.size()); @@ -366,6 +497,7 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { out.writeInt(mSensorStats.size()); for (Map.Entry<Integer, Uid.Sensor> sensorEntry : mSensorStats.entrySet()) { out.writeInt(sensorEntry.getKey()); + out.writeString(sensorEntry.getValue().getName()); Uid.Sensor sensor = sensorEntry.getValue(); sensor.writeToParcelLocked(out); } @@ -383,6 +515,11 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { Uid.Pkg pkg = pkgEntry.getValue(); pkg.writeToParcelLocked(out); } + + out.writeLong(mLoadedTcpBytesReceived); + out.writeLong(mLoadedTcpBytesSent); + out.writeLong(mTcpBytesReceivedAtLastUnplug); + out.writeLong(mTcpBytesSentAtLastUnplug); } void readFromParcelLocked(Parcel in) { @@ -399,7 +536,8 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { mSensorStats.clear(); for (int k = 0; k < numSensors; k++) { int sensorNumber = in.readInt(); - Uid.Sensor sensor = new Sensor(); + String name = in.readString(); + Uid.Sensor sensor = new Sensor(name); sensor.readFromParcelLocked(in); mSensorStats.put(sensorNumber, sensor); } @@ -421,6 +559,11 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { pkg.readFromParcelLocked(in); mPackageStats.put(packageName, pkg); } + + mLoadedTcpBytesReceived = in.readLong(); + mLoadedTcpBytesSent = in.readLong(); + mTcpBytesReceivedAtLastUnplug = in.readLong(); + mTcpBytesSentAtLastUnplug = in.readLong(); } /** @@ -499,7 +642,13 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } public final class Sensor extends BatteryStats.Uid.Sensor { + static final int GPS = -10000; // Treat GPS as a sensor + final String mName; Timer sensorTime; + + public Sensor(String name) { + mName = name; + } private Timer readTimerFromParcel(Parcel in) { if (in.readInt() == 0) { @@ -530,6 +679,10 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { public Timer getSensorTime() { return sensorTime; } + + public String getName() { + return mName; + } } /** @@ -1039,19 +1192,19 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } } - public Timer getSensorTimerLocked(int sensor, boolean create) { + public Timer getSensorTimerLocked(String name, int sensor, boolean create) { Integer sId = Integer.valueOf(sensor); Sensor se = mSensorStats.get(sId); if (se == null) { if (!create) { return null; } - se = new Sensor(); + se = new Sensor(name); mSensorStats.put(sId, se); } Timer t = se.sensorTime; if (t == null) { - t = new Timer(0, mSensorTimers); + t = new Timer(BatteryStats.SENSOR, mSensorTimers); se.sensorTime = t; } return t; @@ -1071,20 +1224,34 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } } - public void noteStartSensor(int sensor) { - Timer t = getSensorTimerLocked(sensor, true); + public void noteStartSensor(String name, int sensor) { + Timer t = getSensorTimerLocked(name, sensor, true); if (t != null) { t.startRunningLocked(BatteryStatsImpl.this); } } - public void noteStopSensor(int sensor) { + public void noteStopSensor(String name, int sensor) { // Don't create a timer if one doesn't already exist - Timer t = getSensorTimerLocked(sensor, false); + Timer t = getSensorTimerLocked(name, sensor, false); if (t != null) { t.stopRunningLocked(BatteryStatsImpl.this); } } + + public void noteStartGps() { + Timer t = getSensorTimerLocked("GPS", Sensor.GPS, true); + if (t != null) { + t.startRunningLocked(BatteryStatsImpl.this); + } + } + + public void noteStopGps() { + Timer t = getSensorTimerLocked("GPS", Sensor.GPS, false); + if (t != null) { + t.stopRunningLocked(BatteryStatsImpl.this); + } + } public BatteryStatsImpl getBatteryStats() { return BatteryStatsImpl.this; @@ -1100,6 +1267,9 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { mTrackBatteryPastRealtime = 0; mUptimeStart = mTrackBatteryUptimeStart = SystemClock.uptimeMillis() * 1000; mRealtimeStart = mTrackBatteryRealtimeStart = SystemClock.elapsedRealtime() * 1000; + + mUnpluggedBatteryUptime = getBatteryUptimeLocked(mUptimeStart); + mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(mRealtimeStart); } public BatteryStatsImpl(Parcel p) { @@ -1119,13 +1289,21 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { public void setOnBattery(boolean onBattery) { synchronized(this) { if (mOnBattery != onBattery) { + if (mScreenOn) { + updateScreenOnTime(true); + } + long uptime = SystemClock.uptimeMillis() * 1000; long mSecRealtime = SystemClock.elapsedRealtime(); long realtime = mSecRealtime * 1000; if (onBattery) { - mTrackBatteryUptimeStart = uptime; - mTrackBatteryRealtimeStart = realtime; + mTrackBatteryUptimeStart = getBatteryUptime(uptime); + mTrackBatteryRealtimeStart = getBatteryRealtime(realtime); + unplugTcpCounters(); unplugTimers(); + + mUnpluggedBatteryUptime = getBatteryUptime(uptime); + mUnpluggedBatteryRealtime = getBatteryRealtime(realtime); } else { mTrackBatteryPastUptime += uptime - mTrackBatteryUptimeStart; mTrackBatteryPastRealtime += realtime - mTrackBatteryRealtimeStart; @@ -1172,14 +1350,16 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { @Override public long computeBatteryUptime(long curTime, int which) { + long uptime = getBatteryUptime(curTime); switch (which) { case STATS_TOTAL: - return mBatteryUptime + getBatteryUptime(curTime); + return mBatteryUptime + uptime; case STATS_LAST: return mBatteryLastUptime; case STATS_CURRENT: + return uptime; case STATS_UNPLUGGED: - return getBatteryUptime(curTime); + return uptime - mUnpluggedBatteryUptime; } return 0; } @@ -1192,8 +1372,9 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { case STATS_LAST: return mBatteryLastRealtime; case STATS_CURRENT: - case STATS_UNPLUGGED: return getBatteryRealtimeLocked(curTime); + case STATS_UNPLUGGED: + return getBatteryRealtimeLocked(curTime) - mUnpluggedBatteryRealtime; } return 0; } @@ -1234,7 +1415,7 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { public Uid getUidStatsLocked(int uid) { Uid u = mUidStats.get(uid); if (u == null) { - u = new Uid(); + u = new Uid(uid); mUidStats.put(uid, u); } return u; @@ -1246,7 +1427,7 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { public void removeUidStatsLocked(int uid) { mUidStats.remove(uid); } - + /** * Retrieve the statistics object for a particular process, creating * if needed. @@ -1388,15 +1569,21 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { mRealtime = in.readLong(); mLastRealtime = in.readLong(); mStartCount++; + + if (version >= 14) { + mBatteryScreenOnTimeMillis = in.readLong(); + mPluggedScreenOnTimeMillis = in.readLong(); + mScreenOn = false; + } final int NU = in.readInt(); - for (int iu=0; iu<NU; iu++) { + for (int iu = 0; iu < NU; iu++) { int uid = in.readInt(); - Uid u = new Uid(); + Uid u = new Uid(uid); mUidStats.put(uid, u); int NW = in.readInt(); - for (int iw=0; iw<NW; iw++) { + for (int iw = 0; iw < NW; iw++) { String wlName = in.readString(); if (in.readInt() != 0) { u.getWakeTimerLocked(wlName, WAKE_TYPE_FULL).readSummaryFromParcelLocked(in); @@ -1411,16 +1598,21 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { if (version >= 12) { int NSE = in.readInt(); - for (int is=0; is<NSE; is++) { + for (int is = 0; is < NSE; is++) { int seNumber = in.readInt(); + String seName = "<unknown>"; + if (version >= 14) { + seName = in.readString(); + } if (in.readInt() != 0) { - u.getSensorTimerLocked(seNumber, true).readSummaryFromParcelLocked(in); + u.getSensorTimerLocked(seName, seNumber, true) + .readSummaryFromParcelLocked(in); } } } int NP = in.readInt(); - for (int ip=0; ip<NP; ip++) { + for (int ip = 0; ip < NP; ip++) { String procName = in.readString(); Uid.Proc p = u.getProcessStatsLocked(procName); p.mUserTime = p.mLoadedUserTime = in.readLong(); @@ -1432,13 +1624,13 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } NP = in.readInt(); - for (int ip=0; ip<NP; ip++) { + for (int ip = 0; ip < NP; ip++) { String pkgName = in.readString(); Uid.Pkg p = u.getPackageStatsLocked(pkgName); p.mWakeups = p.mLoadedWakeups = in.readInt(); p.mLastWakeups = in.readInt(); final int NS = in.readInt(); - for (int is=0; is<NS; is++) { + for (int is = 0; is < NS; is++) { String servName = in.readString(); Uid.Pkg.Serv s = u.getServiceStatsLocked(pkgName, servName); s.mStartTime = s.mLoadedStartTime = in.readLong(); @@ -1449,6 +1641,11 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { s.mLastLaunches = in.readInt(); } } + + if (version >= 14) { + u.mLoadedTcpBytesReceived = in.readLong(); + u.mLoadedTcpBytesSent = in.readLong(); + } } } @@ -1474,10 +1671,13 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { out.writeLong(computeUptime(NOW_SYS, STATS_CURRENT)); out.writeLong(computeRealtime(NOWREAL_SYS, STATS_TOTAL)); out.writeLong(computeRealtime(NOWREAL_SYS, STATS_CURRENT)); + + out.writeLong(mBatteryScreenOnTimeMillis); + out.writeLong(mPluggedScreenOnTimeMillis); final int NU = mUidStats.size(); out.writeInt(NU); - for (int iu=0; iu<NU; iu++) { + for (int iu = 0; iu < NU; iu++) { out.writeInt(mUidStats.keyAt(iu)); Uid u = mUidStats.valueAt(iu); @@ -1516,6 +1716,7 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { : u.mSensorStats.entrySet()) { out.writeInt(ent.getKey()); Uid.Sensor se = ent.getValue(); + out.writeString(se.getName()); if (se.sensorTime != null) { out.writeInt(1); se.sensorTime.writeSummaryFromParcelLocked(out, NOW); @@ -1568,6 +1769,9 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } } } + + out.writeLong(u.getTcpBytesReceived(STATS_TOTAL)); + out.writeLong(u.getTcpBytesSent(STATS_TOTAL)); } } @@ -1586,6 +1790,9 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { mBatteryLastUptime = in.readLong(); mBatteryRealtime = in.readLong(); mBatteryLastRealtime = in.readLong(); + mBatteryScreenOnTimeMillis = in.readLong(); + mPluggedScreenOnTimeMillis = in.readLong(); + mScreenOn = false; mUptime = in.readLong(); mUptimeStart = in.readLong(); mLastUptime = in.readLong(); @@ -1606,10 +1813,10 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { int numUids = in.readInt(); mUidStats.clear(); for (int i = 0; i < numUids; i++) { - int key = in.readInt(); - Uid uid = new Uid(); - uid.readFromParcelLocked(in); - mUidStats.append(key, uid); + int uid = in.readInt(); + Uid u = new Uid(uid); + u.readFromParcelLocked(in); + mUidStats.append(uid, u); } } @@ -1625,6 +1832,8 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { out.writeLong(mBatteryLastUptime); out.writeLong(mBatteryRealtime); out.writeLong(mBatteryLastRealtime); + out.writeLong(mBatteryScreenOnTimeMillis); + out.writeLong(mPluggedScreenOnTimeMillis); out.writeLong(mUptime); out.writeLong(mUptimeStart); out.writeLong(mLastUptime); diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java index 1ec74a1..bab1e21 100644 --- a/core/java/com/android/internal/os/HandlerCaller.java +++ b/core/java/com/android/internal/os/HandlerCaller.java @@ -21,7 +21,8 @@ public class HandlerCaller { public Object arg1; public Object arg2; - Object arg3; + public Object arg3; + public Object arg4; public int argi1; public int argi2; public int argi3; @@ -149,6 +150,16 @@ public class HandlerCaller { return mH.obtainMessage(what, 0, 0, args); } + public Message obtainMessageOOOO(int what, Object arg1, Object arg2, + Object arg3, Object arg4) { + SomeArgs args = obtainArgs(); + args.arg1 = arg1; + args.arg2 = arg2; + args.arg3 = arg3; + args.arg4 = arg4; + return mH.obtainMessage(what, 0, 0, args); + } + public Message obtainMessageIIII(int what, int arg1, int arg2, int arg3, int arg4) { SomeArgs args = obtainArgs(); diff --git a/core/res/res/drawable/checkbox_background.xml b/core/java/com/android/internal/os/PkgUsageStats.aidl index 68bb178..8305271 100644..100755 --- a/core/res/res/drawable/checkbox_background.xml +++ b/core/java/com/android/internal/os/PkgUsageStats.aidl @@ -1,6 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/res/drawable/checkbox_background.xml +/* //device/java/android/android/content/Intent.aidl ** ** Copyright 2007, The Android Open Source Project ** @@ -16,8 +14,7 @@ ** 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:drawable="@drawable/checkbox_label_background" /> -</selector> +package com.android.internal.os; + +parcelable PkgUsageStats; diff --git a/core/java/com/android/internal/os/PkgUsageStats.java b/core/java/com/android/internal/os/PkgUsageStats.java new file mode 100755 index 0000000..e847878 --- /dev/null +++ b/core/java/com/android/internal/os/PkgUsageStats.java @@ -0,0 +1,60 @@ +package com.android.internal.os; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * implementation of PkgUsageStats associated with an + * application package. + * @hide + */ +public class PkgUsageStats implements Parcelable { + public String packageName; + public int launchCount; + public long usageTime; + + public static final Parcelable.Creator<PkgUsageStats> CREATOR + = new Parcelable.Creator<PkgUsageStats>() { + public PkgUsageStats createFromParcel(Parcel in) { + return new PkgUsageStats(in); + } + + public PkgUsageStats[] newArray(int size) { + return new PkgUsageStats[size]; + } + }; + + public String toString() { + return "PkgUsageStats{" + + Integer.toHexString(System.identityHashCode(this)) + + " " + packageName + "}"; + } + + public PkgUsageStats(String pkgName, int count, long time) { + packageName = pkgName; + launchCount = count; + usageTime = time; + } + + public PkgUsageStats(Parcel source) { + packageName = source.readString(); + launchCount = source.readInt(); + usageTime = source.readLong(); + } + + public PkgUsageStats(PkgUsageStats pStats) { + packageName = pStats.packageName; + launchCount = pStats.launchCount; + usageTime = pStats.usageTime; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int parcelableFlags){ + dest.writeString(packageName); + dest.writeInt(launchCount); + dest.writeLong(usageTime); + } +} diff --git a/core/java/com/android/internal/os/RecoverySystem.java b/core/java/com/android/internal/os/RecoverySystem.java new file mode 100644 index 0000000..c938610 --- /dev/null +++ b/core/java/com/android/internal/os/RecoverySystem.java @@ -0,0 +1,128 @@ +/* + * 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.internal.os; + +import android.os.FileUtils; +import android.os.Power; +import android.util.Log; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; + +/** + * Utility class for interacting with the Android recovery partition. + * The recovery partition is a small standalone system which can perform + * operations that are difficult while the main system is running, like + * upgrading system software or reformatting the data partition. + * Note that most of these operations must be run as root. + * + * @hide + */ +public class RecoverySystem { + private static final String TAG = "RecoverySystem"; // for logging + + // Used to communicate with recovery. See commands/recovery/recovery.c. + private static File RECOVERY_DIR = new File("/cache/recovery"); + private static File COMMAND_FILE = new File(RECOVERY_DIR, "command"); + private static File LOG_FILE = new File(RECOVERY_DIR, "log"); + + // Length limits for reading files. + private static int LOG_FILE_MAX_LENGTH = 8 * 1024; + + /** + * Reboot into the recovery system to install a system update. + * @param update package to install (must be in /cache or /data). + * @throws IOException if something goes wrong. + */ + public static void rebootAndUpdate(File update) throws IOException { + String path = update.getCanonicalPath(); + if (path.startsWith("/cache/")) { + path = "CACHE:" + path.substring(7); + } else if (path.startsWith("/data/")) { + path = "DATA:" + path.substring(6); + } else { + throw new IllegalArgumentException( + "Must start with /cache or /data: " + path); + } + bootCommand("--update_package=" + path); + } + + /** + * Reboot into the recovery system to wipe the /data partition. + * @param extras to add to the RECOVERY_COMPLETED intent after rebooting. + * @throws IOException if something goes wrong. + */ + public static void rebootAndWipe() throws IOException { + bootCommand("--wipe_data"); + } + + /** + * Reboot into the recovery system with the supplied argument. + * @param arg to pass to the recovery utility. + * @throws IOException if something goes wrong. + */ + private static void bootCommand(String arg) throws IOException { + RECOVERY_DIR.mkdirs(); // In case we need it + COMMAND_FILE.delete(); // In case it's not writable + LOG_FILE.delete(); + + FileWriter command = new FileWriter(COMMAND_FILE); + try { + command.write(arg); + command.write("\n"); + } finally { + command.close(); + } + + // Having written the command file, go ahead and reboot + Power.reboot("recovery"); + throw new IOException("Reboot failed (no permissions?)"); + } + + /** + * Called after booting to process and remove recovery-related files. + * @return the log file from recovery, or null if none was found. + */ + public static String handleAftermath() { + // Record the tail of the LOG_FILE + String log = null; + try { + log = FileUtils.readTextFile(LOG_FILE, -LOG_FILE_MAX_LENGTH, "...\n"); + } catch (FileNotFoundException e) { + Log.i(TAG, "No recovery log file"); + } catch (IOException e) { + Log.e(TAG, "Error reading recovery log", e); + } + + // Delete everything in RECOVERY_DIR + String[] names = RECOVERY_DIR.list(); + for (int i = 0; names != null && i < names.length; i++) { + File f = new File(RECOVERY_DIR, names[i]); + if (!f.delete()) { + Log.e(TAG, "Can't delete: " + f); + } else { + Log.i(TAG, "Deleted: " + f); + } + } + + return log; + } +} diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 011e944..b0b00b2 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -13,6 +13,8 @@ import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; +import java.lang.ref.WeakReference; + public class IInputConnectionWrapper extends IInputContext.Stub { static final String TAG = "IInputConnectionWrapper"; @@ -22,6 +24,8 @@ public class IInputConnectionWrapper extends IInputContext.Stub { private static final int DO_GET_EXTRACTED_TEXT = 40; private static final int DO_COMMIT_TEXT = 50; private static final int DO_COMMIT_COMPLETION = 55; + private static final int DO_SET_SELECTION = 57; + private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 58; private static final int DO_SET_COMPOSING_TEXT = 60; private static final int DO_FINISH_COMPOSING_TEXT = 65; private static final int DO_SEND_KEY_EVENT = 70; @@ -33,7 +37,7 @@ public class IInputConnectionWrapper extends IInputContext.Stub { private static final int DO_PERFORM_PRIVATE_COMMAND = 120; private static final int DO_CLEAR_META_KEY_STATES = 130; - private InputConnection mInputConnection; + private WeakReference<InputConnection> mInputConnection; private Looper mMainLooper; private Handler mH; @@ -57,17 +61,21 @@ public class IInputConnectionWrapper extends IInputContext.Stub { } public IInputConnectionWrapper(Looper mainLooper, InputConnection conn) { - mInputConnection = conn; + mInputConnection = new WeakReference<InputConnection>(conn); mMainLooper = mainLooper; mH = new MyHandler(mMainLooper); } - public void getTextAfterCursor(int length, int seq, IInputContextCallback callback) { - dispatchMessage(obtainMessageISC(DO_GET_TEXT_AFTER_CURSOR, length, seq, callback)); + public boolean isActive() { + return true; + } + + public void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback) { + dispatchMessage(obtainMessageIISC(DO_GET_TEXT_AFTER_CURSOR, length, flags, seq, callback)); } - public void getTextBeforeCursor(int length, int seq, IInputContextCallback callback) { - dispatchMessage(obtainMessageISC(DO_GET_TEXT_BEFORE_CURSOR, length, seq, callback)); + public void getTextBeforeCursor(int length, int flags, int seq, IInputContextCallback callback) { + dispatchMessage(obtainMessageIISC(DO_GET_TEXT_BEFORE_CURSOR, length, flags, seq, callback)); } public void getCursorCapsMode(int reqModes, int seq, IInputContextCallback callback) { @@ -88,6 +96,14 @@ public class IInputConnectionWrapper extends IInputContext.Stub { dispatchMessage(obtainMessageO(DO_COMMIT_COMPLETION, text)); } + public void setSelection(int start, int end) { + dispatchMessage(obtainMessageII(DO_SET_SELECTION, start, end)); + } + + public void performContextMenuAction(int id) { + dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0)); + } + public void setComposingText(CharSequence text, int newCursorPosition) { dispatchMessage(obtainMessageIO(DO_SET_COMPOSING_TEXT, newCursorPosition, text)); } @@ -147,8 +163,14 @@ public class IInputConnectionWrapper extends IInputContext.Stub { case DO_GET_TEXT_AFTER_CURSOR: { SomeArgs args = (SomeArgs)msg.obj; try { - args.callback.setTextAfterCursor(mInputConnection.getTextAfterCursor(msg.arg1), - args.seq); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "getTextAfterCursor on inactive InputConnection"); + args.callback.setTextAfterCursor(null, args.seq); + return; + } + args.callback.setTextAfterCursor(ic.getTextAfterCursor( + msg.arg1, msg.arg2), args.seq); } catch (RemoteException e) { Log.w(TAG, "Got RemoteException calling setTextAfterCursor", e); } @@ -157,8 +179,14 @@ public class IInputConnectionWrapper extends IInputContext.Stub { case DO_GET_TEXT_BEFORE_CURSOR: { SomeArgs args = (SomeArgs)msg.obj; try { - args.callback.setTextBeforeCursor(mInputConnection.getTextBeforeCursor(msg.arg1), - args.seq); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "getTextBeforeCursor on inactive InputConnection"); + args.callback.setTextBeforeCursor(null, args.seq); + return; + } + args.callback.setTextBeforeCursor(ic.getTextBeforeCursor( + msg.arg1, msg.arg2), args.seq); } catch (RemoteException e) { Log.w(TAG, "Got RemoteException calling setTextBeforeCursor", e); } @@ -167,7 +195,13 @@ public class IInputConnectionWrapper extends IInputContext.Stub { case DO_GET_CURSOR_CAPS_MODE: { SomeArgs args = (SomeArgs)msg.obj; try { - args.callback.setCursorCapsMode(mInputConnection.getCursorCapsMode(msg.arg1), + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "getCursorCapsMode on inactive InputConnection"); + args.callback.setCursorCapsMode(0, args.seq); + return; + } + args.callback.setCursorCapsMode(ic.getCursorCapsMode(msg.arg1), args.seq); } catch (RemoteException e) { Log.w(TAG, "Got RemoteException calling setCursorCapsMode", e); @@ -177,7 +211,13 @@ public class IInputConnectionWrapper extends IInputContext.Stub { case DO_GET_EXTRACTED_TEXT: { SomeArgs args = (SomeArgs)msg.obj; try { - args.callback.setExtractedText(mInputConnection.getExtractedText( + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "getExtractedText on inactive InputConnection"); + args.callback.setExtractedText(null, args.seq); + return; + } + args.callback.setExtractedText(ic.getExtractedText( (ExtractedTextRequest)args.arg1, msg.arg1), args.seq); } catch (RemoteException e) { Log.w(TAG, "Got RemoteException calling setExtractedText", e); @@ -185,52 +225,130 @@ public class IInputConnectionWrapper extends IInputContext.Stub { return; } case DO_COMMIT_TEXT: { - mInputConnection.commitText((CharSequence)msg.obj, msg.arg1); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitText on inactive InputConnection"); + return; + } + ic.commitText((CharSequence)msg.obj, msg.arg1); + return; + } + case DO_SET_SELECTION: { + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "setSelection on inactive InputConnection"); + return; + } + ic.setSelection(msg.arg1, msg.arg2); + return; + } + case DO_PERFORM_CONTEXT_MENU_ACTION: { + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "performContextMenuAction on inactive InputConnection"); + return; + } + ic.performContextMenuAction(msg.arg1); return; } case DO_COMMIT_COMPLETION: { - mInputConnection.commitCompletion((CompletionInfo)msg.obj); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitCompletion on inactive InputConnection"); + return; + } + ic.commitCompletion((CompletionInfo)msg.obj); return; } case DO_SET_COMPOSING_TEXT: { - mInputConnection.setComposingText((CharSequence)msg.obj, msg.arg1); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "setComposingText on inactive InputConnection"); + return; + } + ic.setComposingText((CharSequence)msg.obj, msg.arg1); return; } case DO_FINISH_COMPOSING_TEXT: { - mInputConnection.finishComposingText(); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "finishComposingText on inactive InputConnection"); + return; + } + ic.finishComposingText(); return; } case DO_SEND_KEY_EVENT: { - mInputConnection.sendKeyEvent((KeyEvent)msg.obj); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "sendKeyEvent on inactive InputConnection"); + return; + } + ic.sendKeyEvent((KeyEvent)msg.obj); return; } case DO_CLEAR_META_KEY_STATES: { - mInputConnection.clearMetaKeyStates(msg.arg1); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "clearMetaKeyStates on inactive InputConnection"); + return; + } + ic.clearMetaKeyStates(msg.arg1); return; } case DO_DELETE_SURROUNDING_TEXT: { - mInputConnection.deleteSurroundingText(msg.arg1, msg.arg2); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "deleteSurroundingText on inactive InputConnection"); + return; + } + ic.deleteSurroundingText(msg.arg1, msg.arg2); return; } case DO_BEGIN_BATCH_EDIT: { - mInputConnection.beginBatchEdit(); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "beginBatchEdit on inactive InputConnection"); + return; + } + ic.beginBatchEdit(); return; } case DO_END_BATCH_EDIT: { - mInputConnection.beginBatchEdit(); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "endBatchEdit on inactive InputConnection"); + return; + } + ic.endBatchEdit(); return; } case DO_HIDE_STATUS_ICON: { - mInputConnection.hideStatusIcon(); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "hideStatusIcon on inactive InputConnection"); + return; + } + ic.hideStatusIcon(); return; } case DO_SHOW_STATUS_ICON: { - mInputConnection.showStatusIcon((String)msg.obj, msg.arg1); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "showStatusIcon on inactive InputConnection"); + return; + } + ic.showStatusIcon((String)msg.obj, msg.arg1); return; } case DO_PERFORM_PRIVATE_COMMAND: { + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "performPrivateCommand on inactive InputConnection"); + return; + } SomeArgs args = (SomeArgs)msg.obj; - mInputConnection.performPrivateCommand((String)args.arg1, + ic.performPrivateCommand((String)args.arg1, (Bundle)args.arg2); return; } @@ -257,6 +375,13 @@ public class IInputConnectionWrapper extends IInputContext.Stub { return mH.obtainMessage(what, arg1, 0, args); } + Message obtainMessageIISC(int what, int arg1, int arg2, int seq, IInputContextCallback callback) { + SomeArgs args = new SomeArgs(); + args.callback = callback; + args.seq = seq; + return mH.obtainMessage(what, arg1, arg2, args); + } + Message obtainMessageIOSC(int what, int arg1, Object arg2, int seq, IInputContextCallback callback) { SomeArgs args = new SomeArgs(); diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl index b048ce2..7cc8ada 100644 --- a/core/java/com/android/internal/view/IInputContext.aidl +++ b/core/java/com/android/internal/view/IInputContext.aidl @@ -29,9 +29,9 @@ import com.android.internal.view.IInputContextCallback; * {@hide} */ oneway interface IInputContext { - void getTextBeforeCursor(int length, int seq, IInputContextCallback callback); + void getTextBeforeCursor(int length, int flags, int seq, IInputContextCallback callback); - void getTextAfterCursor(int length, int seq, IInputContextCallback callback); + void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback); void getCursorCapsMode(int reqModes, int seq, IInputContextCallback callback); @@ -48,6 +48,10 @@ import com.android.internal.view.IInputContextCallback; void commitCompletion(in CompletionInfo completion); + void setSelection(int start, int end); + + void performContextMenuAction(int id); + void beginBatchEdit(); void endBatchEdit(); diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl index f650713..9b00402 100644 --- a/core/java/com/android/internal/view/IInputMethod.aidl +++ b/core/java/com/android/internal/view/IInputMethod.aidl @@ -38,9 +38,9 @@ oneway interface IInputMethod { void unbindInput(); - void startInput(in EditorInfo attribute); + void startInput(in IInputContext inputContext, in EditorInfo attribute); - void restartInput(in EditorInfo attribute); + void restartInput(in IInputContext inputContext, in EditorInfo attribute); void createSession(IInputMethodCallback callback); @@ -48,7 +48,7 @@ oneway interface IInputMethod { void revokeSession(IInputMethodSession session); - void showSoftInput(boolean explicit); + void showSoftInput(int flags); void hideSoftInput(); } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 2a15bdb..2f5cd14 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -35,7 +35,8 @@ interface IInputMethodManager { void removeClient(in IInputMethodClient client); InputBindResult startInput(in IInputMethodClient client, - in EditorInfo attribute, boolean initial, boolean needResult); + IInputContext inputContext, in EditorInfo attribute, + boolean initial, boolean needResult); void finishInput(in IInputMethodClient client); void showSoftInput(in IInputMethodClient client, int flags); void hideSoftInput(in IInputMethodClient client, int flags); diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index a9ba5f6..af4ad25 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -151,11 +151,11 @@ public class InputConnectionWrapper implements InputConnection { mIInputContext = inputContext; } - public CharSequence getTextAfterCursor(int length) { + public CharSequence getTextAfterCursor(int length, int flags) { CharSequence value = null; try { InputContextCallback callback = InputContextCallback.getInstance(); - mIInputContext.getTextAfterCursor(length, callback.mSeq, callback); + mIInputContext.getTextAfterCursor(length, flags, callback.mSeq, callback); synchronized (callback) { callback.waitForResultLocked(); if (callback.mHaveValue) { @@ -169,11 +169,11 @@ public class InputConnectionWrapper implements InputConnection { return value; } - public CharSequence getTextBeforeCursor(int length) { + public CharSequence getTextBeforeCursor(int length, int flags) { CharSequence value = null; try { InputContextCallback callback = InputContextCallback.getInstance(); - mIInputContext.getTextBeforeCursor(length, callback.mSeq, callback); + mIInputContext.getTextBeforeCursor(length, flags, callback.mSeq, callback); synchronized (callback) { callback.waitForResultLocked(); if (callback.mHaveValue) { @@ -241,6 +241,24 @@ public class InputConnectionWrapper implements InputConnection { } } + public boolean setSelection(int start, int end) { + try { + mIInputContext.setSelection(start, end); + return true; + } catch (RemoteException e) { + return false; + } + } + + public boolean performContextMenuAction(int id) { + try { + mIInputContext.performContextMenuAction(id); + return true; + } catch (RemoteException e) { + return false; + } + } + public boolean setComposingText(CharSequence text, int newCursorPosition) { try { mIInputContext.setComposingText(text, newCursorPosition); diff --git a/core/java/com/android/internal/view/menu/IconMenuItemView.java b/core/java/com/android/internal/view/menu/IconMenuItemView.java index 558a4c3..9e1f2ae 100644 --- a/core/java/com/android/internal/view/menu/IconMenuItemView.java +++ b/core/java/com/android/internal/view/menu/IconMenuItemView.java @@ -142,9 +142,6 @@ public final class IconMenuItemView extends TextView implements MenuView.ItemVie } void setCaptionMode(boolean shortcut) { - - mShortcutCaptionMode = shortcut && (mItemData.shouldShowShortcut()); - /* * If there is no item model, don't do any of the below (for example, * the 'More' item doesn't have a model) @@ -153,6 +150,8 @@ public final class IconMenuItemView extends TextView implements MenuView.ItemVie return; } + mShortcutCaptionMode = shortcut && (mItemData.shouldShowShortcut()); + CharSequence text = mItemData.getTitleForItemView(this); if (mShortcutCaptionMode) { diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java index 32513cd..e155875 100644 --- a/core/java/com/android/internal/view/menu/ListMenuItemView.java +++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java @@ -220,7 +220,7 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView mRadioButton = (RadioButton) inflater.inflate(com.android.internal.R.layout.list_menu_item_radio, this, false); - addView(mRadioButton, 0); + addView(mRadioButton); } private void insertCheckBox() { @@ -228,7 +228,7 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView mCheckBox = (CheckBox) inflater.inflate(com.android.internal.R.layout.list_menu_item_checkbox, this, false); - addView(mCheckBox, 0); + addView(mCheckBox); } public boolean prefersCondensedTitle() { diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index a2673a5..648d944 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -16,175 +16,74 @@ package com.android.internal.widget; -import android.content.res.TypedArray; import android.os.Bundle; -import android.os.Handler; import android.text.Editable; -import android.text.Selection; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.TextUtils; import android.text.method.KeyListener; import android.util.Log; -import android.util.LogPrinter; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.widget.TextView; -class ComposingText { -} - public class EditableInputConnection extends BaseInputConnection { private static final boolean DEBUG = false; private static final String TAG = "EditableInputConnection"; - public static final Object COMPOSING = new ComposingText(); - private final TextView mTextView; - private Object[] mDefaultComposingSpans; - public EditableInputConnection(TextView textview) { - super(textview); + super(textview, false); mTextView = textview; } - public static final void removeComposingSpans(Spannable text) { - text.removeSpan(COMPOSING); - Object[] sps = text.getSpans(0, text.length(), Object.class); - if (sps != null) { - for (int i=sps.length-1; i>=0; i--) { - Object o = sps[i]; - if ((text.getSpanFlags(o)&Spanned.SPAN_COMPOSING) != 0) { - text.removeSpan(o); - } - } - } - } - - public static void setComposingSpans(Spannable text) { - final Object[] sps = text.getSpans(0, text.length(), Object.class); - if (sps != null) { - for (int i=sps.length-1; i>=0; i--) { - final Object o = sps[i]; - if (o == COMPOSING) { - text.removeSpan(o); - continue; - } - final int fl = text.getSpanFlags(o); - if ((fl&(Spanned.SPAN_COMPOSING|Spanned.SPAN_POINT_MARK_MASK)) - != (Spanned.SPAN_COMPOSING|Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)) { - text.setSpan(o, text.getSpanStart(o), text.getSpanEnd(o), - (fl&Spanned.SPAN_POINT_MARK_MASK) - | Spanned.SPAN_COMPOSING - | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } + public Editable getEditable() { + TextView tv = mTextView; + if (tv != null) { + return tv.getEditableText(); } - - text.setSpan(COMPOSING, 0, text.length(), - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING); - } - - public static int getComposingSpanStart(Spannable text) { - return text.getSpanStart(COMPOSING); + return null; } - public static int getComposingSpanEnd(Spannable text) { - return text.getSpanEnd(COMPOSING); + public boolean beginBatchEdit() { + mTextView.beginBatchEdit(); + return true; } - public boolean setComposingText(CharSequence text, int newCursorPosition) { - if (DEBUG) Log.v(TAG, "setComposingText " + text); - replaceText(text, newCursorPosition, true); + public boolean endBatchEdit() { + mTextView.endBatchEdit(); return true; } - - public boolean finishComposingText() { - if (DEBUG) Log.v(TAG, "finishComposingText"); + + public boolean clearMetaKeyStates(int states) { final Editable content = getEditable(); - if (content != null) { - removeComposingSpans(content); - } + if (content == null) return false; + KeyListener kl = mTextView.getKeyListener(); + if (kl != null) kl.clearMetaKeyState(mTextView, content, states); return true; } - public boolean commitText(CharSequence text, int newCursorPosition) { - if (DEBUG) Log.v(TAG, "commitText " + text); - replaceText(text, newCursorPosition, false); - return true; - } - public boolean commitCompletion(CompletionInfo text) { if (DEBUG) Log.v(TAG, "commitCompletion " + text); + mTextView.beginBatchEdit(); mTextView.onCommitCompletion(text); + mTextView.endBatchEdit(); return true; } - public CharSequence getTextBeforeCursor(int length) { - final Editable content = getEditable(); - if (content == null) return null; - - int a = Selection.getSelectionStart(content); - int b = Selection.getSelectionEnd(content); - - if (a > b) { - int tmp = a; - a = b; - b = tmp; - } - - if (length > a) { - length = a; - } - - return content.subSequence(a - length, a); - } - - public CharSequence getTextAfterCursor(int length) { - final Editable content = getEditable(); - if (content == null) return null; - - int a = Selection.getSelectionStart(content); - int b = Selection.getSelectionEnd(content); - - if (a > b) { - int tmp = a; - a = b; - b = tmp; - } - - if (b + length > content.length()) { - length = content.length() - b; - } - - return content.subSequence(b, b + length); - } - - public int getCursorCapsMode(int reqModes) { - final Editable content = getEditable(); - if (content == null) return 0; - - int a = Selection.getSelectionStart(content); - int b = Selection.getSelectionEnd(content); - - if (a > b) { - int tmp = a; - a = b; - b = tmp; - } - - return TextUtils.getCapsMode(content, a, reqModes); + public boolean performContextMenuAction(int id) { + if (DEBUG) Log.v(TAG, "performContextMenuAction " + id); + mTextView.beginBatchEdit(); + mTextView.onTextContextMenuItem(id); + mTextView.endBatchEdit(); + return true; } public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { if (mTextView != null) { ExtractedText et = new ExtractedText(); if (mTextView.extractText(request, et)) { - if ((flags&EXTRACTED_TEXT_MONITOR) != 0) { + if ((flags&GET_EXTRACTED_TEXT_MONITOR) != 0) { mTextView.setExtracting(request); } return et; @@ -193,173 +92,8 @@ public class EditableInputConnection extends BaseInputConnection { return null; } - public boolean deleteSurroundingText(int leftLength, int rightLength) { - if (DEBUG) Log.v(TAG, "deleteSurroundingText " + leftLength - + " / " + rightLength); - final Editable content = getEditable(); - if (content == null) return false; - - int a = Selection.getSelectionStart(content); - int b = Selection.getSelectionEnd(content); - - if (a > b) { - int tmp = a; - a = b; - b = tmp; - } - - // ignore the composing text. - int ca = content.getSpanStart(COMPOSING); - int cb = content.getSpanEnd(COMPOSING); - if (cb < ca) { - int tmp = ca; - ca = cb; - cb = tmp; - } - if (ca != -1 && cb != -1) { - if (ca < a) a = ca; - if (cb > b) b = cb; - } - - int deleted = 0; - - if (leftLength > 0) { - int start = a - leftLength; - if (start < 0) start = 0; - content.delete(start, a); - deleted = a - start; - } - - if (rightLength > 0) { - b = b - deleted; - - int end = b + rightLength; - if (end > content.length()) end = content.length(); - - content.delete(b, end); - } - - return true; - } - - public boolean beginBatchEdit() { - if (mTextView == null) return false; - mTextView.onBeginBatchEdit(); - return true; - } - - public boolean endBatchEdit() { - if (mTextView == null) return false; - mTextView.onEndBatchEdit(); - return true; - } - - public boolean clearMetaKeyStates(int states) { - final Editable content = getEditable(); - if (content == null) return false; - KeyListener kl = mTextView.getKeyListener(); - if (kl != null) kl.clearMetaKeyState(mTextView, content, states); - return true; - } - public boolean performPrivateCommand(String action, Bundle data) { - if (mTextView == null) return false; mTextView.onPrivateIMECommand(action, data); return true; } - - private Editable getEditable() { - TextView tv = mTextView; - if (tv != null) { - return tv.getEditableText(); - } - return null; - } - - private void replaceText(CharSequence text, int newCursorPosition, - boolean composing) { - final Editable content = getEditable(); - - // delete composing text set previously. - int a = content.getSpanStart(COMPOSING); - int b = content.getSpanEnd(COMPOSING); - - if (DEBUG) Log.v(TAG, "Composing span: " + a + " to " + b); - - if (b < a) { - int tmp = a; - a = b; - b = tmp; - } - - if (a != -1 && b != -1) { - removeComposingSpans(content); - } else { - a = Selection.getSelectionStart(content); - b = Selection.getSelectionEnd(content); - if (a >=0 && b>= 0 && a != b) { - if (b < a) { - int tmp = a; - a = b; - b = tmp; - } - } - } - - if (composing) { - Spannable sp = null; - if (!(text instanceof Spannable)) { - sp = new SpannableStringBuilder(text); - text = sp; - if (mDefaultComposingSpans == null) { - TypedArray ta = mTextView.getContext().getTheme() - .obtainStyledAttributes(new int[] { - com.android.internal.R.attr.candidatesTextStyleSpans - }); - CharSequence style = ta.getText(0); - ta.recycle(); - if (style != null && style instanceof Spanned) { - mDefaultComposingSpans = ((Spanned)style).getSpans( - 0, style.length(), Object.class); - } - } - if (mDefaultComposingSpans != null) { - for (int i = 0; i < mDefaultComposingSpans.length; ++i) { - sp.setSpan(mDefaultComposingSpans[i], 0, sp.length(), - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - } else { - sp = (Spannable)text; - } - setComposingSpans(sp); - } - - // Adjust newCursorPosition to be relative the start of the text. - newCursorPosition += a; - - if (DEBUG) Log.v(TAG, "Replacing from " + a + " to " + b + " with \"" - + text + "\", composing=" + composing - + ", type=" + text.getClass().getCanonicalName()); - - if (DEBUG) { - LogPrinter lp = new LogPrinter(Log.VERBOSE, TAG); - lp.println("Current text:"); - TextUtils.dumpSpans(content, lp, " "); - lp.println("Composing text:"); - TextUtils.dumpSpans(text, lp, " "); - } - - content.replace(a, b, text); - if (newCursorPosition < 0) newCursorPosition = 0; - if (newCursorPosition > content.length()) - newCursorPosition = content.length(); - Selection.setSelection(content, newCursorPosition); - - if (DEBUG) { - LogPrinter lp = new LogPrinter(Log.VERBOSE, TAG); - lp.println("Final text:"); - TextUtils.dumpSpans(content, lp, " "); - } - } } diff --git a/core/java/com/google/android/net/GoogleHttpClient.java b/core/java/com/google/android/net/GoogleHttpClient.java index 4656aff..2fcb0c3 100644 --- a/core/java/com/google/android/net/GoogleHttpClient.java +++ b/core/java/com/google/android/net/GoogleHttpClient.java @@ -16,18 +16,28 @@ package com.google.android.net; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.net.http.AndroidHttpClient; +import android.os.Build; +import android.os.NetStat; +import android.os.SystemClock; +import android.provider.Checkin; +import android.util.Config; +import android.util.Log; +import org.apache.http.HttpEntity; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.ProtocolException; -import org.apache.http.impl.client.RequestWrapper; -import org.apache.http.impl.client.EntityEnclosingRequestWrapper; -import org.apache.http.client.HttpClient; import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpClient; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.impl.client.EntityEnclosingRequestWrapper; +import org.apache.http.impl.client.RequestWrapper; import org.apache.http.params.HttpParams; import org.apache.http.protocol.HttpContext; @@ -35,25 +45,13 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.os.SystemClock; -import android.os.Build; -import android.net.http.AndroidHttpClient; -import android.provider.Checkin; -import android.util.Config; -import android.util.Log; - /** * {@link AndroidHttpClient} wrapper that uses {@link UrlRules} to rewrite URLs * and otherwise tweak HTTP requests. */ public class GoogleHttpClient implements HttpClient { - private static final String TAG = "GoogleHttpClient"; - private final AndroidHttpClient mClient; - private final ContentResolver mResolver; - private final String mUserAgent; + private static final String TAG = "GoogleHttpClient"; /** Exception thrown when a request is blocked by the URL rules. */ public static class BlockedRequestException extends IOException { @@ -63,6 +61,10 @@ public class GoogleHttpClient implements HttpClient { mRule = rule; } } + + private final AndroidHttpClient mClient; + private final ContentResolver mResolver; + private final String mUserAgent; /** * Create an HTTP client. Normally one client is shared throughout an app. @@ -120,8 +122,37 @@ public class GoogleHttpClient implements HttpClient { String code = "Error"; long start = SystemClock.elapsedRealtime(); try { - HttpResponse response = mClient.execute(request, context); - code = Integer.toString(response.getStatusLine().getStatusCode()); + HttpResponse response; + // TODO: if we're logging network stats, and if the apache library is configured + // to follow redirects, count each redirect as an additional round trip. + + // see if we're logging network stats. + boolean logNetworkStats = NetworkStatsEntity.shouldLogNetworkStats(); + + if (logNetworkStats) { + int uid = android.os.Process.myUid(); + long startTx = NetStat.getUidTxBytes(uid); + long startRx = NetStat.getUidRxBytes(uid); + + response = mClient.execute(request, context); + code = Integer.toString(response.getStatusLine().getStatusCode()); + + HttpEntity origEntity = response == null ? null : response.getEntity(); + if (origEntity != null) { + // yeah, we compute the same thing below. we do need to compute this here + // so we can wrap the HttpEntity in the response. + long now = SystemClock.elapsedRealtime(); + long elapsed = now - start; + NetworkStatsEntity entity = new NetworkStatsEntity(origEntity, + mUserAgent, uid, startTx, startRx, + elapsed /* response latency */, now /* processing start time */); + response.setEntity(entity); + } + } else { + response = mClient.execute(request, context); + code = Integer.toString(response.getStatusLine().getStatusCode()); + } + return response; } catch (IOException e) { code = "IOException"; diff --git a/core/java/com/google/android/net/NetStats.java b/core/java/com/google/android/net/NetStats.java deleted file mode 100644 index fee8219..0000000 --- a/core/java/com/google/android/net/NetStats.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.google.android.net; - -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; -import java.util.NoSuchElementException; -import java.util.StringTokenizer; - -/** - * Gets network send/receive statistics for this process. - * The statistics come from /proc/pid/stat, using the ATOP kernel modification. - */ -public class NetStats { - private static String statsFile = "/proc/" + android.os.Process.myPid() + "/stat"; - - private static String TAG = "netstat"; - - /** - * Returns network stats for this process. - * Returns stats of 0 if problem encountered. - * - * @return [bytes sent, bytes received] - */ - public static long[] getStats() { - long result[] = new long[2]; - - try { - BufferedReader br = new BufferedReader(new FileReader(statsFile), 512); - String line = br.readLine(); // Skip first line - line = br.readLine(); - StringTokenizer st = new StringTokenizer(line); - st.nextToken(); // disk read - st.nextToken(); // disk sectors - st.nextToken(); // disk write - st.nextToken(); // disk sectors - st.nextToken(); // tcp send - result[0] = Long.parseLong(st.nextToken()); // tcp bytes sent - st.nextToken(); //tcp recv - result[1] = Long.parseLong(st.nextToken()); // tcp bytes recv - } catch (IOException e) { - // Probably wrong kernel; no point logging exception - } catch (NoSuchElementException e) { - } catch (NullPointerException e) { - } - return result; - } -} diff --git a/core/java/com/google/android/net/NetworkStatsEntity.java b/core/java/com/google/android/net/NetworkStatsEntity.java new file mode 100644 index 0000000..f5d2349 --- /dev/null +++ b/core/java/com/google/android/net/NetworkStatsEntity.java @@ -0,0 +1,85 @@ +/* + * 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. + */ + +package com.google.android.net; + +import android.os.NetStat; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.util.EventLog; + +import org.apache.http.HttpEntity; +import org.apache.http.entity.HttpEntityWrapper; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + + +public class NetworkStatsEntity extends HttpEntityWrapper { + + private static final int HTTP_STATS_EVENT = 52001; + + private class NetworkStatsInputStream extends FilterInputStream { + + public NetworkStatsInputStream(InputStream wrapped) { + super(wrapped); + } + + @Override + public void close() throws IOException { + try { + super.close(); + } finally { + long processingTime = SystemClock.elapsedRealtime() - mProcessingStartTime; + long tx = NetStat.getUidTxBytes(mUid); + long rx = NetStat.getUidRxBytes(mUid); + + EventLog.writeEvent(HTTP_STATS_EVENT, mUa, mResponseLatency, processingTime, + tx - mStartTx, rx - mStartRx); + } + } + } + + private final String mUa; + private final int mUid; + private final long mStartTx; + private final long mStartRx; + private final long mResponseLatency; + private final long mProcessingStartTime; + + public NetworkStatsEntity(HttpEntity orig, String ua, + int uid, long startTx, long startRx, long responseLatency, + long processingStartTime) { + super(orig); + this.mUa = ua; + this.mUid = uid; + this.mStartTx = startTx; + this.mStartRx = startRx; + this.mResponseLatency = responseLatency; + this.mProcessingStartTime = processingStartTime; + } + + public static boolean shouldLogNetworkStats() { + return "1".equals(SystemProperties.get("googlehttpclient.logstats")); + } + + @Override + public InputStream getContent() throws IOException { + InputStream orig = super.getContent(); + return new NetworkStatsInputStream(orig); + } +} diff --git a/core/java/com/google/android/util/SimplePullParser.java b/core/java/com/google/android/util/SimplePullParser.java index 95f2ddb..031790b 100644 --- a/core/java/com/google/android/util/SimplePullParser.java +++ b/core/java/com/google/android/util/SimplePullParser.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.io.Reader; +import java.io.Closeable; import android.util.Xml; import android.util.Log; @@ -41,6 +42,7 @@ public class SimplePullParser { private String mLogTag = null; private final XmlPullParser mParser; + private Closeable source; private String mCurrentStartTag; /** @@ -56,6 +58,7 @@ public class SimplePullParser { moveToStartDocument(parser); mParser = parser; mCurrentStartTag = null; + source = stream; } catch (XmlPullParserException e) { throw new ParseException(e); } @@ -68,6 +71,7 @@ public class SimplePullParser { public SimplePullParser(XmlPullParser parser) { mParser = parser; mCurrentStartTag = null; + source = null; } /** @@ -89,6 +93,7 @@ public class SimplePullParser { moveToStartDocument(parser); mParser = parser; mCurrentStartTag = null; + source = reader; } catch (XmlPullParserException e) { throw new ParseException(e); } @@ -171,6 +176,12 @@ public class SimplePullParser { } if (eventType == XmlPullParser.END_DOCUMENT && parentDepth == 0) { + // we could just rely on the caller calling close(), which it should, but try + // to auto-close for clients that might have missed doing so. + if (source != null) { + source.close(); + source = null; + } return null; } @@ -333,6 +344,20 @@ public class SimplePullParser { } /** + * Close this SimplePullParser and any underlying resources (e.g., its InputStream or + * Reader source) used by this SimplePullParser. + */ + public void close() { + if (source != null) { + try { + source.close(); + } catch (IOException ioe) { + // ignore + } + } + } + + /** * Returns the string value of the named attribute. An exception will * be thrown if the attribute is not present or is not a valid long. * diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 32c3a54..6e5c4e0 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -42,7 +42,6 @@ LOCAL_SRC_FILES:= \ android_os_SystemClock.cpp \ android_os_SystemProperties.cpp \ android_os_UEventObserver.cpp \ - android_os_NetStat.cpp \ android_os_Hardware.cpp \ android_net_LocalSocketImpl.cpp \ android_net_NetUtils.cpp \ @@ -146,7 +145,8 @@ LOCAL_SHARED_LIBRARIES := \ libcorecg \ libsqlite \ libdvm \ - libGLES_CM \ + libEGL \ + libGLESv1_CM \ libhardware \ libhardware_legacy \ libsonivox \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 097ffac..40dc2a1 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -129,7 +129,6 @@ extern int register_android_os_SystemClock(JNIEnv* env); extern int register_android_os_FileObserver(JNIEnv *env); extern int register_android_os_FileUtils(JNIEnv *env); extern int register_android_os_UEventObserver(JNIEnv* env); -extern int register_android_os_NetStat(JNIEnv* env); extern int register_android_os_MemoryFile(JNIEnv* env); extern int register_android_net_LocalSocketImpl(JNIEnv* env); extern int register_android_net_NetworkUtils(JNIEnv* env); @@ -502,6 +501,7 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) JavaVMOption opt; char propBuf[PROPERTY_VALUE_MAX]; char stackTraceFileBuf[PROPERTY_VALUE_MAX]; + char dexoptFlagsBuf[PROPERTY_VALUE_MAX]; char enableAssertBuf[sizeof("-ea:")-1 + PROPERTY_VALUE_MAX]; char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX]; char* stackTraceFile = NULL; @@ -509,7 +509,6 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) char* cp; bool checkJni = false; bool logStdio = false; - bool verifyJava = true; enum { kEMDefault, kEMIntPortable, kEMIntFast } executionMode = kEMDefault; blockSigpipe(); @@ -536,15 +535,6 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) } } - property_get("dalvik.vm.verify-bytecode", propBuf, ""); - if (strcmp(propBuf, "true") == 0) { - verifyJava = true; - } else if (strcmp(propBuf, "false") == 0) { - verifyJava = false; - } else { - /* bad value or not defined; use default */ - } - property_get("dalvik.vm.execution-mode", propBuf, ""); if (strcmp(propBuf, "int:portable") == 0) { executionMode = kEMIntPortable; @@ -609,21 +599,49 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) mOptions.add(opt); /* - * Enable or disable bytecode verification. - * - * We don't optimize classes that haven't been verified, but that only - * matters if we do "just-in-time" DEX optimization. + * Enable or disable dexopt features, such as bytecode verification and + * calculation of register maps for precise GC. */ - if (verifyJava) { - opt.optionString = "-Xverify:all"; - mOptions.add(opt); - opt.optionString = "-Xdexopt:verified"; - mOptions.add(opt); - } else { - opt.optionString = "-Xverify:none"; - mOptions.add(opt); - opt.optionString = "-Xdexopt:verified"; - mOptions.add(opt); + property_get("dalvik.vm.dexopt-flags", dexoptFlagsBuf, ""); + if (dexoptFlagsBuf[0] != '\0') { + const char* opc; + const char* val; + + opc = strstr(dexoptFlagsBuf, "v="); /* verification */ + if (opc != NULL) { + switch (*(opc+2)) { + case 'n': val = "-Xverify:none"; break; + case 'r': val = "-Xverify:remote"; break; + case 'a': val = "-Xverify:all"; break; + default: val = NULL; break; + } + + if (val != NULL) { + opt.optionString = val; + mOptions.add(opt); + } + } + + opc = strstr(dexoptFlagsBuf, "o="); /* optimization */ + if (opc != NULL) { + switch (*(opc+2)) { + case 'n': val = "-Xdexopt:none"; break; + case 'v': val = "-Xdexopt:verified"; break; + case 'a': val = "-Xdexopt:all"; break; + default: val = NULL; break; + } + + if (val != NULL) { + opt.optionString = val; + mOptions.add(opt); + } + } + + opc = strstr(dexoptFlagsBuf, "m=y"); /* register map */ + if (opc != NULL) { + opt.optionString = "-Xgenregmap"; + mOptions.add(opt); + } } /* enable debugging; set suspend=y to pause during VM init */ @@ -1066,7 +1084,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_net_LocalSocketImpl), REG_JNI(register_android_net_NetworkUtils), REG_JNI(register_android_net_wifi_WifiManager), - REG_JNI(register_android_os_NetStat), REG_JNI(register_android_os_MemoryFile), REG_JNI(register_com_android_internal_os_ZygoteInit), REG_JNI(register_android_hardware_Camera), diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index be8526d..332b01c 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -1,3 +1,5 @@ +#define LOG_TAG "BitmapFactory" + #include "SkImageDecoder.h" #include "SkPixelRef.h" #include "SkStream.h" @@ -481,6 +483,48 @@ static void nativeRequestCancel(JNIEnv*, jobject joptions) { (void)AutoDecoderCancel::RequestCancel(joptions); } +static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale, + jobject padding) { + + jbyte* array = env->GetByteArrayElements(chunkObject, 0); + if (array != NULL) { + size_t chunkSize = env->GetArrayLength(chunkObject); + void* storage = alloca(chunkSize); + android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage); + memcpy(chunk, array, chunkSize); + android::Res_png_9patch::deserialize(chunk); + + chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f); + chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f); + chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f); + chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f); + + for (int i = 0; i < chunk->numXDivs; i++) { + chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f); + if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) { + chunk->xDivs[i]++; + } + } + + for (int i = 0; i < chunk->numYDivs; i++) { + chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f); + if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) { + chunk->yDivs[i]++; + } + } + + memcpy(array, chunk, chunkSize); + + if (padding) { + GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop, + chunk->paddingRight, chunk->paddingBottom); + } + + env->ReleaseByteArrayElements(chunkObject, array, 0); + } + return chunkObject; +} + /////////////////////////////////////////////////////////////////////////////// static JNINativeMethod gMethods[] = { @@ -502,7 +546,13 @@ static JNINativeMethod gMethods[] = { { "nativeDecodeByteArray", "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", (void*)nativeDecodeByteArray + }, + + { "nativeScaleNinePatch", + "([BFLandroid/graphics/Rect;)[B", + (void*)nativeScaleNinePatch } + }; static JNINativeMethod gOptionsMethods[] = { diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index b9e5f67..605e4b8 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -93,7 +93,7 @@ public: SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas); return canvas->getDevice()->accessBitmap(false).height(); } - + static void setViewport(JNIEnv* env, jobject, SkCanvas* canvas, int width, int height) { canvas->setViewport(width, height); @@ -454,13 +454,32 @@ public: #endif } - static void drawBitmap__BitmapFFPaint(JNIEnv* env, jobject, + static void drawBitmap__BitmapFFPaint(JNIEnv* env, jobject jcanvas, SkCanvas* canvas, SkBitmap* bitmap, jfloat left, jfloat top, - SkPaint* paint) { + SkPaint* paint, + jboolean autoScale, jfloat densityScale) { SkScalar left_ = SkFloatToScalar(left); SkScalar top_ = SkFloatToScalar(top); - canvas->drawBitmap(*bitmap, left_, top_, paint); + + if (!autoScale || densityScale <= 0.0f) { + canvas->drawBitmap(*bitmap, left_, top_, paint); + } else { + canvas->save(); + SkScalar canvasScale = GraphicsJNI::getCanvasDensityScale(env, jcanvas); + SkScalar scale = canvasScale / SkFloatToScalar(densityScale); + canvas->scale(scale, scale); + + SkPaint filteredPaint; + if (paint) { + filteredPaint = *paint; + } + filteredPaint.setFilterBitmap(true); + + canvas->drawBitmap(*bitmap, left_, top_, &filteredPaint); + + canvas->restore(); + } } static void doDrawBitmap(JNIEnv* env, SkCanvas* canvas, SkBitmap* bitmap, @@ -492,7 +511,7 @@ public: static void drawBitmapArray(JNIEnv* env, jobject, SkCanvas* canvas, jintArray jcolors, int offset, int stride, - int x, int y, int width, int height, + jfloat x, jfloat y, int width, int height, jboolean hasAlpha, SkPaint* paint) { SkBitmap bitmap; @@ -508,7 +527,8 @@ public: return; } - canvas->drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), paint); + canvas->drawBitmap(bitmap, SkFloatToScalar(x), SkFloatToScalar(y), + paint); } static void drawBitmapMatrix(JNIEnv* env, jobject, SkCanvas* canvas, @@ -882,13 +902,13 @@ static JNINativeMethod gCanvasMethods[] = { {"native_drawRoundRect","(ILandroid/graphics/RectF;FFI)V", (void*) SkCanvasGlue::drawRoundRect}, {"native_drawPath","(III)V", (void*) SkCanvasGlue::drawPath}, - {"native_drawBitmap","(IIFFI)V", + {"native_drawBitmap","(IIFFIZF)V", (void*) SkCanvasGlue::drawBitmap__BitmapFFPaint}, {"native_drawBitmap","(IILandroid/graphics/Rect;Landroid/graphics/RectF;I)V", (void*) SkCanvasGlue::drawBitmapRF}, {"native_drawBitmap","(IILandroid/graphics/Rect;Landroid/graphics/Rect;I)V", (void*) SkCanvasGlue::drawBitmapRR}, - {"native_drawBitmap", "(I[IIIIIIIZI)V", + {"native_drawBitmap", "(I[IIIFFIIZI)V", (void*)SkCanvasGlue::drawBitmapArray}, {"nativeDrawBitmapMatrix", "(IIII)V", diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp index 65c2326..a285def 100644 --- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp +++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp @@ -56,11 +56,14 @@ public: break; // eof } - const jbyte* array = env->GetByteArrayElements(fJavaByteArray, - NULL); - memcpy(buffer, array, n); - env->ReleaseByteArrayElements(fJavaByteArray, - const_cast<jbyte*>(array), JNI_ABORT); + env->GetByteArrayRegion(fJavaByteArray, 0, n, + reinterpret_cast<jbyte*>(buffer)); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + SkDebugf("---- read:GetByteArrayRegion threw an exception\n"); + return 0; + } buffer = (void*)((char*)buffer + n); bytesRead += n; @@ -189,10 +192,15 @@ public: requested = fCapacity; } - jbyte* array = env->GetByteArrayElements(storage, NULL); - memcpy(array, buffer, requested); - env->ReleaseByteArrayElements(storage, array, 0); - + env->SetByteArrayRegion(storage, 0, requested, + reinterpret_cast<const jbyte*>(buffer)); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + SkDebugf("--- write:SetByteArrayElements threw an exception\n"); + return false; + } + fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_writeMethodID, storage, 0, requested); if (env->ExceptionCheck()) { diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 44113e5..6eebbdc 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -162,6 +162,7 @@ static jfieldID gBitmapConfig_nativeInstanceID; static jclass gCanvas_class; static jfieldID gCanvas_nativeInstanceID; +static jfieldID gCanvas_densityScaleID; static jclass gPaint_class; static jfieldID gPaint_nativeInstanceID; @@ -318,6 +319,13 @@ SkCanvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) { return c; } +SkScalar GraphicsJNI::getCanvasDensityScale(JNIEnv* env, jobject canvas) { + SkASSERT(env); + SkASSERT(canvas); + SkASSERT(env->IsInstanceOf(canvas, gCanvas_class)); + return SkFloatToScalar(env->GetFloatField(canvas, gCanvas_densityScaleID)); +} + SkPaint* GraphicsJNI::getNativePaint(JNIEnv* env, jobject paint) { SkASSERT(env); SkASSERT(paint); @@ -543,7 +551,8 @@ int register_android_graphics_Graphics(JNIEnv* env) gCanvas_class = make_globalref(env, "android/graphics/Canvas"); gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvas", "I"); - + gCanvas_densityScaleID = getFieldIDCheck(env, gCanvas_class, "mDensityScale", "F"); + gPaint_class = make_globalref(env, "android/graphics/Paint"); gPaint_nativeInstanceID = getFieldIDCheck(env, gPaint_class, "mNativePaint", "I"); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index e67b20b..e2dc9ac 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -38,6 +38,7 @@ public: static SkBitmap* getNativeBitmap(JNIEnv*, jobject bitmap); static SkPicture* getNativePicture(JNIEnv*, jobject picture); static SkRegion* getNativeRegion(JNIEnv*, jobject region); + static SkScalar getCanvasDensityScale(JNIEnv*, jobject canvas); /** Return the corresponding native config from the java Config enum, or kNo_Config if the java object is null. diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp index 9e943f3..b11edfc 100644 --- a/core/jni/android/graphics/NinePatch.cpp +++ b/core/jni/android/graphics/NinePatch.cpp @@ -47,19 +47,17 @@ public: static void draw(JNIEnv* env, SkCanvas* canvas, SkRect& bounds, const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint) { - const jbyte* array = env->GetByteArrayElements(chunkObj, 0); - if (array != NULL) { - size_t chunkSize = env->GetArrayLength(chunkObj); + size_t chunkSize = env->GetArrayLength(chunkObj); + void* storage = alloca(chunkSize); + env->GetByteArrayRegion(chunkObj, 0, chunkSize, + reinterpret_cast<jbyte*>(storage)); + if (!env->ExceptionCheck()) { // need to deserialize the chunk - void* storage = alloca(chunkSize); Res_png_9patch* chunk = static_cast<Res_png_9patch*>(storage); - memcpy(chunk, array, chunkSize); assert(chunkSize == chunk->serializedSize()); // this relies on deserialization being done in place Res_png_9patch::deserialize(chunk); NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL); - env->ReleaseByteArrayElements(chunkObj, const_cast<jbyte*>(array), - JNI_ABORT); } } @@ -102,23 +100,20 @@ public: SkRect bounds; GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds); - const jbyte* array = (jbyte*)env->GetByteArrayElements(chunkObj, 0); - if (array != NULL) { - size_t chunkSize = env->GetArrayLength(chunkObj); + size_t chunkSize = env->GetArrayLength(chunkObj); + void* storage = alloca(chunkSize); + env->GetByteArrayRegion(chunkObj, 0, chunkSize, + reinterpret_cast<jbyte*>(storage)); + if (!env->ExceptionCheck()) { // need to deserialize the chunk - void* storage = alloca(chunkSize); Res_png_9patch* chunk = static_cast<Res_png_9patch*>(storage); - memcpy(chunk, array, chunkSize); assert(chunkSize == chunk->serializedSize()); // this relies on deserialization being done in place Res_png_9patch::deserialize(chunk); SkRegion* region = NULL; NinePatch_Draw(NULL, bounds, *bitmap, *chunk, NULL, ®ion); - env->ReleaseByteArrayElements(chunkObj, const_cast<jbyte*>(array), - JNI_ABORT); return (jint)region; } - return 0; } diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index a81f252..79965b9 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -259,7 +259,7 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t } else { callback_flag = FRAME_CALLBACK_FLAG_NOOP; } - c->setFrameCallback(installed ? preview_callback : NULL, cookie, callback_flag); + c->setPreviewCallback(installed ? preview_callback : NULL, cookie, callback_flag); } static void autofocus_callback_impl(bool success, void *cookie) diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index e4586d9..307c6fd 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -63,11 +63,11 @@ struct audiorecord_callback_cookie { #define AUDIORECORD_ERROR -1 #define AUDIORECORD_ERROR_BAD_VALUE -2 #define AUDIORECORD_ERROR_INVALID_OPERATION -3 -#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT -4 -#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT -5 -#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT -6 -#define AUDIORECORD_ERROR_SETUP_INVALIDSTREAMTYPE -7 -#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED -8 +#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT -16 +#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT -17 +#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT -18 +#define AUDIORECORD_ERROR_SETUP_INVALIDSTREAMTYPE -19 +#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED -20 jint android_media_translateRecorderErrorCode(int code) { switch(code) { diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 99785a2..6bd3655 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -55,6 +55,8 @@ android_media_AudioSystem_setVolume(JNIEnv *env, jobject clazz, jint type, jint LOGV("setVolume(%d)", int(volume)); if (int(type) == AudioTrack::VOICE_CALL) { return check_AudioSystem_Command(AudioSystem::setStreamVolume(type, float(volume) / 100.0)); + } else if (int(type) == AudioTrack::BLUETOOTH_SCO) { + return check_AudioSystem_Command(AudioSystem::setStreamVolume(type, float(1.0))); } else { return check_AudioSystem_Command(AudioSystem::setStreamVolume(type, AudioSystem::linearToLog(volume))); } diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 6bbcaee..bbecc1b 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -53,6 +53,7 @@ struct fields_t { int STREAM_MUSIC; //... stream type constants int STREAM_ALARM; //... stream type constants int STREAM_NOTIFICATION; //... stream type constants + int STREAM_BLUETOOTH_SCO; //... stream type constants int MODE_STREAM; //... memory mode int MODE_STATIC; //... memory mode jfieldID nativeTrackInJavaObj; // stores in Java the native AudioTrack object @@ -95,13 +96,13 @@ class AudioTrackJniStorage { #define AUDIOTRACK_SUCCESS 0 #define AUDIOTRACK_ERROR -1 -#define AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM -2 -#define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELCOUNT -3 -#define AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT -4 -#define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE -5 -#define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED -6 -#define AUDIOTRACK_ERROR_BAD_VALUE -7 -#define AUDIOTRACK_ERROR_INVALID_OPERATION -8 +#define AUDIOTRACK_ERROR_BAD_VALUE -2 +#define AUDIOTRACK_ERROR_INVALID_OPERATION -3 +#define AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM -16 +#define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELCOUNT -17 +#define AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT -18 +#define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE -19 +#define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED -20 jint android_media_translateErrorCode(int code) { @@ -195,10 +196,12 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th atStreamType = AudioTrack::ALARM; } else if (streamType == javaAudioTrackFields.STREAM_NOTIFICATION) { atStreamType = AudioTrack::NOTIFICATION; + } else if (streamType == javaAudioTrackFields.STREAM_BLUETOOTH_SCO) { + atStreamType = AudioTrack::BLUETOOTH_SCO; } else { LOGE("Error creating AudioTrack: unknown stream type."); return AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE; - } + } // check the format. // This function was called from Java, so we compare the format against the Java constants @@ -663,6 +666,35 @@ static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobjec // ---------------------------------------------------------------------------- +// returns the minimum required size for the successful creation of a streaming AudioTrack +static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz, + jint sampleRateInHertz, jint nbChannels, jint audioFormat) { + int afSamplingRate; + int afFrameCount; + uint32_t afLatency; + + if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) { + return -1; + } + if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) { + return -1; + } + + if (AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) { + return -1; + } + + // Ensure that buffer depth covers at least audio hardware latency + uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSamplingRate); + uint32_t minFrameCount = (afFrameCount*sampleRateInHertz*minBufCount)/afSamplingRate; + int minBuffSize = minFrameCount + * (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1) + * nbChannels; + return minBuffSize; +} + + +// ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { // name, signature, funcPtr @@ -695,24 +727,27 @@ static JNINativeMethod gMethods[] = { {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload}, {"native_get_output_sample_rate", "()I", (void *)android_media_AudioTrack_get_output_sample_rate}, + {"native_get_min_buff_size", + "(III)I", (void *)android_media_AudioTrack_get_min_buff_size}, }; // field names found in android/media/AudioTrack.java -#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" -#define JAVA_CONST_PCM16_NAME "ENCODING_PCM_16BIT" -#define JAVA_CONST_PCM8_NAME "ENCODING_PCM_8BIT" -#define JAVA_CONST_BUFFER_COUNT_NAME "BUFFER_COUNT" -#define JAVA_CONST_STREAM_VOICE_CALL_NAME "STREAM_VOICE_CALL" -#define JAVA_CONST_STREAM_SYSTEM_NAME "STREAM_SYSTEM" -#define JAVA_CONST_STREAM_RING_NAME "STREAM_RING" -#define JAVA_CONST_STREAM_MUSIC_NAME "STREAM_MUSIC" -#define JAVA_CONST_STREAM_ALARM_NAME "STREAM_ALARM" -#define JAVA_CONST_STREAM_NOTIFICATION_NAME "STREAM_NOTIFICATION" -#define JAVA_CONST_MODE_STREAM_NAME "MODE_STREAM" -#define JAVA_CONST_MODE_STATIC_NAME "MODE_STATIC" -#define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj" -#define JAVA_JNIDATA_FIELD_NAME "mJniData" +#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" +#define JAVA_CONST_PCM16_NAME "ENCODING_PCM_16BIT" +#define JAVA_CONST_PCM8_NAME "ENCODING_PCM_8BIT" +#define JAVA_CONST_BUFFER_COUNT_NAME "BUFFER_COUNT" +#define JAVA_CONST_STREAM_VOICE_CALL_NAME "STREAM_VOICE_CALL" +#define JAVA_CONST_STREAM_SYSTEM_NAME "STREAM_SYSTEM" +#define JAVA_CONST_STREAM_RING_NAME "STREAM_RING" +#define JAVA_CONST_STREAM_MUSIC_NAME "STREAM_MUSIC" +#define JAVA_CONST_STREAM_ALARM_NAME "STREAM_ALARM" +#define JAVA_CONST_STREAM_NOTIFICATION_NAME "STREAM_NOTIFICATION" +#define JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME "STREAM_BLUETOOTH_SCO" +#define JAVA_CONST_MODE_STREAM_NAME "MODE_STREAM" +#define JAVA_CONST_MODE_STATIC_NAME "MODE_STATIC" +#define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj" +#define JAVA_JNIDATA_FIELD_NAME "mJniData" #define JAVA_AUDIOFORMAT_CLASS_NAME "android/media/AudioFormat" #define JAVA_AUDIOMANAGER_CLASS_NAME "android/media/AudioManager" @@ -810,28 +845,32 @@ int register_android_media_AudioTrack(JNIEnv *env) LOGE("Can't find %s", JAVA_AUDIOMANAGER_CLASS_NAME); return -1; } - if ( !android_media_getIntConstantFromClass(env, audioManagerClass, - JAVA_AUDIOMANAGER_CLASS_NAME, + if ( !android_media_getIntConstantFromClass(env, audioManagerClass, + JAVA_AUDIOMANAGER_CLASS_NAME, JAVA_CONST_STREAM_VOICE_CALL_NAME, &(javaAudioTrackFields.STREAM_VOICE_CALL)) - || !android_media_getIntConstantFromClass(env, audioManagerClass, - JAVA_AUDIOMANAGER_CLASS_NAME, + || !android_media_getIntConstantFromClass(env, audioManagerClass, + JAVA_AUDIOMANAGER_CLASS_NAME, JAVA_CONST_STREAM_MUSIC_NAME, &(javaAudioTrackFields.STREAM_MUSIC)) - || !android_media_getIntConstantFromClass(env, audioManagerClass, - JAVA_AUDIOMANAGER_CLASS_NAME, + || !android_media_getIntConstantFromClass(env, audioManagerClass, + JAVA_AUDIOMANAGER_CLASS_NAME, JAVA_CONST_STREAM_SYSTEM_NAME, &(javaAudioTrackFields.STREAM_SYSTEM)) - || !android_media_getIntConstantFromClass(env, audioManagerClass, - JAVA_AUDIOMANAGER_CLASS_NAME, + || !android_media_getIntConstantFromClass(env, audioManagerClass, + JAVA_AUDIOMANAGER_CLASS_NAME, JAVA_CONST_STREAM_RING_NAME, &(javaAudioTrackFields.STREAM_RING)) || !android_media_getIntConstantFromClass(env, audioManagerClass, - JAVA_AUDIOMANAGER_CLASS_NAME, + JAVA_AUDIOMANAGER_CLASS_NAME, JAVA_CONST_STREAM_ALARM_NAME, &(javaAudioTrackFields.STREAM_ALARM)) || !android_media_getIntConstantFromClass(env, audioManagerClass, - JAVA_AUDIOMANAGER_CLASS_NAME, - JAVA_CONST_STREAM_NOTIFICATION_NAME, &(javaAudioTrackFields.STREAM_NOTIFICATION)) ) { + JAVA_AUDIOMANAGER_CLASS_NAME, + JAVA_CONST_STREAM_NOTIFICATION_NAME, &(javaAudioTrackFields.STREAM_NOTIFICATION)) + || !android_media_getIntConstantFromClass(env, audioManagerClass, + JAVA_AUDIOMANAGER_CLASS_NAME, + JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME, + &(javaAudioTrackFields.STREAM_BLUETOOTH_SCO))) { // error log performed in android_media_getIntConstantFromClass() return -1; } - + return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/core/jni/android_media_JetPlayer.cpp b/core/jni/android_media_JetPlayer.cpp index 994f161..fe60943 100644 --- a/core/jni/android_media_JetPlayer.cpp +++ b/core/jni/android_media_JetPlayer.cpp @@ -127,7 +127,7 @@ android_media_JetPlayer_release(JNIEnv *env, jobject thiz) // ---------------------------------------------------------------------------- static jboolean -android_media_JetPlayer_openFile(JNIEnv *env, jobject thiz, jstring path) +android_media_JetPlayer_loadFromFile(JNIEnv *env, jobject thiz, jstring path) { JetPlayer *lpJet = (JetPlayer *)env->GetIntField( thiz, javaJetPlayerFields.nativePlayerInJavaObj); @@ -139,7 +139,6 @@ android_media_JetPlayer_openFile(JNIEnv *env, jobject thiz, jstring path) // set up event callback function lpJet->setEventCallback(jetPlayerEventCallback); - const char *pathStr = env->GetStringUTFChars(path, NULL); if (pathStr == NULL) { // Out of memory LOGE("android_media_JetPlayer_openFile(): aborting, out of memory"); @@ -148,7 +147,7 @@ android_media_JetPlayer_openFile(JNIEnv *env, jobject thiz, jstring path) } LOGV("android_media_JetPlayer_openFile(): trying to open %s", pathStr ); - EAS_RESULT result = lpJet->openFile(pathStr); + EAS_RESULT result = lpJet->loadFromFile(pathStr); env->ReleaseStringUTFChars(path, pathStr); if(result==EAS_SUCCESS) { @@ -164,7 +163,8 @@ android_media_JetPlayer_openFile(JNIEnv *env, jobject thiz, jstring path) // ---------------------------------------------------------------------------- static jboolean -android_media_JetPlayer_closeFile(JNIEnv *env, jobject thiz) +android_media_JetPlayer_loadFromFileD(JNIEnv *env, jobject thiz, + jobject fileDescriptor, jlong offset, jlong length) { JetPlayer *lpJet = (JetPlayer *)env->GetIntField( thiz, javaJetPlayerFields.nativePlayerInJavaObj); @@ -173,6 +173,35 @@ android_media_JetPlayer_closeFile(JNIEnv *env, jobject thiz) "Unable to retrieve JetPlayer pointer for openFile()"); } + // set up event callback function + lpJet->setEventCallback(jetPlayerEventCallback); + + LOGV("android_media_JetPlayer_openFileDescr(): trying to load JET file through its fd" ); + EAS_RESULT result = lpJet->loadFromFD(getParcelFileDescriptorFD(env, fileDescriptor), + (long long)offset, (long long)length); // cast params to types used by EAS_FILE + + if(result==EAS_SUCCESS) { + LOGV("android_media_JetPlayer_openFileDescr(): file successfully opened"); + return JNI_TRUE; + } else { + LOGE("android_media_JetPlayer_openFileDescr(): failed to open file with EAS error %d", + (int)result); + return JNI_FALSE; + } +} + + +// ---------------------------------------------------------------------------- +static jboolean +android_media_JetPlayer_closeFile(JNIEnv *env, jobject thiz) +{ + JetPlayer *lpJet = (JetPlayer *)env->GetIntField( + thiz, javaJetPlayerFields.nativePlayerInJavaObj); + if (lpJet == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve JetPlayer pointer for closeFile()"); + } + if( lpJet->closeFile()==EAS_SUCCESS) { //LOGV("android_media_JetPlayer_closeFile(): file successfully closed"); return JNI_TRUE; @@ -191,7 +220,7 @@ android_media_JetPlayer_play(JNIEnv *env, jobject thiz) thiz, javaJetPlayerFields.nativePlayerInJavaObj); if (lpJet == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", - "Unable to retrieve JetPlayer pointer for openFile()"); + "Unable to retrieve JetPlayer pointer for play()"); } EAS_RESULT result = lpJet->play(); @@ -214,7 +243,7 @@ android_media_JetPlayer_pause(JNIEnv *env, jobject thiz) thiz, javaJetPlayerFields.nativePlayerInJavaObj); if (lpJet == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", - "Unable to retrieve JetPlayer pointer for openFile()"); + "Unable to retrieve JetPlayer pointer for pause()"); } EAS_RESULT result = lpJet->pause(); @@ -243,7 +272,7 @@ android_media_JetPlayer_queueSegment(JNIEnv *env, jobject thiz, thiz, javaJetPlayerFields.nativePlayerInJavaObj); if (lpJet == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", - "Unable to retrieve JetPlayer pointer for openFile()"); + "Unable to retrieve JetPlayer pointer for queueSegment()"); } EAS_RESULT result @@ -269,7 +298,7 @@ android_media_JetPlayer_queueSegmentMuteArray(JNIEnv *env, jobject thiz, thiz, javaJetPlayerFields.nativePlayerInJavaObj); if (lpJet == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", - "Unable to retrieve JetPlayer pointer for openFile()"); + "Unable to retrieve JetPlayer pointer for queueSegmentMuteArray()"); } EAS_RESULT result=EAS_FAILURE; @@ -314,7 +343,7 @@ android_media_JetPlayer_setMuteFlags(JNIEnv *env, jobject thiz, thiz, javaJetPlayerFields.nativePlayerInJavaObj); if (lpJet == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", - "Unable to retrieve JetPlayer pointer for openFile()"); + "Unable to retrieve JetPlayer pointer for setMuteFlags()"); } EAS_RESULT result; @@ -338,7 +367,7 @@ android_media_JetPlayer_setMuteArray(JNIEnv *env, jobject thiz, thiz, javaJetPlayerFields.nativePlayerInJavaObj); if (lpJet == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", - "Unable to retrieve JetPlayer pointer for openFile()"); + "Unable to retrieve JetPlayer pointer for setMuteArray()"); } EAS_RESULT result=EAS_FAILURE; @@ -367,7 +396,8 @@ android_media_JetPlayer_setMuteArray(JNIEnv *env, jobject thiz, //LOGV("android_media_JetPlayer_setMuteArray(): mute flags successfully updated"); return JNI_TRUE; } else { - LOGE("android_media_JetPlayer_setMuteArray(): failed to update mute flags with EAS error code %ld", result); + LOGE("android_media_JetPlayer_setMuteArray(): \ + failed to update mute flags with EAS error code %ld", result); return JNI_FALSE; } } @@ -382,7 +412,7 @@ android_media_JetPlayer_setMuteFlag(JNIEnv *env, jobject thiz, thiz, javaJetPlayerFields.nativePlayerInJavaObj); if (lpJet == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", - "Unable to retrieve JetPlayer pointer for openFile()"); + "Unable to retrieve JetPlayer pointer for setMuteFlag()"); } EAS_RESULT result; @@ -407,7 +437,7 @@ android_media_JetPlayer_triggerClip(JNIEnv *env, jobject thiz, jint clipId) thiz, javaJetPlayerFields.nativePlayerInJavaObj); if (lpJet == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", - "Unable to retrieve JetPlayer pointer for openFile()"); + "Unable to retrieve JetPlayer pointer for triggerClip()"); } EAS_RESULT result; @@ -424,13 +454,39 @@ android_media_JetPlayer_triggerClip(JNIEnv *env, jobject thiz, jint clipId) // ---------------------------------------------------------------------------- +static jboolean +android_media_JetPlayer_clearQueue(JNIEnv *env, jobject thiz) +{ + JetPlayer *lpJet = (JetPlayer *)env->GetIntField( + thiz, javaJetPlayerFields.nativePlayerInJavaObj); + if (lpJet == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve JetPlayer pointer for clearQueue()"); + } + + EAS_RESULT result = lpJet->clearQueue(); + if(result==EAS_SUCCESS) { + //LOGV("android_media_JetPlayer_clearQueue(): clearQueue successful"); + return JNI_TRUE; + } else { + LOGE("android_media_JetPlayer_clearQueue(): clearQueue failed with EAS error code %ld", + result); + return JNI_FALSE; + } +} + + +// ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { // name, signature, funcPtr {"native_setup", "(Ljava/lang/Object;II)Z", (void *)android_media_JetPlayer_setup}, {"native_finalize", "()V", (void *)android_media_JetPlayer_finalize}, {"native_release", "()V", (void *)android_media_JetPlayer_release}, - {"native_openJetFile", "(Ljava/lang/String;)Z", (void *)android_media_JetPlayer_openFile}, + {"native_loadJetFromFile", + "(Ljava/lang/String;)Z", (void *)android_media_JetPlayer_loadFromFile}, + {"native_loadJetFromFileD", "(Ljava/io/FileDescriptor;JJ)Z", + (void *)android_media_JetPlayer_loadFromFileD}, {"native_closeJetFile","()Z", (void *)android_media_JetPlayer_closeFile}, {"native_playJet", "()Z", (void *)android_media_JetPlayer_play}, {"native_pauseJet", "()Z", (void *)android_media_JetPlayer_pause}, @@ -442,6 +498,7 @@ static JNINativeMethod gMethods[] = { {"native_setMuteArray","([ZZ)Z", (void *)android_media_JetPlayer_setMuteArray}, {"native_setMuteFlag", "(IZZ)Z", (void *)android_media_JetPlayer_setMuteFlag}, {"native_triggerClip", "(I)Z", (void *)android_media_JetPlayer_triggerClip}, + {"native_clearQueue", "()Z", (void *)android_media_JetPlayer_clearQueue}, }; #define JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME "mNativePlayerInJavaObj" diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp index 722b5b8..c98207a 100644 --- a/core/jni/android_net_wifi_Wifi.cpp +++ b/core/jni/android_net_wifi_Wifi.cpp @@ -287,6 +287,24 @@ static jboolean android_net_wifi_stopDriverCommand(JNIEnv* env, jobject clazz) return doBooleanCommand("DRIVER STOP", "OK"); } +static jboolean android_net_wifi_startPacketFiltering(JNIEnv* env, jobject clazz) +{ + return doBooleanCommand("DRIVER RXFILTER-ADD 0", "OK") + && doBooleanCommand("DRIVER RXFILTER-ADD 1", "OK") + && doBooleanCommand("DRIVER RXFILTER-START", "OK"); +} + +static jboolean android_net_wifi_stopPacketFiltering(JNIEnv* env, jobject clazz) +{ + jboolean result = doBooleanCommand("DRIVER RXFILTER-STOP", "OK"); + if (result) { + (void)doBooleanCommand("DRIVER RXFILTER-REMOVE 1", "OK"); + (void)doBooleanCommand("DRIVER RXFILTER-REMOVE 0", "OK"); + } + + return result; +} + static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz) { char reply[256]; @@ -478,6 +496,8 @@ static JNINativeMethod gWifiMethods[] = { { "setScanModeCommand", "(Z)Z", (void*) android_net_wifi_setScanModeCommand }, { "startDriverCommand", "()Z", (void*) android_net_wifi_startDriverCommand }, { "stopDriverCommand", "()Z", (void*) android_net_wifi_stopDriverCommand }, + { "startPacketFiltering", "()Z", (void*) android_net_wifi_startPacketFiltering }, + { "stopPacketFiltering", "()Z", (void*) android_net_wifi_stopPacketFiltering }, { "setPowerModeCommand", "(I)Z", (void*) android_net_wifi_setPowerModeCommand }, { "setNumAllowedChannelsCommand", "(I)Z", (void*) android_net_wifi_setNumAllowedChannelsCommand }, { "getNumAllowedChannelsCommand", "()I", (void*) android_net_wifi_getNumAllowedChannelsCommand }, diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 6ba949c..a7a0428 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -88,93 +88,98 @@ static jlong android_os_Debug_getNativeHeapFreeSize(JNIEnv *env, jobject clazz) #endif } -static int read_mapinfo(FILE *fp, stats_t* stats) +static void read_mapinfo(FILE *fp, stats_t* stats) { char line[1024]; int len; - int skip; + bool skip, done = false; unsigned start = 0, size = 0, resident = 0, pss = 0; unsigned shared_clean = 0, shared_dirty = 0; unsigned private_clean = 0, private_dirty = 0; unsigned referenced = 0; + unsigned temp; int isNativeHeap; int isDalvikHeap; int isSqliteHeap; -again: - isNativeHeap = 0; - isDalvikHeap = 0; - isSqliteHeap = 0; - skip = 0; - - if(fgets(line, 1024, fp) == 0) return 0; + if(fgets(line, 1024, fp) == 0) return; + + while (!done) { + isNativeHeap = 0; + isDalvikHeap = 0; + isSqliteHeap = 0; + skip = false; - len = strlen(line); - if (len < 1) return 0; - line[--len] = 0; + len = strlen(line); + if (len < 1) return; + line[--len] = 0; - /* ignore guard pages */ - if (line[18] == '-') skip = 1; + /* ignore guard pages */ + if (len > 18 && line[18] == '-') skip = true; - start = strtoul(line, 0, 16); + start = strtoul(line, 0, 16); - if (len >= 50) { - if (!strcmp(line + 49, "[heap]")) { + if (strstr("[heap]", line)) { isNativeHeap = 1; - } else if (!strncmp(line + 49, "/dalvik-LinearAlloc", strlen("/dalvik-LinearAlloc"))) { + } else if (strstr("/dalvik-LinearAlloc", line)) { isDalvikHeap = 1; - } else if (!strncmp(line + 49, "/mspace/dalvik-heap", strlen("/mspace/dalvik-heap"))) { + } else if (strstr("/mspace/dalvik-heap", line)) { isDalvikHeap = 1; - } else if (!strncmp(line + 49, "/dalvik-heap-bitmap/", strlen("/dalvik-heap-bitmap/"))) { + } else if (strstr("/dalvik-heap-bitmap/", line)) { isDalvikHeap = 1; - } else if (!strncmp(line + 49, "/tmp/sqlite-heap", strlen("/tmp/sqlite-heap"))) { + } else if (strstr("/tmp/sqlite-heap", line)) { isSqliteHeap = 1; } - } - // TODO: This needs to be fixed to be less fragile. If the order of this file changes or a new - // line is add, this method will return without filling out any of the information. - - if (fgets(line, 1024, fp) == 0) return 0; - if (sscanf(line, "Size: %d kB", &size) != 1) return 0; - if (fgets(line, 1024, fp) == 0) return 0; - if (sscanf(line, "Rss: %d kB", &resident) != 1) return 0; - if (fgets(line, 1024, fp) == 0) return 0; - if (sscanf(line, "Pss: %d kB", &pss) != 1) return 0; - if (fgets(line, 1024, fp) == 0) return 0; - if (sscanf(line, "Shared_Clean: %d kB", &shared_clean) != 1) return 0; - if (fgets(line, 1024, fp) == 0) return 0; - if (sscanf(line, "Shared_Dirty: %d kB", &shared_dirty) != 1) return 0; - if (fgets(line, 1024, fp) == 0) return 0; - if (sscanf(line, "Private_Clean: %d kB", &private_clean) != 1) return 0; - if (fgets(line, 1024, fp) == 0) return 0; - if (sscanf(line, "Private_Dirty: %d kB", &private_dirty) != 1) return 0; - if (fgets(line, 1024, fp) == 0) return 0; - if (sscanf(line, "Referenced: %d kB", &referenced) != 1) return 0; - - if (skip) { - goto again; - } + while (true) { + if (fgets(line, 1024, fp) == 0) { + done = true; + break; + } + + if (sscanf(line, "Size: %d kB", &temp) == 1) { + size = temp; + } else if (sscanf(line, "Rss: %d kB", &temp) == 1) { + resident = temp; + } else if (sscanf(line, "Pss: %d kB", &temp) == 1) { + pss = temp; + } else if (sscanf(line, "Shared_Clean: %d kB", &temp) == 1) { + shared_clean = temp; + } else if (sscanf(line, "Shared_Dirty: %d kB", &temp) == 1) { + shared_dirty = temp; + } else if (sscanf(line, "Private_Clean: %d kB", &temp) == 1) { + private_clean = temp; + } else if (sscanf(line, "Private_Dirty: %d kB", &temp) == 1) { + private_dirty = temp; + } else if (sscanf(line, "Referenced: %d kB", &temp) == 1) { + referenced = temp; + } else if (strlen(line) > 40 && line[8] == '-' && line[17] == ' ') { + // looks like a new mapping + // example: "0000a000-00232000 rwxp 0000a000 00:00 0 [heap]" + break; + } + } - if (isNativeHeap) { - stats->nativePss += pss; - stats->nativePrivateDirty += private_dirty; - stats->nativeSharedDirty += shared_dirty; - } else if (isDalvikHeap) { - stats->dalvikPss += pss; - stats->dalvikPrivateDirty += private_dirty; - stats->dalvikSharedDirty += shared_dirty; - } else if (isSqliteHeap) { - // ignore - } else { - stats->otherPss += pss; - stats->otherPrivateDirty += shared_dirty; - stats->otherSharedDirty += private_dirty; + if (!skip) { + if (isNativeHeap) { + stats->nativePss += pss; + stats->nativePrivateDirty += private_dirty; + stats->nativeSharedDirty += shared_dirty; + } else if (isDalvikHeap) { + stats->dalvikPss += pss; + stats->dalvikPrivateDirty += private_dirty; + stats->dalvikSharedDirty += shared_dirty; + } else if ( isSqliteHeap) { + // ignore + } else { + stats->otherPss += pss; + stats->otherPrivateDirty += shared_dirty; + stats->otherSharedDirty += private_dirty; + } + } } - - return 1; } static void load_maps(int pid, stats_t* stats) @@ -185,10 +190,8 @@ static void load_maps(int pid, stats_t* stats) sprintf(tmp, "/proc/%d/smaps", pid); fp = fopen(tmp, "r"); if (fp == 0) return; - - while (read_mapinfo(fp, stats) != 0) { - // Do nothing - } + + read_mapinfo(fp, stats); fclose(fp); } diff --git a/core/jni/android_os_NetStat.cpp b/core/jni/android_os_NetStat.cpp deleted file mode 100644 index 983f719..0000000 --- a/core/jni/android_os_NetStat.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* //device/libs/android_runtime/android_os_Wifi.cpp -** -** 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. -*/ - -#define LOG_TAG "NetStat" - -#include "jni.h" -#include <utils/misc.h> -#include <android_runtime/AndroidRuntime.h> -#include <utils/Log.h> - -#include <stdio.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <stdlib.h> -#if HAVE_ANDROID_OS -#include <utils/Atomic.h> -#endif - -namespace android { - -static jint android_os_netStatGetTxPkts(JNIEnv* env, jobject clazz) -{ - int ret = 0; - int fd = -1; - char input[50]; - - fd = open("/sys/class/net/rmnet0/statistics/tx_packets", O_RDONLY); - if (fd <= 0) { - fd = open("/sys/class/net/ppp0/statistics/tx_packets", O_RDONLY); - } - - if (fd > 0) { - int size = read(fd, input, 50); - if (size > 0) { - ret = atoi(input); - } - close(fd); - } - - return (jint)ret; -} - -static jint android_os_netStatGetRxPkts(JNIEnv* env, jobject clazz) -{ - int ret = 0; - int fd = -1; - char input[50]; - - fd = open("/sys/class/net/rmnet0/statistics/rx_packets", O_RDONLY); - if (fd <= 0) { - fd = open("/sys/class/net/ppp0/statistics/rx_packets", O_RDONLY); - } - - if (fd > 0) { - int size = read(fd, input, 50); - if (size > 0) { - ret = atoi(input); - } - close(fd); - } - - return (jint)ret; -} - -static jint android_os_netStatGetRxBytes(JNIEnv* env, jobject clazz) -{ - int ret = 0; - int fd = -1; - char input[50]; - - fd = open("/sys/class/net/rmnet0/statistics/rx_bytes", O_RDONLY); - if (fd <= 0) { - fd = open("/sys/class/net/ppp0/statistics/rx_bytes", O_RDONLY); - } - - if (fd > 0) { - int size = read(fd, input, 50); - if (size > 0) { - ret = atoi(input); - } - close(fd); - } - - return (jint)ret; -} - - -static jint android_os_netStatGetTxBytes(JNIEnv* env, jobject clazz) -{ - int ret = 0; - int fd = -1; - char input[50]; - - fd = open("/sys/class/net/rmnet0/statistics/tx_bytes", O_RDONLY); - if (fd <= 0) { - fd = open("/sys/class/net/ppp0/statistics/tx_bytes", O_RDONLY); - } - - if (fd > 0) { - int size = read(fd, input, 50); - if (size > 0) { - ret = atoi(input); - } - close(fd); - } - - return (jint)ret; -} - -// ---------------------------------------------------------------------------- - -/* - * JNI registration. - */ -static JNINativeMethod gMethods[] = { - /* name, signature, funcPtr */ - - { "netStatGetTxPkts", "()I", - (void*) android_os_netStatGetTxPkts }, - - { "netStatGetRxPkts", "()I", - (void*) android_os_netStatGetRxPkts }, - - { "netStatGetTxBytes", "()I", - (void*) android_os_netStatGetTxBytes }, - - { "netStatGetRxBytes", "()I", - (void*) android_os_netStatGetRxBytes }, - -}; - -int register_android_os_NetStat(JNIEnv* env) -{ - jclass netStat = env->FindClass("android/os/NetStat"); - LOG_FATAL_IF(netStat == NULL, "Unable to find class android/os/NetStat"); - - return AndroidRuntime::registerNativeMethods(env, - "android/os/NetStat", gMethods, NELEM(gMethods)); -} - -}; // namespace android - diff --git a/core/jni/android_server_BluetoothDeviceService.cpp b/core/jni/android_server_BluetoothDeviceService.cpp index 61a4a26..0936310 100644 --- a/core/jni/android_server_BluetoothDeviceService.cpp +++ b/core/jni/android_server_BluetoothDeviceService.cpp @@ -428,36 +428,6 @@ static void disconnectRemoteDeviceNative(JNIEnv *env, jobject object, jstring ad #endif } -static jboolean isConnectableNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "IsConnectable", - DBUS_TYPE_INVALID); - return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; - } -#endif - return JNI_FALSE; -} - -static jboolean isDiscoverableNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "IsDiscoverable", - DBUS_TYPE_INVALID); - return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; - } -#endif - return JNI_FALSE; -} - static jstring getModeNative(JNIEnv *env, jobject object) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); @@ -495,26 +465,6 @@ static jboolean setModeNative(JNIEnv *env, jobject object, jstring mode) { return JNI_FALSE; } -static void common_Bonding(JNIEnv *env, jobject object, int timeout_ms, - const char *func, jstring address) { -#ifdef HAVE_BLUETOOTH - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - LOGV("... address = %s", c_address); - DBusMessage *reply = - dbus_func_args_timeout(env, nat->conn, timeout_ms, nat->adapter, - DBUS_CLASS_NAME, func, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - if (reply) { - dbus_message_unref(reply); - } - } -#endif -} - static jboolean createBondingNative(JNIEnv *env, jobject object, jstring address, jint timeout_ms) { LOGV(__FUNCTION__); @@ -540,15 +490,49 @@ static jboolean createBondingNative(JNIEnv *env, jobject object, return JNI_FALSE; } -static void cancelBondingProcessNative(JNIEnv *env, jobject object, +static jboolean cancelBondingProcessNative(JNIEnv *env, jobject object, jstring address) { LOGV(__FUNCTION__); - common_Bonding(env, object, -1, "CancelBondingProcess", address); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + LOGV("... address = %s", c_address); + DBusMessage *reply = + dbus_func_args_timeout(env, nat->conn, -1, nat->adapter, + DBUS_CLASS_NAME, "CancelBondingProcess", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + if (reply) { + dbus_message_unref(reply); + } + return JNI_TRUE; + } +#endif + return JNI_FALSE; } -static void removeBondingNative(JNIEnv *env, jobject object, jstring address) { +static jboolean removeBondingNative(JNIEnv *env, jobject object, jstring address) { LOGV(__FUNCTION__); - common_Bonding(env, object, -1, "RemoveBonding", address); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + LOGV("... address = %s", c_address); + DBusMessage *reply = + dbus_func_args_timeout(env, nat->conn, -1, nat->adapter, + DBUS_CLASS_NAME, "RemoveBonding", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + if (reply) { + dbus_message_unref(reply); + } + return JNI_TRUE; + } +#endif + return JNI_FALSE; } static jobjectArray listBondingsNative(JNIEnv *env, jobject object) { @@ -660,14 +644,6 @@ static jboolean setNameNative(JNIEnv *env, jobject obj, jstring name) { return JNI_FALSE; } -static jstring getMajorClassNative(JNIEnv *env, jobject obj) { - return common_Get(env, obj, "GetMajorClass"); -} - -static jstring getMinorClassNative(JNIEnv *env, jobject obj) { - return common_Get(env, obj, "GetMinorClass"); -} - static jstring common_getRemote(JNIEnv *env, jobject object, const char *func, jstring address) { LOGV("%s:%s", __FUNCTION__, func); @@ -704,66 +680,6 @@ static jstring common_getRemote(JNIEnv *env, jobject object, const char *func, return NULL; } -static jstring getRemoteAliasNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "GetRemoteAlias", address); -} - -static jboolean setRemoteAliasNative(JNIEnv *env, jobject obj, - jstring address, jstring alias) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, obj); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - const char *c_alias = env->GetStringUTFChars(alias, NULL); - - LOGV("... address = %s alias = %s", c_address, c_alias); - - DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "SetRemoteAlias", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_STRING, &c_alias, - DBUS_TYPE_INVALID); - - env->ReleaseStringUTFChars(address, c_address); - env->ReleaseStringUTFChars(alias, c_alias); - if (reply) - { - dbus_message_unref(reply); - return JNI_TRUE; - } - return JNI_FALSE; - } -#endif - return JNI_FALSE; -} - -static jboolean clearRemoteAliasNative(JNIEnv *env, jobject obj, jstring address) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, obj); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - - LOGV("... address = %s", c_address); - - DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "ClearRemoteAlias", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - - env->ReleaseStringUTFChars(address, c_address); - if (reply) - { - dbus_message_unref(reply); - return JNI_TRUE; - } - return JNI_FALSE; - } -#endif - return JNI_FALSE; -} - static jstring getRemoteVersionNative(JNIEnv *env, jobject obj, jstring address) { return common_getRemote(env, obj, "GetRemoteVersion", address); } @@ -780,14 +696,6 @@ static jstring getRemoteCompanyNative(JNIEnv *env, jobject obj, jstring address) return common_getRemote(env, obj, "GetRemoteCompany", address); } -static jstring getRemoteMajorClassNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "GetRemoteMajorClass", address); -} - -static jstring getRemoteMinorClassNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "GetRemoteMinorClass", address); -} - static jstring getRemoteNameNative(JNIEnv *env, jobject obj, jstring address) { return common_getRemote(env, obj, "GetRemoteName", address); } @@ -800,28 +708,6 @@ static jstring lastUsedNative(JNIEnv *env, jobject obj, jstring address) { return common_getRemote(env, obj, "LastUsed", address); } -static jobjectArray getRemoteServiceClassesNative(JNIEnv *env, jobject object, - jstring address) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - - LOGV("... address = %s", c_address); - - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "GetRemoteServiceClasses", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - return reply ? dbus_returns_array_of_strings(env, reply) : NULL; - } -#endif - return NULL; -} - static jint getRemoteClassNative(JNIEnv *env, jobject object, jstring address) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); @@ -1070,8 +956,6 @@ static JNINativeMethod sMethods[] = { {"getAddressNative", "()Ljava/lang/String;", (void *)getAddressNative}, {"getNameNative", "()Ljava/lang/String;", (void*)getNameNative}, {"setNameNative", "(Ljava/lang/String;)Z", (void *)setNameNative}, - {"getMajorClassNative", "()Ljava/lang/String;", (void *)getMajorClassNative}, - {"getMinorClassNative", "()Ljava/lang/String;", (void *)getMinorClassNative}, {"getVersionNative", "()Ljava/lang/String;", (void *)getVersionNative}, {"getRevisionNative", "()Ljava/lang/String;", (void *)getRevisionNative}, {"getManufacturerNative", "()Ljava/lang/String;", (void *)getManufacturerNative}, @@ -1100,17 +984,11 @@ static JNINativeMethod sMethods[] = { {"removeBondingNative", "(Ljava/lang/String;)Z", (void *)removeBondingNative}, {"getRemoteNameNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteNameNative}, - {"getRemoteAliasNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteAliasNative}, - {"setRemoteAliasNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)setRemoteAliasNative}, - {"clearRemoteAliasNative", "(Ljava/lang/String;)Z", (void *)clearRemoteAliasNative}, {"getRemoteVersionNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteVersionNative}, {"getRemoteRevisionNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteRevisionNative}, {"getRemoteClassNative", "(Ljava/lang/String;)I", (void *)getRemoteClassNative}, {"getRemoteManufacturerNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteManufacturerNative}, {"getRemoteCompanyNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteCompanyNative}, - {"getRemoteMajorClassNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteMajorClassNative}, - {"getRemoteMinorClassNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteMinorClassNative}, - {"getRemoteServiceClassesNative", "(Ljava/lang/String;)[Ljava/lang/String;", (void *)getRemoteServiceClassesNative}, {"getRemoteServiceChannelNative", "(Ljava/lang/String;S)Z", (void *)getRemoteServiceChannelNative}, {"getRemoteFeaturesNative", "(Ljava/lang/String;)[B", (void *)getRemoteFeaturesNative}, {"lastSeenNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)lastSeenNative}, diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index 3468265..75a0fbe 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -47,8 +47,6 @@ static jmethodID method_onRemoteDeviceDisappeared; static jmethodID method_onRemoteClassUpdated; static jmethodID method_onRemoteNameUpdated; static jmethodID method_onRemoteNameFailed; -static jmethodID method_onRemoteAliasChanged; -static jmethodID method_onRemoteAliasCleared; static jmethodID method_onRemoteDeviceConnected; static jmethodID method_onRemoteDeviceDisconnectRequested; static jmethodID method_onRemoteDeviceDisconnected; @@ -60,6 +58,8 @@ static jmethodID method_onGetRemoteServiceChannelResult; static jmethodID method_onPasskeyAgentRequest; static jmethodID method_onPasskeyAgentCancel; +static jmethodID method_onAuthAgentAuthorize; +static jmethodID method_onAuthAgentCancel; typedef event_loop_native_data_t native_data_t; @@ -85,7 +85,6 @@ static void classInitNative(JNIEnv* env, jclass clazz) { method_onRemoteClassUpdated = env->GetMethodID(clazz, "onRemoteClassUpdated", "(Ljava/lang/String;I)V"); method_onRemoteNameUpdated = env->GetMethodID(clazz, "onRemoteNameUpdated", "(Ljava/lang/String;Ljava/lang/String;)V"); method_onRemoteNameFailed = env->GetMethodID(clazz, "onRemoteNameFailed", "(Ljava/lang/String;)V"); - method_onRemoteAliasChanged = env->GetMethodID(clazz, "onRemoteAliasChanged", "(Ljava/lang/String;Ljava/lang/String;)V"); method_onRemoteDeviceConnected = env->GetMethodID(clazz, "onRemoteDeviceConnected", "(Ljava/lang/String;)V"); method_onRemoteDeviceDisconnectRequested = env->GetMethodID(clazz, "onRemoteDeviceDisconnectRequested", "(Ljava/lang/String;)V"); method_onRemoteDeviceDisconnected = env->GetMethodID(clazz, "onRemoteDeviceDisconnected", "(Ljava/lang/String;)V"); @@ -96,6 +95,8 @@ static void classInitNative(JNIEnv* env, jclass clazz) { method_onPasskeyAgentRequest = env->GetMethodID(clazz, "onPasskeyAgentRequest", "(Ljava/lang/String;I)V"); method_onPasskeyAgentCancel = env->GetMethodID(clazz, "onPasskeyAgentCancel", "(Ljava/lang/String;)V"); + method_onAuthAgentAuthorize = env->GetMethodID(clazz, "onAuthAgentAuthorize", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z"); + method_onAuthAgentCancel = env->GetMethodID(clazz, "onAuthAgentCancel", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); method_onGetRemoteServiceChannelResult = env->GetMethodID(clazz, "onGetRemoteServiceChannelResult", "(Ljava/lang/String;I)V"); field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I"); @@ -139,12 +140,12 @@ static void cleanupNativeDataNative(JNIEnv* env, jobject object) { #ifdef HAVE_BLUETOOTH static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, void *data); -static DBusHandlerResult passkey_agent_event_filter(DBusConnection *conn, - DBusMessage *msg, - void *data); +static DBusHandlerResult agent_event_filter(DBusConnection *conn, + DBusMessage *msg, + void *data); -static const DBusObjectPathVTable passkey_agent_vtable = { - NULL, passkey_agent_event_filter, NULL, NULL, NULL, NULL +static const DBusObjectPathVTable agent_vtable = { + NULL, agent_event_filter, NULL, NULL, NULL, NULL }; #endif @@ -193,9 +194,9 @@ static jboolean setUpEventLoopNative(JNIEnv *env, jobject object) { } // Add an object handler for passkey agent method calls - const char *path = "/android/bluetooth/PasskeyAgent"; + const char *path = "/android/bluetooth/Agent"; if (!dbus_connection_register_object_path(nat->conn, path, - &passkey_agent_vtable, NULL)) { + &agent_vtable, NULL)) { LOGE("%s: Can't register object path %s for agent!", __FUNCTION__, path); return JNI_FALSE; @@ -204,7 +205,7 @@ static jboolean setUpEventLoopNative(JNIEnv *env, jobject object) { // RegisterDefaultPasskeyAgent() will fail until hcid is up, so keep // trying for 10 seconds. int attempt; - for (attempt = 1000; attempt > 0; attempt--) { + for (attempt = 0; attempt < 1000; attempt++) { DBusMessage *reply = dbus_func_args_error(env, nat->conn, &err, BLUEZ_DBUS_BASE_PATH, "org.bluez.Security", "RegisterDefaultPasskeyAgent", @@ -213,7 +214,8 @@ static jboolean setUpEventLoopNative(JNIEnv *env, jobject object) { if (reply) { // Success dbus_message_unref(reply); - return JNI_TRUE; + LOGV("Registered agent on attempt %d of 1000\n", attempt); + break; } else if (dbus_error_has_name(&err, "org.freedesktop.DBus.Error.ServiceUnknown")) { // hcid is still down, retry @@ -225,9 +227,25 @@ static jboolean setUpEventLoopNative(JNIEnv *env, jobject object) { return JNI_FALSE; } } - LOGE("Time-out trying to call RegisterDefaultPasskeyAgent(), " - "is hcid running?"); - return JNI_FALSE; + if (attempt == 1000) { + LOGE("Time-out trying to call RegisterDefaultPasskeyAgent(), " + "is hcid running?"); + return JNI_FALSE; + } + + // Now register the Auth agent + DBusMessage *reply = dbus_func_args_error(env, nat->conn, &err, + BLUEZ_DBUS_BASE_PATH, + "org.bluez.Security", "RegisterDefaultAuthorizationAgent", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + if (!reply) { + LOG_AND_FREE_DBUS_ERROR(&err); + return JNI_FALSE; + } + + dbus_message_unref(reply); + return JNI_TRUE; } #endif @@ -243,12 +261,19 @@ static void tearDownEventLoopNative(JNIEnv *env, jobject object) { DBusError err; dbus_error_init(&err); - const char *path = "/android/bluetooth/PasskeyAgent"; + const char *path = "/android/bluetooth/Agent"; DBusMessage *reply = dbus_func_args(env, nat->conn, BLUEZ_DBUS_BASE_PATH, - "org.bluez.Security", "UnregisterDefaultPasskeyAgent", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); + "org.bluez.Security", "UnregisterDefaultPasskeyAgent", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + if (reply) dbus_message_unref(reply); + + reply = + dbus_func_args(env, nat->conn, BLUEZ_DBUS_BASE_PATH, + "org.bluez.Security", "UnregisterDefaultAuthorizationAgent", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); if (reply) dbus_message_unref(reply); dbus_connection_unregister_object_path(nat->conn, path); @@ -395,34 +420,6 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", - "RemoteAliasChanged")) { - char *c_address, *c_alias; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_STRING, &c_alias, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s, alias = %s", c_address, c_alias); - env->CallVoidMethod(nat->me, - method_onRemoteAliasChanged, - env->NewStringUTF(c_address), - env->NewStringUTF(c_alias)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteAliasCleared")) { - char *c_address; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, - method_onRemoteAliasCleared, - env->NewStringUTF(c_address)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", "RemoteDeviceConnected")) { char *c_address; if (dbus_message_get_args(msg, &err, @@ -518,9 +515,8 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, } // Called by dbus during WaitForAndDispatchEventNative() -static DBusHandlerResult passkey_agent_event_filter(DBusConnection *conn, - DBusMessage *msg, - void *data) { +static DBusHandlerResult agent_event_filter(DBusConnection *conn, + DBusMessage *msg, void *data) { native_data_t *nat = event_loop_nat; JNIEnv *env; @@ -573,11 +569,120 @@ static DBusHandlerResult passkey_agent_event_filter(DBusConnection *conn, env->CallVoidMethod(nat->me, method_onPasskeyAgentCancel, env->NewStringUTF(address)); + // reply + DBusMessage *reply = dbus_message_new_method_return(msg); + if (!reply) { + LOGE("%s: Cannot create message reply\n", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_method_call(msg, "org.bluez.PasskeyAgent", "Release")) { - LOGE("We are no longer the passkey agent!"); + LOGW("We are no longer the passkey agent!"); + + // reply + DBusMessage *reply = dbus_message_new_method_return(msg); + if (!reply) { + LOGE("%s: Cannot create message reply\n", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; + } else if (dbus_message_is_method_call(msg, + "org.bluez.AuthorizationAgent", "Authorize")) { + const char *adapter; + const char *address; + const char *service; + const char *uuid; + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &adapter, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_STRING, &service, + DBUS_TYPE_STRING, &uuid, + DBUS_TYPE_INVALID)) { + LOGE("%s: Invalid arguments for Authorize() method", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + LOGV("... address = %s", address); + LOGV("... service = %s", service); + LOGV("... uuid = %s", uuid); + + bool auth_granted = env->CallBooleanMethod(nat->me, + method_onAuthAgentAuthorize, env->NewStringUTF(address), + env->NewStringUTF(service), env->NewStringUTF(uuid)); + + // reply + if (auth_granted) { + DBusMessage *reply = dbus_message_new_method_return(msg); + if (!reply) { + LOGE("%s: Cannot create message reply\n", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(reply); + } else { + DBusMessage *reply = dbus_message_new_error(msg, + "org.bluez.Error.Rejected", "Authorization rejected"); + if (!reply) { + LOGE("%s: Cannot create message reply\n", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(reply); + } + return DBUS_HANDLER_RESULT_HANDLED; + } else if (dbus_message_is_method_call(msg, + "org.bluez.AuthorizationAgent", "Cancel")) { + const char *adapter; + const char *address; + const char *service; + const char *uuid; + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &adapter, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_STRING, &service, + DBUS_TYPE_STRING, &uuid, + DBUS_TYPE_INVALID)) { + LOGE("%s: Invalid arguments for Cancel() method", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + LOGV("... address = %s", address); + LOGV("... service = %s", service); + LOGV("... uuid = %s", uuid); + + env->CallVoidMethod(nat->me, + method_onAuthAgentCancel, env->NewStringUTF(address), + env->NewStringUTF(service), env->NewStringUTF(uuid)); + + // reply + DBusMessage *reply = dbus_message_new_method_return(msg); + if (!reply) { + LOGE("%s: Cannot create message reply\n", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; + + } else if (dbus_message_is_method_call(msg, + "org.bluez.AuthorizationAgent", "Release")) { + LOGW("We are no longer the auth agent!"); + + // reply + DBusMessage *reply = dbus_message_new_method_return(msg); + if (!reply) { + LOGE("%s: Cannot create message reply\n", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; } else { LOGV("... ignored"); } @@ -614,7 +719,8 @@ static jboolean waitForAndDispatchEventNative(JNIEnv *env, jobject object, #define BOND_RESULT_SUCCESS 0 #define BOND_RESULT_AUTH_FAILED 1 #define BOND_RESULT_AUTH_REJECTED 2 -#define BOND_RESULT_REMOTE_DEVICE_DOWN 3 +#define BOND_RESULT_AUTH_CANCELED 3 +#define BOND_RESULT_REMOTE_DEVICE_DOWN 4 void onCreateBondingResult(DBusMessage *msg, void *user) { LOGV(__FUNCTION__); @@ -637,7 +743,11 @@ void onCreateBondingResult(DBusMessage *msg, void *user) { // happens if either side presses 'cancel' at the pairing dialog. LOGV("... error = %s (%s)\n", err.name, err.message); result = BOND_RESULT_AUTH_REJECTED; - } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".ConnectionAttemptFailed")) { + } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationCanceled")) { + // Not sure if this happens + LOGV("... error = %s (%s)\n", err.name, err.message); + result = BOND_RESULT_AUTH_CANCELED; + } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.ConnectionAttemptFailed")) { // Other device is not responding at all LOGV("... error = %s (%s)\n", err.name, err.message); result = BOND_RESULT_REMOTE_DEVICE_DOWN; diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index add1080..d147bcc 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -44,6 +44,7 @@ static struct typedvalue_offsets_t jfieldID mAssetCookie; jfieldID mResourceId; jfieldID mChangingConfigurations; + jfieldID mDensity; } gTypedValueOffsets; static struct assetfiledescriptor_offsets_t @@ -83,7 +84,11 @@ enum { static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, const Res_value& value, uint32_t ref, ssize_t block, - uint32_t typeSpecFlags) + uint32_t typeSpecFlags, ResTable_config* config = NULL); + +jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, + const Res_value& value, uint32_t ref, ssize_t block, + uint32_t typeSpecFlags, ResTable_config* config) { env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType); env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie, @@ -93,6 +98,9 @@ static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref); env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations, typeSpecFlags); + if (config != NULL) { + env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density); + } return block; } @@ -703,13 +711,14 @@ static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject const ResTable& res(am->getResources()); Res_value value; + ResTable_config config; uint32_t typeSpecFlags; - ssize_t block = res.getResource(ident, &value, false, &typeSpecFlags); + ssize_t block = res.getResource(ident, &value, false, &typeSpecFlags, &config); uint32_t ref = ident; if (resolve) { block = res.resolveReference(&value, block, &ref); } - return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block; + return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config) : block; } static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz, @@ -1648,6 +1657,8 @@ int register_android_content_AssetManager(JNIEnv* env) gTypedValueOffsets.mChangingConfigurations = env->GetFieldID(typedValue, "changingConfigurations", "I"); LOG_FATAL_IF(gTypedValueOffsets.mChangingConfigurations == NULL, "Unable to find TypedValue.changingConfigurations"); + gTypedValueOffsets.mDensity = env->GetFieldID(typedValue, "density", "I"); + LOG_FATAL_IF(gTypedValueOffsets.mDensity == NULL, "Unable to find TypedValue.density"); jclass assetFd = env->FindClass("android/content/res/AssetFileDescriptor"); LOG_FATAL_IF(assetFd == NULL, "Unable to find class android/content/res/AssetFileDescriptor"); diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp index 8bacc74..fbbd852 100644 --- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp +++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp @@ -18,7 +18,7 @@ #include <android_runtime/AndroidRuntime.h> #include <utils/misc.h> -#include <GLES/egl.h> +#include <EGL/egl.h> #include <GLES/gl.h> #include <ui/EGLNativeWindowSurface.h> diff --git a/core/jni/server/com_android_server_HardwareService.cpp b/core/jni/server/com_android_server_HardwareService.cpp index 224ab18..ac36348 100644 --- a/core/jni/server/com_android_server_HardwareService.cpp +++ b/core/jni/server/com_android_server_HardwareService.cpp @@ -28,21 +28,21 @@ namespace android { -static void on(JNIEnv *env, jobject clazz) +static void vibratorOn(JNIEnv *env, jobject clazz, jlong timeout_ms) { - // LOGI("on\n"); - vibrator_on(); + // LOGI("vibratorOn\n"); + vibrator_on(timeout_ms); } -static void off(JNIEnv *env, jobject clazz) +static void vibratorOff(JNIEnv *env, jobject clazz) { - // LOGI("off\n"); + // LOGI("vibratorOff\n"); vibrator_off(); } static JNINativeMethod method_table[] = { - { "on", "()V", (void*)on }, - { "off", "()V", (void*)off } + { "vibratorOn", "(J)V", (void*)vibratorOn }, + { "vibratorOff", "()V", (void*)vibratorOff } }; int register_android_os_Vibrator(JNIEnv *env) diff --git a/core/jni/server/com_android_server_KeyInputQueue.cpp b/core/jni/server/com_android_server_KeyInputQueue.cpp index 4e9ffb1..63830d5 100644 --- a/core/jni/server/com_android_server_KeyInputQueue.cpp +++ b/core/jni/server/com_android_server_KeyInputQueue.cpp @@ -205,6 +205,25 @@ android_server_KeyInputQueue_getKeycodeStateDevice(JNIEnv* env, jobject clazz, return st; } +static jboolean +android_server_KeyInputQueue_hasKeys(JNIEnv* env, jobject clazz, + jintArray keyCodes, jbooleanArray outFlags) +{ + jboolean ret = JNI_FALSE; + + int32_t* codes = env->GetIntArrayElements(keyCodes, NULL); + uint8_t* flags = env->GetBooleanArrayElements(outFlags, NULL); + size_t numCodes = env->GetArrayLength(keyCodes); + if (numCodes == env->GetArrayLength(outFlags)) { + gLock.lock(); + if (gHub != NULL) ret = gHub->hasKeys(numCodes, codes, flags); + gLock.unlock(); + } + + env->ReleaseBooleanArrayElements(outFlags, flags, 0); + env->ReleaseIntArrayElements(keyCodes, codes, 0); + return ret; +} // ---------------------------------------------------------------------------- @@ -233,6 +252,8 @@ static JNINativeMethod gInputMethods[] = { (void*) android_server_KeyInputQueue_getKeycodeState }, { "getKeycodeState", "(II)I", (void*) android_server_KeyInputQueue_getKeycodeStateDevice }, + { "hasKeys", "([I[Z)Z", + (void*) android_server_KeyInputQueue_hasKeys }, }; int register_android_server_KeyInputQueue(JNIEnv* env) diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ded909f..0e8d3fd 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -549,6 +549,13 @@ android:label="@string/permlab_mount_unmount_filesystems" android:description="@string/permdesc_mount_unmount_filesystems" /> + <!-- Allows formatting file systems for removable storage. --> + <permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS" + android:permissionGroup="android.permission-group.SYSTEM_TOOLS" + android:protectionLevel="dangerous" + android:label="@string/permlab_mount_format_filesystems" + android:description="@string/permdesc_mount_format_filesystems" /> + <!-- Allows applications to disable the keyguard --> <permission android:name="android.permission.DISABLE_KEYGUARD" android:permissionGroup="android.permission-group.SYSTEM_TOOLS" @@ -725,8 +732,9 @@ android:description="@string/permdesc_fotaUpdate" android:protectionLevel="signature" /> - <!-- Allows an application to update the collected battery statistics --> - <permission android:name="android.permission.BATTERY_STATS" + <!-- Allows an application to update device statistics. Not for + use by third party apps. --> + <permission android:name="android.permission.UPDATE_DEVICE_STATS" android:label="@string/permlab_batteryStats" android:description="@string/permdesc_batteryStats" android:protectionLevel="signature" /> @@ -902,6 +910,39 @@ android:description="@string/permdesc_checkinProperties" android:protectionLevel="signature" /> + <!-- Allows an application to collect component usage + statistics --> + <permission android:name="android.permission.PACKAGE_USAGE_STATS" + android:label="@string/permlab_pkgUsageStats" + android:description="@string/permdesc_pkgUsageStats" + android:protectionLevel="normal" /> + + <!-- Allows an application to collect battery statistics --> + <permission android:name="android.permission.BATTERY_STATS" + android:label="@string/permlab_batteryStats" + android:description="@string/permdesc_batteryStats" + android:protectionLevel="normal" /> + + <!-- Allows an application to tell the gadget service which application + can access gadget's data. The normal user flow is that a user + picks a gadget to go into a particular host, thereby giving that + host application access to the private data from the gadget app. + An application that has this permission should honor that contract. + Very few applications should need to use this permission. --> + <permission android:name="android.permission.BIND_GADGET" + android:permissionGroup="android.permission-group.PERSONAL_INFO" + android:label="@string/permlab_bindGadget" + android:description="@string/permdesc_bindGadget" + android:protectionLevel="signature" /> + + <!-- Allows applications to change the background data setting + @hide pending API council --> + <permission android:name="android.permission.CHANGE_BACKGROUND_DATA_SETTING" + android:permissionGroup="android.permission-group.SYSTEM_TOOLS" + android:protectionLevel="signature" + android:description="@string/permdesc_changeBackgroundDataSetting" + android:label="@string/permlab_changeBackgroundDataSetting" /> + <application android:process="system" android:persistent="true" android:hasCode="false" @@ -930,43 +971,27 @@ android:theme="@style/Theme.Dialog.Alert" android:excludeFromRecents="true"> </activity> - <provider android:name=".server.checkin.CheckinProvider" - android:authorities="android.server.checkin" - android:multiprocess="false" /> + <activity android:name="com.android.internal.app.UsbStorageStopActivity" + android:theme="@style/Theme.Dialog.Alert" + android:excludeFromRecents="true"> + </activity> + <activity android:name="com.android.internal.app.ExternalMediaFormatActivity" + android:theme="@style/Theme.Dialog.Alert" + android:excludeFromRecents="true"> + </activity> + <provider android:name=".content.SyncProvider" android:authorities="sync" android:multiprocess="false" /> <service android:name="com.android.server.LoadAverageService" android:exported="true" /> - <receiver - android:name=".server.checkin.UpdateReceiver$DownloadCompletedBroadcastReceiver" - android:exported="true" /> - - <receiver android:name="com.google.android.server.checkin.SecretCodeReceiver"> - <intent-filter> - <action android:name="android.provider.Telephony.SECRET_CODE" /> - <data android:scheme="android_secret_code" - android:host="682" /> <!-- 682 = "OTA" on dialpad --> - <data android:scheme="android_secret_code" - android:host="682364" /> <!-- 682364 = "OTAENG" --> - <data android:scheme="android_secret_code" - android:host="682226" /> <!-- 682226 = "OTACAN" --> - <data android:scheme="android_secret_code" - android:host="682668" /> <!-- 682668 = "OTANOT" --> - </intent-filter> - </receiver> <receiver android:name="com.android.server.BootReceiver" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> - <receiver android:name="com.android.server.BrickReceiver" - android:permission="android.permission.BRICK" > - <intent-filter> - <action android:name="android.intent.action.BRICK" /> - </intent-filter> - </receiver> + <receiver android:name="com.android.server.MasterClearReceiver" android:permission="android.permission.MASTER_CLEAR" > <intent-filter> diff --git a/core/res/res/anim/dialog_enter.xml b/core/res/res/anim/dialog_enter.xml index fd08cd0..f48dd37 100644 --- a/core/res/res/anim/dialog_enter.xml +++ b/core/res/res/anim/dialog_enter.xml @@ -20,10 +20,10 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/decelerate_interpolator"> - <scale android:fromXScale="0.5" android:toXScale="1.0" - android:fromYScale="0.5" android:toYScale="1.0" + <scale android:fromXScale="0.9" android:toXScale="1.0" + android:fromYScale="0.9" android:toYScale="1.0" android:pivotX="50%" android:pivotY="50%" - android:duration="200" /> + android:duration="75" /> <alpha android:fromAlpha="0.0" android:toAlpha="1.0" - android:duration="200" /> + android:duration="75" /> </set> diff --git a/core/res/res/anim/dialog_exit.xml b/core/res/res/anim/dialog_exit.xml index e1b7a77..24de6e7 100644 --- a/core/res/res/anim/dialog_exit.xml +++ b/core/res/res/anim/dialog_exit.xml @@ -19,10 +19,10 @@ --> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/accelerate_interpolator"> - <scale android:fromXScale="1.0" android:toXScale="0.5" - android:fromYScale="1.0" android:toYScale="0.5" + <scale android:fromXScale="1.0" android:toXScale="0.9" + android:fromYScale="1.0" android:toYScale="0.9" android:pivotX="50%" android:pivotY="50%" - android:duration="200" /> + android:duration="75" /> <alpha android:fromAlpha="1.0" android:toAlpha="0.0" - android:duration="200"/> + android:duration="75"/> </set> diff --git a/core/res/res/anim/input_method_enter.xml b/core/res/res/anim/input_method_enter.xml index 572a266..6263c37 100644 --- a/core/res/res/anim/input_method_enter.xml +++ b/core/res/res/anim/input_method_enter.xml @@ -20,12 +20,6 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/decelerate_interpolator"> - <!-- - <scale android:fromXScale="2.0" android:toXScale="1.0" - android:fromYScale="2.0" android:toYScale="1.0" - android:pivotX="50%" android:pivotY="50%" - android:duration="100" /> - --> <translate android:fromYDelta="20%" android:toYDelta="0" android:duration="100"/> <alpha android:fromAlpha="0.5" android:toAlpha="1.0" android:duration="100" /> diff --git a/core/res/res/anim/input_method_exit.xml b/core/res/res/anim/input_method_exit.xml index d28313a..af9382c 100644 --- a/core/res/res/anim/input_method_exit.xml +++ b/core/res/res/anim/input_method_exit.xml @@ -19,12 +19,6 @@ --> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/accelerate_interpolator"> - <!-- - <scale android:fromXScale="1.0" android:toXScale="2.0" - android:fromYScale="1.0" android:toYScale="2.0" - android:pivotX="50%" android:pivotY="50%" - android:duration="100" /> - --> <translate android:fromYDelta="0" android:toYDelta="20%" android:duration="100"/> <alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="100"/> diff --git a/core/res/res/drawable/checkbox.xml b/core/res/res/anim/input_method_fancy_enter.xml index 7a22185..15f5ad7 100644 --- a/core/res/res/drawable/checkbox.xml +++ b/core/res/res/anim/input_method_fancy_enter.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -/* //device/apps/common/res/drawable/checkbox.xml +/* //device/apps/common/res/anim/fade_in.xml ** ** Copyright 2007, The Android Open Source Project ** @@ -18,12 +18,11 @@ */ --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_checked="true" android:state_focused="true" - android:drawable="@drawable/checkbox_on_background_focus_yellow" /> - <item android:state_checked="false" android:state_focused="true" - android:drawable="@drawable/checkbox_off_background_focus_yellow" /> - <item android:state_checked="false" android:drawable="@drawable/checkbox_off_background" /> - <item android:state_checked="true" android:drawable="@drawable/checkbox_on_background" /> -</selector> - +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:interpolator="@anim/decelerate_interpolator"> + <scale android:fromXScale="2.0" android:toXScale="1.0" + android:fromYScale="2.0" android:toYScale="1.0" + android:pivotX="50%" android:pivotY="50%" + android:duration="100" /> + <translate android:fromYDelta="100%" android:toYDelta="0" android:duration="100"/> +</set> diff --git a/core/res/res/drawable/radiobutton.xml b/core/res/res/anim/input_method_fancy_exit.xml index a72c825..ecc5de3 100644 --- a/core/res/res/drawable/radiobutton.xml +++ b/core/res/res/anim/input_method_fancy_exit.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -/* //device/apps/common/res/drawable/radiobutton.xml +/* //device/apps/common/res/anim/fade_out.xml ** ** Copyright 2007, The Android Open Source Project ** @@ -17,12 +17,11 @@ ** limitations under the License. */ --> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_checked="false" android:state_focused="true" - android:drawable="@drawable/radiobutton_off_background_focus_yellow" /> - <item android:state_checked="true" android:state_focused="true" - android:drawable="@drawable/radiobutton_on_background_focus_yellow" /> - <item android:state_checked="false" android:drawable="@drawable/radiobutton_off_background" /> - <item android:state_checked="true" android:drawable="@drawable/radiobutton_on_background" /> -</selector> +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:interpolator="@anim/accelerate_interpolator"> + <scale android:fromXScale="1.0" android:toXScale="2.0" + android:fromYScale="1.0" android:toYScale="2.0" + android:pivotX="50%" android:pivotY="50%" + android:duration="100" /> + <translate android:fromYDelta="0" android:toYDelta="100%" android:duration="100"/> +</set> diff --git a/core/res/res/drawable/radiobutton_background.xml b/core/res/res/anim/lock_screen_exit.xml index 948a141..54d8e93 100644 --- a/core/res/res/drawable/radiobutton_background.xml +++ b/core/res/res/anim/lock_screen_exit.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -/* //device/apps/common/res/drawable/radiobutton_background.xml +/* //device/apps/common/res/anim/options_panel_exit.xml ** ** Copyright 2007, The Android Open Source Project ** @@ -18,6 +18,6 @@ */ --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:drawable="@drawable/radiobutton_label_background" /> -</selector> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/accelerate_interpolator"> + <alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="200" /> +</set> diff --git a/core/res/res/anim/status_bar_enter.xml b/core/res/res/anim/status_bar_enter.xml index 2df1af4..b57d8e7 100644 --- a/core/res/res/anim/status_bar_enter.xml +++ b/core/res/res/anim/status_bar_enter.xml @@ -19,6 +19,8 @@ --> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/decelerate_interpolator"> - <translate android:fromYDelta="-100%" android:toYDelta="0" android:duration="400"/> - <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="400" /> + <translate android:fromYDelta="-75%" android:toYDelta="0" + android:duration="200"/> + <alpha android:fromAlpha="0.0" android:toAlpha="1.0" + android:duration="200" /> </set> diff --git a/core/res/res/anim/status_bar_exit.xml b/core/res/res/anim/status_bar_exit.xml index c72d014..8c6dc1c 100644 --- a/core/res/res/anim/status_bar_exit.xml +++ b/core/res/res/anim/status_bar_exit.xml @@ -19,6 +19,8 @@ --> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/accelerate_interpolator"> - <translate android:fromYDelta="0" android:toYDelta="-100%" android:duration="400"/> - <alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="400" /> + <translate android:fromYDelta="0" android:toYDelta="-75%" + android:startOffset="100" android:duration="200"/> + <alpha android:fromAlpha="1.0" android:toAlpha="0.0" + android:startOffset="100" android:duration="200" /> </set> diff --git a/core/res/res/drawable-land/statusbar_background.png b/core/res/res/drawable-land/statusbar_background.png Binary files differindex 9e82f75..8ecc24c 100644 --- a/core/res/res/drawable-land/statusbar_background.png +++ b/core/res/res/drawable-land/statusbar_background.png diff --git a/core/res/res/drawable-land/title_bar.9.png b/core/res/res/drawable-land/title_bar.9.png Binary files differnew file mode 100644 index 0000000..63ad916 --- /dev/null +++ b/core/res/res/drawable-land/title_bar.9.png diff --git a/core/res/res/drawable-land/title_bar_tall.png b/core/res/res/drawable-land/title_bar_tall.png Binary files differnew file mode 100644 index 0000000..670decc --- /dev/null +++ b/core/res/res/drawable-land/title_bar_tall.png diff --git a/core/res/res/drawable/btn_check_off.png b/core/res/res/drawable/btn_check_off.png Binary files differindex 56d3861..3a137d9 100644 --- a/core/res/res/drawable/btn_check_off.png +++ b/core/res/res/drawable/btn_check_off.png diff --git a/core/res/res/drawable/btn_check_off_disable.png b/core/res/res/drawable/btn_check_off_disable.png Binary files differindex 6c065bd..e012afd 100644 --- a/core/res/res/drawable/btn_check_off_disable.png +++ b/core/res/res/drawable/btn_check_off_disable.png diff --git a/core/res/res/drawable/btn_check_off_disable_focused.png b/core/res/res/drawable/btn_check_off_disable_focused.png Binary files differindex cf23690..47d225d 100644 --- a/core/res/res/drawable/btn_check_off_disable_focused.png +++ b/core/res/res/drawable/btn_check_off_disable_focused.png diff --git a/core/res/res/drawable/btn_check_off_pressed.png b/core/res/res/drawable/btn_check_off_pressed.png Binary files differindex 47c1a46..984dfd7 100644 --- a/core/res/res/drawable/btn_check_off_pressed.png +++ b/core/res/res/drawable/btn_check_off_pressed.png diff --git a/core/res/res/drawable/btn_check_off_selected.png b/core/res/res/drawable/btn_check_off_selected.png Binary files differindex cf53075..6854550 100644 --- a/core/res/res/drawable/btn_check_off_selected.png +++ b/core/res/res/drawable/btn_check_off_selected.png diff --git a/core/res/res/drawable/btn_check_on_disable.png b/core/res/res/drawable/btn_check_on_disable.png Binary files differindex 8e9f633..6cb02f3 100644 --- a/core/res/res/drawable/btn_check_on_disable.png +++ b/core/res/res/drawable/btn_check_on_disable.png diff --git a/core/res/res/drawable/btn_check_on_disable_focused.png b/core/res/res/drawable/btn_check_on_disable_focused.png Binary files differindex 0abb051..4b6064d 100644 --- a/core/res/res/drawable/btn_check_on_disable_focused.png +++ b/core/res/res/drawable/btn_check_on_disable_focused.png diff --git a/core/res/res/drawable/btn_check_on_pressed.png b/core/res/res/drawable/btn_check_on_pressed.png Binary files differindex ee2175d..300d64a 100644 --- a/core/res/res/drawable/btn_check_on_pressed.png +++ b/core/res/res/drawable/btn_check_on_pressed.png diff --git a/core/res/res/drawable/btn_check_on_selected.png b/core/res/res/drawable/btn_check_on_selected.png Binary files differindex 9356456..0b36adb 100644 --- a/core/res/res/drawable/btn_check_on_selected.png +++ b/core/res/res/drawable/btn_check_on_selected.png diff --git a/core/res/res/drawable/btn_default_normal.9.png b/core/res/res/drawable/btn_default_normal.9.png Binary files differindex ad1634a..a337eb5 100644 --- a/core/res/res/drawable/btn_default_normal.9.png +++ b/core/res/res/drawable/btn_default_normal.9.png diff --git a/core/res/res/drawable/btn_default_normal_disable.9.png b/core/res/res/drawable/btn_default_normal_disable.9.png Binary files differindex a89f37d..e414d76 100644 --- a/core/res/res/drawable/btn_default_normal_disable.9.png +++ b/core/res/res/drawable/btn_default_normal_disable.9.png diff --git a/core/res/res/drawable/btn_default_normal_disable_focused.9.png b/core/res/res/drawable/btn_default_normal_disable_focused.9.png Binary files differindex b71dae9..b2d45e5 100644 --- a/core/res/res/drawable/btn_default_normal_disable_focused.9.png +++ b/core/res/res/drawable/btn_default_normal_disable_focused.9.png diff --git a/core/res/res/drawable/btn_default_pressed.9.png b/core/res/res/drawable/btn_default_pressed.9.png Binary files differindex f496a86..92d5ab5 100644 --- a/core/res/res/drawable/btn_default_pressed.9.png +++ b/core/res/res/drawable/btn_default_pressed.9.png diff --git a/core/res/res/drawable/btn_default_selected.9.png b/core/res/res/drawable/btn_default_selected.9.png Binary files differindex c9e7e64..5405fa0 100644 --- a/core/res/res/drawable/btn_default_selected.9.png +++ b/core/res/res/drawable/btn_default_selected.9.png diff --git a/core/res/res/drawable/btn_default_small_normal.9.png b/core/res/res/drawable/btn_default_small_normal.9.png Binary files differindex faac205..963ecb3 100644 --- a/core/res/res/drawable/btn_default_small_normal.9.png +++ b/core/res/res/drawable/btn_default_small_normal.9.png diff --git a/core/res/res/drawable/btn_default_small_normal_disable.9.png b/core/res/res/drawable/btn_default_small_normal_disable.9.png Binary files differindex ce4fd28..15bb123 100644 --- a/core/res/res/drawable/btn_default_small_normal_disable.9.png +++ b/core/res/res/drawable/btn_default_small_normal_disable.9.png diff --git a/core/res/res/drawable/btn_default_small_normal_disable_focused.9.png b/core/res/res/drawable/btn_default_small_normal_disable_focused.9.png Binary files differindex 1f04c18..a343515 100644 --- a/core/res/res/drawable/btn_default_small_normal_disable_focused.9.png +++ b/core/res/res/drawable/btn_default_small_normal_disable_focused.9.png diff --git a/core/res/res/drawable/btn_default_small_pressed.9.png b/core/res/res/drawable/btn_default_small_pressed.9.png Binary files differindex dab005b..8476e12 100644 --- a/core/res/res/drawable/btn_default_small_pressed.9.png +++ b/core/res/res/drawable/btn_default_small_pressed.9.png diff --git a/core/res/res/drawable/btn_default_small_selected.9.png b/core/res/res/drawable/btn_default_small_selected.9.png Binary files differindex 5dec504..8ab8d63 100644 --- a/core/res/res/drawable/btn_default_small_selected.9.png +++ b/core/res/res/drawable/btn_default_small_selected.9.png diff --git a/core/res/res/drawable/btn_keyboard_key_normal.9.png b/core/res/res/drawable/btn_keyboard_key_normal.9.png Binary files differindex 5c8a945..7ba18dd 100644 --- a/core/res/res/drawable/btn_keyboard_key_normal.9.png +++ b/core/res/res/drawable/btn_keyboard_key_normal.9.png diff --git a/core/res/res/drawable/btn_keyboard_key_normal_off.9.png b/core/res/res/drawable/btn_keyboard_key_normal_off.9.png Binary files differindex e6e640a..bda9b83 100644 --- a/core/res/res/drawable/btn_keyboard_key_normal_off.9.png +++ b/core/res/res/drawable/btn_keyboard_key_normal_off.9.png diff --git a/core/res/res/drawable/btn_keyboard_key_normal_on.9.png b/core/res/res/drawable/btn_keyboard_key_normal_on.9.png Binary files differindex ce97a44..0c16ed5 100644 --- a/core/res/res/drawable/btn_keyboard_key_normal_on.9.png +++ b/core/res/res/drawable/btn_keyboard_key_normal_on.9.png diff --git a/core/res/res/drawable/btn_keyboard_key_pressed.9.png b/core/res/res/drawable/btn_keyboard_key_pressed.9.png Binary files differindex f2e9f04..39b9314 100755 --- a/core/res/res/drawable/btn_keyboard_key_pressed.9.png +++ b/core/res/res/drawable/btn_keyboard_key_pressed.9.png diff --git a/core/res/res/drawable/btn_keyboard_key_pressed_off.9.png b/core/res/res/drawable/btn_keyboard_key_pressed_off.9.png Binary files differindex d541f95..bdcf06e 100644 --- a/core/res/res/drawable/btn_keyboard_key_pressed_off.9.png +++ b/core/res/res/drawable/btn_keyboard_key_pressed_off.9.png diff --git a/core/res/res/drawable/btn_keyboard_key_pressed_on.9.png b/core/res/res/drawable/btn_keyboard_key_pressed_on.9.png Binary files differindex 1cd6d45..79621a9 100644 --- a/core/res/res/drawable/btn_keyboard_key_pressed_on.9.png +++ b/core/res/res/drawable/btn_keyboard_key_pressed_on.9.png diff --git a/core/res/res/drawable/btn_radio_off_pressed.png b/core/res/res/drawable/btn_radio_off_pressed.png Binary files differindex 41120ae..d6d8a9d 100644 --- a/core/res/res/drawable/btn_radio_off_pressed.png +++ b/core/res/res/drawable/btn_radio_off_pressed.png diff --git a/core/res/res/drawable/btn_radio_off_selected.png b/core/res/res/drawable/btn_radio_off_selected.png Binary files differindex 8b5535d..53f3e87 100644 --- a/core/res/res/drawable/btn_radio_off_selected.png +++ b/core/res/res/drawable/btn_radio_off_selected.png diff --git a/core/res/res/drawable/btn_radio_on.png b/core/res/res/drawable/btn_radio_on.png Binary files differindex 7286b31..7bf696d 100644 --- a/core/res/res/drawable/btn_radio_on.png +++ b/core/res/res/drawable/btn_radio_on.png diff --git a/core/res/res/drawable/btn_radio_on_pressed.png b/core/res/res/drawable/btn_radio_on_pressed.png Binary files differindex 20ce0ec..c904a35 100644 --- a/core/res/res/drawable/btn_radio_on_pressed.png +++ b/core/res/res/drawable/btn_radio_on_pressed.png diff --git a/core/res/res/drawable/btn_radio_on_selected.png b/core/res/res/drawable/btn_radio_on_selected.png Binary files differindex ed53dc7..78e1fc0 100644 --- a/core/res/res/drawable/btn_radio_on_selected.png +++ b/core/res/res/drawable/btn_radio_on_selected.png diff --git a/core/res/res/drawable/checkbox_label_background.9.png b/core/res/res/drawable/checkbox_label_background.9.png Binary files differdeleted file mode 100644 index e6af4b0..0000000 --- a/core/res/res/drawable/checkbox_label_background.9.png +++ /dev/null diff --git a/core/res/res/drawable/checkbox_off_background_focus_yellow.png b/core/res/res/drawable/checkbox_off_background_focus_yellow.png Binary files differdeleted file mode 100644 index ffde6f8..0000000 --- a/core/res/res/drawable/checkbox_off_background_focus_yellow.png +++ /dev/null diff --git a/core/res/res/drawable/checkbox_on_background_focus_yellow.png b/core/res/res/drawable/checkbox_on_background_focus_yellow.png Binary files differdeleted file mode 100644 index 3018009..0000000 --- a/core/res/res/drawable/checkbox_on_background_focus_yellow.png +++ /dev/null diff --git a/core/res/res/drawable/dark_header.9.png b/core/res/res/drawable/dark_header.9.png Binary files differnew file mode 100644 index 0000000..8fa5f09 --- /dev/null +++ b/core/res/res/drawable/dark_header.9.png diff --git a/core/res/res/drawable/divider_horizontal_bright.9.png b/core/res/res/drawable/divider_horizontal_bright.9.png Binary files differindex a1ba2d3..144fc22 100644 --- a/core/res/res/drawable/divider_horizontal_bright.9.png +++ b/core/res/res/drawable/divider_horizontal_bright.9.png diff --git a/core/res/res/drawable/divider_horizontal_dark.9.png b/core/res/res/drawable/divider_horizontal_dark.9.png Binary files differindex 7b32381..08838ca 100644 --- a/core/res/res/drawable/divider_horizontal_dark.9.png +++ b/core/res/res/drawable/divider_horizontal_dark.9.png diff --git a/core/res/res/drawable/divider_horizontal_dim_dark.9.png b/core/res/res/drawable/divider_horizontal_dim_dark.9.png Binary files differindex 20bc4dc..08838ca 100644 --- a/core/res/res/drawable/divider_horizontal_dim_dark.9.png +++ b/core/res/res/drawable/divider_horizontal_dim_dark.9.png diff --git a/core/res/res/drawable/extract_edit_text.xml b/core/res/res/drawable/extract_edit_text.xml new file mode 100644 index 0000000..9c6c4ba --- /dev/null +++ b/core/res/res/drawable/extract_edit_text.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2007 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:drawable="@drawable/keyboard_textfield_pressed" /> + <item android:state_focused="true" android:drawable="@drawable/keyboard_textfield_selected" /> + <item android:drawable="@drawable/textfield_disabled" /> +</selector> + diff --git a/core/res/res/drawable/ic_btn_speak_now.png b/core/res/res/drawable/ic_btn_speak_now.png Binary files differnew file mode 100644 index 0000000..83ee68b --- /dev/null +++ b/core/res/res/drawable/ic_btn_speak_now.png diff --git a/core/res/res/drawable/keyboard_background.9.png b/core/res/res/drawable/keyboard_background.9.png Binary files differindex 595acc5..1d3ce05 100644 --- a/core/res/res/drawable/keyboard_background.9.png +++ b/core/res/res/drawable/keyboard_background.9.png diff --git a/core/res/res/drawable/keyboard_suggest_strip_shadow.9.png b/core/res/res/drawable/keyboard_suggest_strip_shadow.9.png Binary files differnew file mode 100644 index 0000000..d231ae6 --- /dev/null +++ b/core/res/res/drawable/keyboard_suggest_strip_shadow.9.png diff --git a/core/res/res/drawable/keyboard_textfield_pressed.9.png b/core/res/res/drawable/keyboard_textfield_pressed.9.png Binary files differnew file mode 100644 index 0000000..f4e3f10 --- /dev/null +++ b/core/res/res/drawable/keyboard_textfield_pressed.9.png diff --git a/core/res/res/drawable/keyboard_textfield_selected.9.png b/core/res/res/drawable/keyboard_textfield_selected.9.png Binary files differnew file mode 100644 index 0000000..6e703af --- /dev/null +++ b/core/res/res/drawable/keyboard_textfield_selected.9.png diff --git a/core/res/res/drawable/menu_background.9.png b/core/res/res/drawable/menu_background.9.png Binary files differindex e7266b2..ee99583 100644 --- a/core/res/res/drawable/menu_background.9.png +++ b/core/res/res/drawable/menu_background.9.png diff --git a/core/res/res/drawable/radiobutton_label_background.9.png b/core/res/res/drawable/radiobutton_label_background.9.png Binary files differdeleted file mode 100644 index e6af4b0..0000000 --- a/core/res/res/drawable/radiobutton_label_background.9.png +++ /dev/null diff --git a/core/res/res/drawable/radiobutton_off_background_focus_yellow.png b/core/res/res/drawable/radiobutton_off_background_focus_yellow.png Binary files differdeleted file mode 100644 index 1a092e3..0000000 --- a/core/res/res/drawable/radiobutton_off_background_focus_yellow.png +++ /dev/null diff --git a/core/res/res/drawable/radiobutton_on_background_focus_yellow.png b/core/res/res/drawable/radiobutton_on_background_focus_yellow.png Binary files differdeleted file mode 100644 index aa59771..0000000 --- a/core/res/res/drawable/radiobutton_on_background_focus_yellow.png +++ /dev/null diff --git a/core/res/res/drawable/scrollbar_handle_horizontal.9.png b/core/res/res/drawable/scrollbar_handle_horizontal.9.png Binary files differindex f333733..324b4bd 100755 --- a/core/res/res/drawable/scrollbar_handle_horizontal.9.png +++ b/core/res/res/drawable/scrollbar_handle_horizontal.9.png diff --git a/core/res/res/drawable/scrollbar_handle_vertical.9.png b/core/res/res/drawable/scrollbar_handle_vertical.9.png Binary files differindex ff08295..3519d68 100755 --- a/core/res/res/drawable/scrollbar_handle_vertical.9.png +++ b/core/res/res/drawable/scrollbar_handle_vertical.9.png diff --git a/core/res/res/drawable/statusbar_background.png b/core/res/res/drawable/statusbar_background.png Binary files differindex 6af7329..945ad92 100644 --- a/core/res/res/drawable/statusbar_background.png +++ b/core/res/res/drawable/statusbar_background.png diff --git a/core/res/res/drawable/textfield_default.9.png b/core/res/res/drawable/textfield_default.9.png Binary files differindex ab99aeb..12cdd4c 100644 --- a/core/res/res/drawable/textfield_default.9.png +++ b/core/res/res/drawable/textfield_default.9.png diff --git a/core/res/res/drawable/textfield_disabled.9.png b/core/res/res/drawable/textfield_disabled.9.png Binary files differindex 0070158..ad8ba70 100644 --- a/core/res/res/drawable/textfield_disabled.9.png +++ b/core/res/res/drawable/textfield_disabled.9.png diff --git a/core/res/res/drawable/textfield_disabled_selected.9.png b/core/res/res/drawable/textfield_disabled_selected.9.png Binary files differindex 139d606..04b6b6b 100755..100644 --- a/core/res/res/drawable/textfield_disabled_selected.9.png +++ b/core/res/res/drawable/textfield_disabled_selected.9.png diff --git a/core/res/res/drawable/textfield_pressed.9.png b/core/res/res/drawable/textfield_pressed.9.png Binary files differindex 7b1350f..bcc0152 100644 --- a/core/res/res/drawable/textfield_pressed.9.png +++ b/core/res/res/drawable/textfield_pressed.9.png diff --git a/core/res/res/drawable/textfield_selected.9.png b/core/res/res/drawable/textfield_selected.9.png Binary files differindex 7286ba5..47115b6 100644 --- a/core/res/res/drawable/textfield_selected.9.png +++ b/core/res/res/drawable/textfield_selected.9.png diff --git a/core/res/res/drawable/title_bar.9.png b/core/res/res/drawable/title_bar.9.png Binary files differindex 8d18339..44c3179 100644 --- a/core/res/res/drawable/title_bar.9.png +++ b/core/res/res/drawable/title_bar.9.png diff --git a/core/res/res/drawable/title_bar_shadow.9.png b/core/res/res/drawable/title_bar_shadow.9.png Binary files differnew file mode 100644 index 0000000..0872366 --- /dev/null +++ b/core/res/res/drawable/title_bar_shadow.9.png diff --git a/core/res/res/drawable/title_bar_shadow.png b/core/res/res/drawable/title_bar_shadow.png Binary files differdeleted file mode 100644 index a717814..0000000 --- a/core/res/res/drawable/title_bar_shadow.png +++ /dev/null diff --git a/core/res/res/drawable/title_bar_tall.png b/core/res/res/drawable/title_bar_tall.png Binary files differnew file mode 100644 index 0000000..09f5447 --- /dev/null +++ b/core/res/res/drawable/title_bar_tall.png diff --git a/core/res/res/drawable/zoom_ring_arrows.png b/core/res/res/drawable/zoom_ring_arrows.png Binary files differnew file mode 100644 index 0000000..e443de7 --- /dev/null +++ b/core/res/res/drawable/zoom_ring_arrows.png diff --git a/core/res/res/drawable/zoom_ring_thumb.png b/core/res/res/drawable/zoom_ring_thumb.png Binary files differnew file mode 100644 index 0000000..4cc5f6f --- /dev/null +++ b/core/res/res/drawable/zoom_ring_thumb.png diff --git a/core/res/res/drawable/zoom_ring_track.png b/core/res/res/drawable/zoom_ring_track.png Binary files differnew file mode 100644 index 0000000..91d91f4 --- /dev/null +++ b/core/res/res/drawable/zoom_ring_track.png diff --git a/core/res/res/layout/activity_list.xml b/core/res/res/layout/activity_list.xml new file mode 100644 index 0000000..2967f0f --- /dev/null +++ b/core/res/res/layout/activity_list.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + > + + <ListView + android:id="@android:id/list" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + /> + + <TextView + android:id="@android:id/empty" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:gravity="center" + android:text="@string/activity_list_empty" + android:visibility="gone" + android:textAppearance="?android:attr/textAppearanceMedium" + /> + +</FrameLayout> diff --git a/core/res/res/layout/activity_list_item_2.xml b/core/res/res/layout/activity_list_item_2.xml new file mode 100644 index 0000000..78eca02 --- /dev/null +++ b/core/res/res/layout/activity_list_item_2.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:minHeight="?android:attr/listPreferredItemHeight" + android:textAppearance="?android:attr/textAppearanceLarge" + android:gravity="center_vertical" + android:drawablePadding="14dip" + android:paddingLeft="15dip" + android:paddingRight="15dip" /> diff --git a/core/res/res/layout/expanded_menu_layout.xml b/core/res/res/layout/expanded_menu_layout.xml index cd4ea12..5d98773 100644 --- a/core/res/res/layout/expanded_menu_layout.xml +++ b/core/res/res/layout/expanded_menu_layout.xml @@ -16,5 +16,5 @@ <com.android.internal.view.menu.ExpandedMenuView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+android:id/expanded_menu" - android:layout_width="320dip" + android:layout_width="296dip" android:layout_height="wrap_content" /> diff --git a/core/res/res/layout/input_method.xml b/core/res/res/layout/input_method.xml index ec75cf1..a21bbe8 100644 --- a/core/res/res/layout/input_method.xml +++ b/core/res/res/layout/input_method.xml @@ -27,7 +27,7 @@ <FrameLayout android:id="@android:id/extractArea" android:layout_width="fill_parent" - android:layout_height="wrap_content" + android:layout_height="0px" android:layout_weight="1" android:visibility="gone"> </FrameLayout> diff --git a/core/res/res/layout/input_method_extract_view.xml b/core/res/res/layout/input_method_extract_view.xml index f8a4bde..3f68bd3 100644 --- a/core/res/res/layout/input_method_extract_view.xml +++ b/core/res/res/layout/input_method_extract_view.xml @@ -21,8 +21,10 @@ <android.inputmethodservice.ExtractEditText xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/inputExtractEditText" - android:layout_width="fill_parent" - android:layout_height="wrap_content" + android:scrollbars="vertical" + android:gravity="top" + android:minLines="1" android:inputType="text" + android:background="@android:drawable/extract_edit_text" > </android.inputmethodservice.ExtractEditText> diff --git a/core/res/res/layout/js_prompt.xml b/core/res/res/layout/js_prompt.xml index 9ab9d09..86974ba 100644 --- a/core/res/res/layout/js_prompt.xml +++ b/core/res/res/layout/js_prompt.xml @@ -20,16 +20,17 @@ android:layout_height="wrap_content" android:gravity="center_horizontal" > - + <TextView android:id="@+id/message" android:layout_width="fill_parent" android:layout_height="wrap_content" /> - + <EditText android:id="@+id/value" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textStyle="bold" + android:inputType="text" android:selectAllOnFocus="true" android:scrollHorizontally="true" android:layout_marginTop="6dip" diff --git a/core/res/res/layout/keyguard_screen_glogin_unlock.xml b/core/res/res/layout/keyguard_screen_glogin_unlock.xml index 9f306cc..beaa926 100644 --- a/core/res/res/layout/keyguard_screen_glogin_unlock.xml +++ b/core/res/res/layout/keyguard_screen_glogin_unlock.xml @@ -16,87 +16,100 @@ ** limitations under the License. */ --> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" + android:orientation="vertical" android:background="#FF000000" > - - <TextView - android:id="@+id/topHeader" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_alignParentTop="true" - android:textSize="24sp" - android:layout_marginTop="8dip" - android:drawableLeft="@drawable/ic_lock_idle_lock" - android:drawablePadding="5dip" - android:text="@android:string/lockscreen_glogin_too_many_attempts" - /> - - <!-- spacer below header --> - <View - android:id="@+id/spacerTop" - android:layout_width="fill_parent" - android:layout_height="1dip" - android:layout_below="@id/topHeader" - android:layout_marginTop="8dip" - android:background="@android:drawable/divider_horizontal_bright"/> - - <TextView - android:id="@+id/instructions" + <ScrollView android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_below="@+id/spacerTop" - android:layout_marginTop="8dip" - android:gravity="center" - android:textSize="18sp" - android:text="@android:string/lockscreen_glogin_instructions" - /> - - <EditText - android:id="@+id/login" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_below="@id/instructions" - android:layout_marginTop="8dip" - android:hint="@android:string/lockscreen_glogin_username_hint" - android:singleLine="true" - /> - - <EditText - android:id="@+id/password" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_below="@id/login" - android:layout_marginTop="8dip" - android:password="true" - android:hint="@android:string/lockscreen_glogin_password_hint" - android:singleLine="true" - android:nextFocusRight="@+id/ok" - android:nextFocusDown="@+id/ok" - /> - - <!-- ok below password, aligned to right of screen --> - <Button - android:id="@+id/ok" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@id/password" - android:layout_marginTop="8dip" - android:layout_alignParentRight="true" - android:textSize="18sp" - android:text="@android:string/lockscreen_glogin_submit_button" - /> - + android:layout_height="0px" + android:layout_weight="1" + android:layout_above="@+id/emergencyCall"> + <RelativeLayout + android:layout_width="fill_parent" + android:layout_height="fill_parent" + > + + <TextView + android:id="@+id/topHeader" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:textSize="24sp" + android:layout_marginTop="8dip" + android:drawableLeft="@drawable/ic_lock_idle_lock" + android:drawablePadding="5dip" + android:text="@android:string/lockscreen_glogin_too_many_attempts" + /> + + <!-- spacer below header --> + <View + android:id="@+id/spacerTop" + android:layout_width="fill_parent" + android:layout_height="1dip" + android:layout_below="@id/topHeader" + android:layout_marginTop="8dip" + android:background="@android:drawable/divider_horizontal_bright"/> + + <TextView + android:id="@+id/instructions" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@+id/spacerTop" + android:layout_marginTop="8dip" + android:gravity="center" + android:textSize="18sp" + android:text="@android:string/lockscreen_glogin_instructions" + /> + + <EditText + android:id="@+id/login" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/instructions" + android:layout_marginTop="8dip" + android:hint="@android:string/lockscreen_glogin_username_hint" + android:inputType="textEmailAddress" + /> + + <EditText + android:id="@+id/password" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/login" + android:layout_marginTop="8dip" + android:inputType="textPassword" + android:hint="@android:string/lockscreen_glogin_password_hint" + android:nextFocusRight="@+id/ok" + android:nextFocusDown="@+id/ok" + /> + + <!-- ok below password, aligned to right of screen --> + <Button + android:id="@+id/ok" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/password" + android:layout_marginTop="8dip" + android:layout_alignParentRight="true" + android:textSize="18sp" + android:text="@android:string/lockscreen_glogin_submit_button" + /> + + </RelativeLayout> + </ScrollView> + + <View android:layout_width="1dp" android:layout_height="3dp" /> + <!-- emergency call button at bottom center --> <Button android:id="@+id/emergencyCall" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentBottom="true" - android:layout_marginBottom="8dip" - android:layout_centerHorizontal="true" + android:layout_gravity="center" + android:layout_weight="0" android:textSize="18sp" android:drawableLeft="@drawable/ic_emergency" android:drawablePadding="3dip" @@ -110,6 +123,4 @@ android:layout_above="@id/emergencyCall" android:layout_marginBottom="8dip" android:background="@android:drawable/divider_horizontal_bright"/--> - - -</RelativeLayout> +</LinearLayout> diff --git a/core/res/res/layout/keyguard_screen_lock.xml b/core/res/res/layout/keyguard_screen_lock.xml index f5c850a..907859a 100644 --- a/core/res/res/layout/keyguard_screen_lock.xml +++ b/core/res/res/layout/keyguard_screen_lock.xml @@ -71,7 +71,7 @@ android:layout_width="fill_parent" android:layout_height="1dip" android:layout_marginTop="8dip" - android:background="@android:drawable/divider_horizontal_bright"/> + android:background="@android:drawable/divider_horizontal_dark"/> <!-- time and date --> <TextView android:id="@+id/time" @@ -91,7 +91,7 @@ android:layout_width="fill_parent" android:layout_height="1dip" android:layout_marginBottom="8dip" - android:background="@android:drawable/divider_horizontal_bright" + android:background="@android:drawable/divider_horizontal_dark" /> <!-- battery info --> @@ -125,7 +125,7 @@ android:layout_height="1dip" android:layout_marginTop="8dip" android:layout_marginBottom="8dip" - android:background="@android:drawable/divider_horizontal_bright" + android:background="@android:drawable/divider_horizontal_dark" /> <!-- next alarm info --> @@ -160,7 +160,7 @@ android:layout_height="1dip" android:layout_marginTop="8dip" android:layout_marginBottom="8dip" - android:background="@android:drawable/divider_horizontal_bright"/> + android:background="@android:drawable/divider_horizontal_dark"/> <!-- lock icon with 'screen locked' message (shown when SIM card is present) --> @@ -194,6 +194,7 @@ <TextView android:id="@+id/lockInstructions" android:layout_width="fill_parent" android:layout_height="wrap_content" + android:layout_marginBottom="5dip" android:gravity="center" android:textSize="14sp"/> @@ -206,7 +207,6 @@ android:layout_marginLeft="5dip" android:layout_marginRight="5dip" android:layout_marginBottom="5dip" - android:layout_marginTop="5dip" android:drawableTop="@drawable/ic_emergency" android:drawablePadding="3dip" android:text="@android:string/lockscreen_emergency_call" diff --git a/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml b/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml index fd70f50..00a6487 100644 --- a/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml +++ b/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml @@ -45,7 +45,7 @@ android:maxLines="1" android:background="@null" android:textSize="32sp" - android:password="true" + android:inputType="textPassword" /> <ImageButton android:id="@+id/backspace" diff --git a/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml b/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml index 566da21..d47ca5f 100644 --- a/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml +++ b/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml @@ -54,7 +54,7 @@ android:layout_height="fill_parent" android:maxLines="1" android:background="@null" - android:password="true" + android:inputType="textPassword" /> <ImageButton android:id="@+id/backspace" diff --git a/core/res/res/layout/list_menu_item_layout.xml b/core/res/res/layout/list_menu_item_layout.xml index 20c46a7..df4958f 100644 --- a/core/res/res/layout/list_menu_item_layout.xml +++ b/core/res/res/layout/list_menu_item_layout.xml @@ -18,7 +18,7 @@ android:layout_width="fill_parent" android:layout_height="?android:attr/listPreferredItemHeight"> - <!-- Icon, checkbox, and/or radio button will be inserted here. --> + <!-- Icon will be inserted here. --> <!-- The title and summary have some gap between them, and this 'group' should be centered vertically. --> <RelativeLayout @@ -54,4 +54,6 @@ </RelativeLayout> + <!-- Checkbox, and/or radio button will be inserted here. --> + </com.android.internal.view.menu.ListMenuItemView> diff --git a/core/res/res/layout/preference.xml b/core/res/res/layout/preference.xml index ac6dd76..00745b4 100644 --- a/core/res/res/layout/preference.xml +++ b/core/res/res/layout/preference.xml @@ -27,10 +27,10 @@ <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginLeft="16sp" - android:layout_marginRight="6sp" - android:layout_marginTop="6sp" - android:layout_marginBottom="6sp" + android:layout_marginLeft="15dip" + android:layout_marginRight="6dip" + android:layout_marginTop="6dip" + android:layout_marginBottom="6dip" android:layout_weight="1"> <TextView android:id="@+android:id/title" diff --git a/core/res/res/layout/preference_child.xml b/core/res/res/layout/preference_child.xml index 1d626b9..5f8ddd4 100644 --- a/core/res/res/layout/preference_child.xml +++ b/core/res/res/layout/preference_child.xml @@ -17,29 +17,35 @@ <!-- Layout for a visually child-like Preference in a PreferenceActivity. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" - android:layout_height="?android:attr/listPreferredItemHeight" + android:layout_height="wrap_content" + android:minHeight="?android:attr/listPreferredItemHeight" android:gravity="center_vertical" android:paddingRight="?android:attr/scrollbarSize"> <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginLeft="20sp" + android:layout_marginLeft="20dip" + android:layout_marginRight="6dip" + android:layout_marginTop="6dip" + android:layout_marginBottom="6dip" android:layout_weight="1"> <TextView android:id="@+android:id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" - android:textAppearance="?android:attr/textAppearanceLarge" /> + android:textAppearance="?android:attr/textAppearanceLarge" + android:ellipsize="marquee" + android:fadingEdge="horizontal" /> <TextView android:id="@+android:id/summary" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@android:id/title" android:layout_alignLeft="@android:id/title" - android:maxLines="2" android:textAppearance="?android:attr/textAppearanceSmall" + android:maxLines="2" android:textColor="?android:attr/textColorSecondary" /> </RelativeLayout> @@ -48,6 +54,7 @@ <LinearLayout android:id="@+android:id/widget_frame" android:layout_width="wrap_content" android:layout_height="fill_parent" - /> + android:gravity="center_vertical" + android:orientation="vertical" /> </LinearLayout> diff --git a/core/res/res/layout/resolve_list_item.xml b/core/res/res/layout/resolve_list_item.xml index da35e97..5e296c5 100644 --- a/core/res/res/layout/resolve_list_item.xml +++ b/core/res/res/layout/resolve_list_item.xml @@ -17,37 +17,38 @@ ** limitations under the License. */ --> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:gravity="center_vertical" + android:orientation="horizontal" android:minHeight="?android:attr/listPreferredItemHeight" + android:layout_height="wrap_content" + android:layout_width="fill_parent" android:paddingLeft="14dip" android:paddingRight="15dip"> - <!-- Activity icon when presenting dialog --> <ImageView android:id="@+id/icon" android:layout_width="@android:dimen/app_icon_size" android:layout_height="@android:dimen/app_icon_size" - android:layout_alignParentLeft="true" android:scaleType="fitCenter" /> - <!-- Activity name --> - <TextView android:id="@android:id/text1" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceLargeInverse" - android:layout_toRightOf="@id/icon" - android:paddingLeft="6dip" /> - - <!-- Extended activity info to distinguish between duplicate activity names --> - <TextView android:id="@android:id/text2" + <LinearLayout + android:orientation="vertical" + android:gravity="center_vertical" android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceMediumInverse" - android:layout_toRightOf="@id/icon" - android:layout_below="@id/text1" - android:paddingLeft="6dip" /> -</RelativeLayout> + android:layout_height="wrap_content" > + <!-- Activity name --> + <TextView android:id="@android:id/text1" + android:textAppearance="?android:attr/textAppearanceLargeInverse" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="6dip" /> + <!-- Extended activity info to distinguish between duplicate activity names --> + <TextView android:id="@android:id/text2" + android:textAppearance="?android:attr/textAppearanceMediumInverse" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="6dip" /> + </LinearLayout> +</LinearLayout> diff --git a/core/res/res/layout/search_bar.xml b/core/res/res/layout/search_bar.xml index 6678bc2..657ee1f 100644 --- a/core/res/res/layout/search_bar.xml +++ b/core/res/res/layout/search_bar.xml @@ -91,6 +91,11 @@ android:drawableLeft="@android:drawable/ic_btn_search" /> + <ImageButton android:id="@+id/search_voice_btn" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@android:drawable/ic_btn_speak_now" + /> </LinearLayout> </LinearLayout> diff --git a/core/res/res/layout/status_bar.xml b/core/res/res/layout/status_bar.xml index dfdaf66..9a6b7e8 100644 --- a/core/res/res/layout/status_bar.xml +++ b/core/res/res/layout/status_bar.xml @@ -90,7 +90,7 @@ <com.android.server.status.DateView android:id="@+id/date" android:layout_width="wrap_content" - android:layout_height="25dp" + android:layout_height="fill_parent" android:singleLine="true" android:textSize="16sp" android:textStyle="bold" diff --git a/core/res/res/layout/zoom_magnify.xml b/core/res/res/layout/zoom_magnify.xml index 08a5f7b..374819e 100644 --- a/core/res/res/layout/zoom_magnify.xml +++ b/core/res/res/layout/zoom_magnify.xml @@ -19,16 +19,14 @@ --> <merge xmlns:android="http://schemas.android.com/apk/res/android"> <ZoomControls android:id="@+id/zoomControls" - android:layout_alignParentBottom="true" - android:layout_centerHorizontal="true" + android:layout_gravity="bottom|center_horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" style="@style/ZoomControls" /> <ImageView android:id="@+id/zoomMagnify" android:focusable="true" - android:layout_alignParentBottom="true" - android:layout_alignParentRight="true" + android:layout_gravity="bottom|right" android:paddingRight="2dip" android:src="@drawable/btn_zoom_page" android:layout_width="wrap_content" diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 76232de..37632f8 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -262,6 +262,10 @@ <string name="permdesc_reboot">"Umožňuje aplikaci vynutit restartování telefonu."</string> <string name="permlab_mount_unmount_filesystems">"připojení a odpojení souborových systémů"</string> <string name="permdesc_mount_unmount_filesystems">"Umožňuje aplikaci připojit či odpojit souborové systémy ve vyměnitelných úložištích."</string> + <!-- no translation found for permlab_mount_format_filesystems (5523285143576718981) --> + <skip /> + <!-- no translation found for permdesc_mount_format_filesystems (574060044906047386) --> + <skip /> <string name="permlab_vibrate">"ovládání vibrací"</string> <string name="permdesc_vibrate">"Umožňuje aplikaci ovládat vibrace."</string> <string name="permlab_flashlight">"ovládání kontrolky"</string> @@ -276,6 +280,10 @@ <string name="permdesc_locationUpdates">"Umožňuje povolit či zakázat aktualizace polohy prostřednictvím bezdrátového připojení. Aplikace toto nastavení obvykle nepoužívají."</string> <string name="permlab_checkinProperties">"přístup k vlastnostem Checkin"</string> <string name="permdesc_checkinProperties">"Umožňuje čtení i zápis vlastností nahraných službou Checkin. Běžné aplikace toto nastavení obvykle nevyužívají."</string> + <!-- no translation found for permlab_bindGadget (2519859363977647275) --> + <skip /> + <!-- no translation found for permdesc_bindGadget (7865866514555126333) --> + <skip /> <string name="permlab_modifyPhoneState">"změny stavu telefonu"</string> <string name="permdesc_modifyPhoneState">"Umožňuje aplikaci ovládat telefonní funkce zařízení. Aplikace s tímto oprávněním může přepínat sítě nebo zapnout či vypnout bezdrátové připojení telefonu bez vašeho svolení."</string> <string name="permlab_readPhoneState">"zjistit stav telefonu"</string> @@ -304,6 +312,10 @@ <string name="permdesc_writeApnSettings">"Umožňuje aplikaci změnit nastavení APN, jako je například proxy či port APN."</string> <string name="permlab_changeNetworkState">"změna připojení k síti"</string> <string name="permdesc_changeNetworkState">"Umožňuje aplikaci změnit stav připojení k síti."</string> + <!-- no translation found for permlab_changeBackgroundDataSetting (1400666012671648741) --> + <skip /> + <!-- no translation found for permdesc_changeBackgroundDataSetting (1001482853266638864) --> + <skip /> <string name="permlab_accessWifiState">"zobrazení stavu WiFi"</string> <string name="permdesc_accessWifiState">"Umožňuje aplikaci zobrazit informace o stavu připojení WiFi."</string> <string name="permlab_changeWifiState">"Změnit stav WiFi"</string> @@ -546,12 +558,12 @@ <!-- no translation found for relative_time (1818557177829411417) --> <skip /> <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first">"<xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="DAY">dd</xliff:g>, <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first">"<xliff:g id="DAY">dd</xliff:g>. <xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first">"<xliff:g id="DAY">dd</xliff:g>. <xliff:g id="MONTH">MMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first">"<xliff:g id="DAY">dd</xliff:g>. <xliff:g id="MONTH">MMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g>"</string> + <string name="full_date_month_first">"<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="full_date_day_first">"<xliff:g id="DAY">d</xliff:g>'. '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_month_first">"<xliff:g id="DAY">d</xliff:g>'. '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_day_first">"<xliff:g id="DAY">d</xliff:g>'. '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> + <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"poledne"</string> <string name="Noon">"Poledne"</string> <string name="midnight">"půlnoc"</string> @@ -679,6 +691,8 @@ <string name="paste">"Vložit"</string> <string name="copyUrl">"Kopírovat adresu URL"</string> <string name="inputMethod">"Metoda zadávání dat"</string> + <!-- no translation found for addToDictionary (726256909274177272) --> + <skip /> <string name="editTextMenuTitle">"Úpravy textu"</string> <string name="low_internal_storage_view_title">"Málo paměti"</string> <string name="low_internal_storage_view_text">"V telefonu zbývá málo místa pro ukládání dat."</string> @@ -686,6 +700,8 @@ <string name="cancel">"Zrušit"</string> <string name="yes">"OK"</string> <string name="no">"Zrušit"</string> + <!-- no translation found for dialog_alert_title (2049658708609043103) --> + <skip /> <string name="capital_on">"ZAPNUTO"</string> <string name="capital_off">"VYPNOUT"</string> <string name="whichApplication">"Dokončit akci pomocí aplikace"</string> @@ -709,7 +725,8 @@ <string name="volume_music">"Hlasitost médií"</string> <string name="volume_music_hint_playing_through_bluetooth">"Přehrávání pomocí rozhraní Bluetooth"</string> <string name="volume_call">"Hlasitost hovoru"</string> - <string name="volume_call_hint_playing_through_bluetooth">"Přehrávání pomocí rozhraní Bluetooth"</string> + <!-- no translation found for volume_bluetooth_call (2002891926351151534) --> + <skip /> <string name="volume_alarm">"Hlasitost upozornění a budíku"</string> <string name="volume_notification">"Hlasitost oznámení"</string> <string name="volume_unknown">"Hlasitost"</string> @@ -745,8 +762,61 @@ <string name="usb_storage_error_message">"Při používání vaší karty SD jako úložiště USB došlo k problému."</string> <string name="usb_storage_notification_title">"USB připojeno"</string> <string name="usb_storage_notification_message">"Vyberte, chcete-li kopírovat soubory do nebo z počítače."</string> + <!-- no translation found for usb_storage_stop_notification_title (2336058396663516017) --> + <skip /> + <!-- no translation found for usb_storage_stop_notification_message (2591813490269841539) --> + <skip /> + <!-- no translation found for usb_storage_stop_title (6014127947456185321) --> + <skip /> + <!-- no translation found for usb_storage_stop_message (2390958966725232848) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_mount (1181858854166273345) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_unmount (3774611918660582898) --> + <skip /> + <!-- no translation found for usb_storage_stop_error_message (3917317248440072084) --> + <skip /> + <!-- no translation found for extmedia_format_title (8663247929551095854) --> + <skip /> + <!-- no translation found for extmedia_format_message (3621369962433523619) --> + <skip /> + <!-- no translation found for extmedia_format_button_format (4131064560127478695) --> + <skip /> <string name="select_input_method">"Výběr metody zadávání dat"</string> <string name="fast_scroll_alphabet">"AÁBCČDĎEÉĚFGHCHIÍJKLMNŇOÓPQRŘSŠTŤUÚVWXYÝZŽ"</string> <string name="fast_scroll_numeric_alphabet">"0123456789AÁBCČDĎEÉĚFGHCHIÍJKLMNŇOÓPQRŘSŠTŤUÚVWXYÝZŽ"</string> - <string name="candidates_style"><font fgcolor="#ff000000" bgcolor="#ff8080ff"><u>"kandidáti"</u>"u>"</font></string> + <!-- no translation found for candidates_style (4333913089637062257) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_title (5457603418970994050) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_message (4747432538578886744) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_title (780477838241212997) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_message (1312266820092958014) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_title (6410723906019100189) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_message (2679412884290061775) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_title (6872152882604407837) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_message (7260183293747448241) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_title (6729801130790616200) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_message (7613960686747592770) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_title (8902518030404381318) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_message (4205117227342822275) --> + <skip /> + <!-- no translation found for activity_list_empty (4168820609403385789) --> + <skip /> + <!-- no translation found for permlab_pkgUsageStats (8787352074326748892) --> + <skip /> + <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> + <skip /> + <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> + <skip /> </resources> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index a8115a8..3015957 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -89,7 +89,7 @@ <string name="low_memory">"Telefonspeicher ist voll! Löschen Sie Dateien, um Speicherplatz freizugeben."</string> <string name="me">"Eigene"</string> <string name="power_dialog">"Telefonoptionen"</string> - <string name="silent_mode">"Lautlos"</string> + <string name="silent_mode">"Lautlos-Modus"</string> <string name="turn_on_radio">"Funk einschalten"</string> <string name="turn_off_radio">"Funk ausschalten"</string> <string name="screen_lock">"Bildschirmsperre"</string> @@ -101,8 +101,8 @@ <string name="global_action_lock">"Bildschirmsperre"</string> <string name="global_action_power_off">"Ausschalten"</string> <string name="global_action_toggle_silent_mode">"Lautlos"</string> - <string name="global_action_silent_mode_on_status">"Ton ist ausgeschaltet."</string> - <string name="global_action_silent_mode_off_status">"Ton ist EINGESCHALTET"</string> + <string name="global_action_silent_mode_on_status">"Ton ist bereits AUS"</string> + <string name="global_action_silent_mode_off_status">"Ton ist momentan AN"</string> <string name="safeMode">"Abgesicherter Modus"</string> <string name="permgrouplab_costMoney">"Kostenpflichtige Dienste"</string> <string name="permgroupdesc_costMoney">"Ermöglicht Anwendungen die Ausführung eventuell kostenpflichtiger Aktionen."</string> @@ -262,6 +262,10 @@ <string name="permdesc_reboot">"Ermöglicht der Anwendung, einen Neustart des Telefons zu erzwingen."</string> <string name="permlab_mount_unmount_filesystems">"Dateisysteme bereitstellen oder Bereitstellung aufheben"</string> <string name="permdesc_mount_unmount_filesystems">"Ermöglicht der Anwendung, Dateisysteme für austauschbare Speicherplätze bereitzustellen oder die Bereitstellung aufzuheben."</string> + <!-- no translation found for permlab_mount_format_filesystems (5523285143576718981) --> + <skip /> + <!-- no translation found for permdesc_mount_format_filesystems (574060044906047386) --> + <skip /> <string name="permlab_vibrate">"Vibrationsalarm steuern"</string> <string name="permdesc_vibrate">"Ermöglicht der Anwendung, den Vibrationsalarm zu steuern."</string> <string name="permlab_flashlight">"Lichtanzeige steuern"</string> @@ -276,6 +280,10 @@ <string name="permdesc_locationUpdates">"Ermöglicht die Aktivierung/Deaktivierung der Radio-Benachrichtigungen über Standort-Updates. Nicht für normale Anwendungen vorgesehen."</string> <string name="permlab_checkinProperties">"Auf Check-In-Eigenschaften zugreifen"</string> <string name="permdesc_checkinProperties">"Ermöglicht den Schreib-/Lesezugriff auf vom Check-In-Service hochgeladene Elemente. Nicht für normale Anwendungen vorgesehen."</string> + <!-- no translation found for permlab_bindGadget (2519859363977647275) --> + <skip /> + <!-- no translation found for permdesc_bindGadget (7865866514555126333) --> + <skip /> <string name="permlab_modifyPhoneState">"Telefonstatus ändern"</string> <string name="permdesc_modifyPhoneState">"Ermöglicht einer Anwendung, die Telefonfunktionen des Gerätes zu steuern. Eine Anwendung mit dieser Berechtigung kann unter anderem das Netzwerk wechseln oder das Radio des Telefons ein- und ausschalten, ohne Sie darüber zu informieren."</string> <string name="permlab_readPhoneState">"Telefonstatus lesen"</string> @@ -304,6 +312,10 @@ <string name="permdesc_writeApnSettings">"Ermöglicht einer Anwendung, die APN-Einstellungen wie Proxy und Port eines Zugriffspunkts zu ändern."</string> <string name="permlab_changeNetworkState">"Netzwerkkonnektivität ändern"</string> <string name="permdesc_changeNetworkState">"Ermöglicht einer Anwendung, den Status der Netzwerkkonnektivität zu ändern."</string> + <!-- no translation found for permlab_changeBackgroundDataSetting (1400666012671648741) --> + <skip /> + <!-- no translation found for permdesc_changeBackgroundDataSetting (1001482853266638864) --> + <skip /> <string name="permlab_accessWifiState">"WLAN-Status anzeigen"</string> <string name="permdesc_accessWifiState">"Ermöglicht einer Anwendung, die Informationen zum WLAN-Status einzusehen."</string> <string name="permlab_changeWifiState">"WLAN-Status ändern"</string> @@ -381,7 +393,7 @@ <string name="emergency_call_dialog_number_for_display">"Notrufnummer"</string> <string name="lockscreen_carrier_default">"(kein Dienst)"</string> <string name="lockscreen_screen_locked">"Display gesperrt."</string> - <string name="lockscreen_instructions_when_pattern_enabled">"Drücken Sie auf die \"Menü\", um das Telefon zu entsperren oder einen Notruf zu tätigen."</string> + <string name="lockscreen_instructions_when_pattern_enabled">"Drücken Sie auf \"Menü\", um das Telefon zu entsperren oder einen Notruf zu tätigen."</string> <string name="lockscreen_instructions_when_pattern_disabled">"Drücken Sie zum Entsperren auf \"Menü\"."</string> <string name="lockscreen_pattern_instructions">"Schema für Entsperrung zeichnen"</string> <string name="lockscreen_emergency_call">"Notruf"</string> @@ -547,12 +559,12 @@ <!-- no translation found for relative_time (1818557177829411417) --> <skip /> <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first">"<xliff:g id="DAY">dd</xliff:g>. <xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first">"<xliff:g id="DAY">dd</xliff:g>. <xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first">"<xliff:g id="DAY">dd</xliff:g>. <xliff:g id="MONTH">MMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first">"<xliff:g id="DAY">dd</xliff:g>. <xliff:g id="MONTH">MMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g>"</string> + <string name="full_date_month_first">"<xliff:g id="DAY">d</xliff:g>'. '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="full_date_day_first">"<xliff:g id="DAY">d</xliff:g>'. '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_month_first">"<xliff:g id="DAY">d</xliff:g>'. '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_day_first">"<xliff:g id="DAY">d</xliff:g>'. '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> + <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"Mittag"</string> <string name="Noon">"Mittag"</string> <string name="midnight">"Mitternacht"</string> @@ -680,6 +692,8 @@ <string name="paste">"Einfügen"</string> <string name="copyUrl">"URL kopieren"</string> <string name="inputMethod">"Eingabemethode"</string> + <!-- no translation found for addToDictionary (726256909274177272) --> + <skip /> <string name="editTextMenuTitle">"Text bearbeiten"</string> <string name="low_internal_storage_view_title">"Geringer Speicher"</string> <string name="low_internal_storage_view_text">"Kaum noch freier Telefonspeicher verfügbar."</string> @@ -687,6 +701,8 @@ <string name="cancel">"Abbrechen"</string> <string name="yes">"OK"</string> <string name="no">"Abbrechen"</string> + <!-- no translation found for dialog_alert_title (2049658708609043103) --> + <skip /> <string name="capital_on">"EIN"</string> <string name="capital_off">"AUS"</string> <string name="whichApplication">"Aktion beenden mit"</string> @@ -709,8 +725,9 @@ <string name="volume_ringtone">"Klingeltonlautstärke"</string> <string name="volume_music">"Medienlautstärke"</string> <string name="volume_music_hint_playing_through_bluetooth">"Wiedergabe durch Bluetooth"</string> - <string name="volume_call">"Lautstärke bei eingehendem Anruf"</string> - <string name="volume_call_hint_playing_through_bluetooth">"Wiedergabe durch Bluetooth"</string> + <string name="volume_call">"Hörerlautstärke"</string> + <!-- no translation found for volume_bluetooth_call (2002891926351151534) --> + <skip /> <string name="volume_alarm">"Lautstärke für Alarm"</string> <string name="volume_notification">"Benachrichtigungslautstärke"</string> <string name="volume_unknown">"Lautstärke"</string> @@ -746,8 +763,61 @@ <string name="usb_storage_error_message">"Bei der Verwendung Ihrer SD-Karte als USB-Speicher ist ein Problem aufgetreten."</string> <string name="usb_storage_notification_title">"USB-Verbindung"</string> <string name="usb_storage_notification_message">"Wählen Sie die Dateien aus, die von Ihrem oder auf Ihren Computer kopiert werden sollen."</string> + <!-- no translation found for usb_storage_stop_notification_title (2336058396663516017) --> + <skip /> + <!-- no translation found for usb_storage_stop_notification_message (2591813490269841539) --> + <skip /> + <!-- no translation found for usb_storage_stop_title (6014127947456185321) --> + <skip /> + <!-- no translation found for usb_storage_stop_message (2390958966725232848) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_mount (1181858854166273345) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_unmount (3774611918660582898) --> + <skip /> + <!-- no translation found for usb_storage_stop_error_message (3917317248440072084) --> + <skip /> + <!-- no translation found for extmedia_format_title (8663247929551095854) --> + <skip /> + <!-- no translation found for extmedia_format_message (3621369962433523619) --> + <skip /> + <!-- no translation found for extmedia_format_button_format (4131064560127478695) --> + <skip /> <string name="select_input_method">"Eingabemethode auswählen"</string> <string name="fast_scroll_alphabet">"ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> <string name="fast_scroll_numeric_alphabet">"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> - <string name="candidates_style"><font fgcolor="#ff000000" bgcolor="#ff8080ff"><u>"Kandidaten"</u>"u>"</font></string> + <!-- no translation found for candidates_style (4333913089637062257) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_title (5457603418970994050) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_message (4747432538578886744) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_title (780477838241212997) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_message (1312266820092958014) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_title (6410723906019100189) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_message (2679412884290061775) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_title (6872152882604407837) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_message (7260183293747448241) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_title (6729801130790616200) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_message (7613960686747592770) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_title (8902518030404381318) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_message (4205117227342822275) --> + <skip /> + <!-- no translation found for activity_list_empty (4168820609403385789) --> + <skip /> + <!-- no translation found for permlab_pkgUsageStats (8787352074326748892) --> + <skip /> + <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> + <skip /> + <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> + <skip /> </resources> diff --git a/core/res/res/values-en-rSG/arrays.xml b/core/res/res/values-en-rSG/arrays.xml new file mode 100644 index 0000000..ee1e64b --- /dev/null +++ b/core/res/res/values-en-rSG/arrays.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/colors.xml +** +** Copyright 2006, Google Inc. +** +** 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. +*/ +--> +<resources> + + <!-- Do not translate. --> + <integer-array name="maps_starting_lat_lng"> + <item>1333333</item> + <item>103875000</item> + </integer-array> + <!-- Do not translate. --> + <integer-array name="maps_starting_zoom"> + <item>4</item> + </integer-array> + +</resources> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml deleted file mode 100644 index a2fd8d5..0000000 --- a/core/res/res/values-es-rUS/strings.xml +++ /dev/null @@ -1,904 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="byteShort">"b"</string> - <string name="kilobyteShort">"Kb"</string> - <string name="megabyteShort">"Mb"</string> - <string name="gigabyteShort">"Gb"</string> - <string name="terabyteShort">"Tb"</string> - <string name="petabyteShort">"Pb"</string> - <string name="untitled">"<sin título>"</string> - <string name="ellipsis">"…"</string> - <string name="emptyPhoneNumber">"(ningún número de teléfono)"</string> - <string name="unknownName">"(desconocido)"</string> - <string name="defaultVoiceMailAlphaTag">"Correo de voz"</string> - <string name="defaultMsisdnAlphaTag">"Msisdn1"</string> - <string name="mmiError">"Error de red o código MMI no válido."</string> - <string name="serviceEnabled">"Servicio habilitado"</string> - <string name="serviceEnabledFor">"Servicio habilitado para:"</string> - <string name="serviceDisabled">"Servicio deshabilitado"</string> - <string name="serviceRegistered">"Registro completado"</string> - <string name="serviceErased">"Borrado completado"</string> - <string name="passwordIncorrect">"Contraseña incorrecta"</string> - <string name="mmiComplete">"MMI completo"</string> - <!-- no translation found for badPin (5103184589972647739) --> - <skip /> - <!-- no translation found for badPuk (2200634943393540609) --> - <skip /> - <!-- no translation found for mismatchPin (5055729703806180857) --> - <skip /> - <!-- no translation found for invalidPin (6201854814319326475) --> - <skip /> - <!-- no translation found for needPuk (4788728144863892764) --> - <skip /> - <!-- no translation found for needPuk2 (7056908944942451033) --> - <skip /> - <string name="ClipMmi">"ID de llamada entrante"</string> - <string name="ClirMmi">"ID de llamada saliente"</string> - <string name="CfMmi">"Desvío de llamadas"</string> - <string name="CwMmi">"Llamada en espera"</string> - <string name="BaMmi">"Bloqueo de llamadas"</string> - <string name="PwdMmi">"Cambio de contraseña"</string> - <string name="PinMmi">"Cambio de PIN"</string> - <string name="CLIRDefaultOnNextCallOn">"El valor predeterminado de la restricción de ID es restringida. Próxima llamada: restringida"</string> - <string name="CLIRDefaultOnNextCallOff">"El valor predeterminado de la restricción de ID es restringida. Próxima llamada: no restringida"</string> - <string name="CLIRDefaultOffNextCallOn">"El valor predeterminado de la restricción de ID es no restringida. Próxima llamada: restringida"</string> - <string name="CLIRDefaultOffNextCallOff">"El valor predeterminado de la restricción de ID es no restringida. Próxima llamada: no restringida"</string> - <string name="serviceNotProvisioned">"Servicio no previsto."</string> - <string name="CLIRPermanent">"Restricción de ID prevista en el modo permanente."</string> - <string name="serviceClassVoice">"Voz"</string> - <string name="serviceClassData">"Datos"</string> - <string name="serviceClassFAX">"FAX"</string> - <string name="serviceClassSMS">"SMS"</string> - <string name="serviceClassDataAsync">"Asíncrono"</string> - <string name="serviceClassDataSync">"Sincronizar"</string> - <string name="serviceClassPacket">"Paquete"</string> - <string name="serviceClassPAD">"PAD"</string> - <string name="cfTemplateNotForwarded">"{0}: No desviada"</string> - <string name="cfTemplateForwarded">"{0}: {1}"</string> - <string name="cfTemplateForwardedTime">"{0}: {1} después de {2} segundos"</string> - <string name="cfTemplateRegistered">"{0}: No desviada ({1})"</string> - <string name="cfTemplateRegisteredTime">"{0}: No desviada ({1} después de {2} segundos)"</string> - <string name="httpErrorOk">"Aceptar"</string> - <string name="httpError">"Error desconocido"</string> - <string name="httpErrorLookup">"Host desconocido"</string> - <string name="httpErrorUnsupportedAuthScheme">"Esquema de autenticación no compatible. Fallo al autenticar."</string> - <string name="httpErrorAuth">"Error de autenticación"</string> - <string name="httpErrorProxyAuth">"Error de autenticación de servidor proxy"</string> - <string name="httpErrorConnect">"Fallo al conectar con el servidor"</string> - <string name="httpErrorIO">"Fallo al leer o escribir en el servidor"</string> - <string name="httpErrorTimeout">"Agotado el tiempo de espera de conexión con el servidor"</string> - <string name="httpErrorRedirectLoop">"Demasiadas redirecciones de servidor"</string> - <string name="httpErrorUnsupportedScheme">"Protocolo no compatible"</string> - <string name="httpErrorFailedSslHandshake">"Fallo al realizar SSL mutuo"</string> - <string name="httpErrorBadUrl">"URL no válida"</string> - <string name="httpErrorFile">"Error en el archivo"</string> - <string name="httpErrorFileNotFound">"Archivo no encontrado"</string> - <!-- no translation found for httpErrorTooManyRequests (3764334538393544875) --> - <skip /> - <string name="contentServiceSync">"Sincronizar"</string> - <string name="contentServiceSyncNotificationTitle">"Sincronizar"</string> - <!-- no translation found for contentServiceTooManyDeletesNotificationDesc (8477597194404210723) --> - <skip /> - <!-- no translation found for low_memory (4191592786596642367) --> - <skip /> - <!-- no translation found for me (4616693653158602117) --> - <skip /> - <string name="power_dialog">"Opciones de energía"</string> - <string name="silent_mode">"Modo silencioso"</string> - <string name="turn_on_radio">"Encender radio"</string> - <string name="turn_off_radio">"Apagar radio"</string> - <string name="screen_lock">"Bloquear"</string> - <string name="power_off">"Apagado"</string> - <!-- no translation found for shutdown_progress (3735034517335251808) --> - <skip /> - <!-- no translation found for shutdown_confirm (699224922526414097) --> - <skip /> - <string name="no_recent_tasks">"Ninguna aplicación reciente"</string> - <string name="global_actions">"Acciones globales"</string> - <string name="global_action_lock">"Bloquear"</string> - <string name="global_action_power_off">"Apagado"</string> - <string name="global_action_toggle_silent_mode">"Modo silencioso"</string> - <string name="global_action_silent_mode_on_status">"El sonido está desactivado"</string> - <string name="global_action_silent_mode_off_status">"El sonido está activado"</string> - <string name="safeMode">"Modo seguro"</string> - <!-- no translation found for permgrouplab_costMoney (904087853776533085) --> - <skip /> - <!-- no translation found for permgroupdesc_costMoney (4662370555643969515) --> - <skip /> - <!-- no translation found for permgrouplab_messages (2984053976424233925) --> - <skip /> - <!-- no translation found for permgroupdesc_messages (2129093134354989379) --> - <skip /> - <!-- no translation found for permgrouplab_personalInfo (4548406335021507392) --> - <skip /> - <!-- no translation found for permgroupdesc_personalInfo (8499310823817958034) --> - <skip /> - <!-- no translation found for permgrouplab_location (8535677827151907069) --> - <skip /> - <!-- no translation found for permgroupdesc_location (2341662219604651887) --> - <skip /> - <!-- no translation found for permgrouplab_network (3597781730625751831) --> - <skip /> - <!-- no translation found for permgroupdesc_network (8332572695347918340) --> - <skip /> - <!-- no translation found for permgrouplab_accounts (8631201594657951893) --> - <skip /> - <!-- no translation found for permgroupdesc_accounts (443982868906396781) --> - <skip /> - <!-- no translation found for permgrouplab_hardwareControls (5074512938567152139) --> - <skip /> - <!-- no translation found for permgroupdesc_hardwareControls (8772503144945278440) --> - <skip /> - <!-- no translation found for permgrouplab_phoneCalls (7096448531266882376) --> - <skip /> - <!-- no translation found for permgroupdesc_phoneCalls (6703873478653366233) --> - <skip /> - <!-- no translation found for permgrouplab_systemTools (1840847965111633430) --> - <skip /> - <!-- no translation found for permgroupdesc_systemTools (2810337951496685271) --> - <skip /> - <!-- no translation found for permgrouplab_developmentTools (692844635256963358) --> - <skip /> - <!-- no translation found for permgroupdesc_developmentTools (5253915519857796400) --> - <skip /> - <string name="permlab_statusBar">"Controlar la barra de estado"</string> - <string name="permdesc_statusBar">"Permite a las aplicaciones abrir, cerrar o deshabilitar la barra de estado y sus iconos."</string> - <!-- no translation found for permlab_expandStatusBar (6382500803293284173) --> - <skip /> - <!-- no translation found for permdesc_expandStatusBar (90953162060681436) --> - <skip /> - <!-- no translation found for permlab_processOutgoingCalls (786316295241100144) --> - <skip /> - <!-- no translation found for permdesc_processOutgoingCalls (1655242138991854396) --> - <skip /> - <string name="permlab_receiveSms">"Recibir mensajes SMS"</string> - <string name="permdesc_receiveSms">"Permite que la aplicación reciba y procese mensajes de texto. Una aplicación maliciosa puede controlar sus mensajes o eliminarlos sin que usted los vea."</string> - <string name="permlab_receiveMms">"Recibir mensajes MMS"</string> - <string name="permdesc_receiveMms">"Permite que la aplicación reciba y procese mensajes multimedia. Una aplicación maliciosa puede controlar sus mensajes o eliminarlos sin que usted los vea."</string> - <string name="permlab_sendSms">"Enviar mensajes SMS"</string> - <string name="permdesc_sendSms">"Permite a la aplicación enviar mensajes de texto. Una aplicación maliciosa puede hacerle gastar dinero enviando mensajes sin su confirmación."</string> - <string name="permlab_readSms">"Leer mensajes SMS/MMS"</string> - <string name="permdesc_readSms">"Permite que la aplicación lea los mensajes SMS almacenados en su teléfono o tarjeta SIM. Una aplicación maliciosa puede leer sus mensajes confidenciales."</string> - <string name="permlab_writeSms">"Escribir mensajes SMS/MMS"</string> - <string name="permdesc_writeSms">"Permite a la aplicación escribir mensajes SMS almacenados en su teléfono o tarjeta SIM. Una aplicación maliciosa puede eliminar sus mensajes."</string> - <string name="permlab_receiveWapPush">"Recibir mensajes WAP"</string> - <string name="permdesc_receiveWapPush">"Permite que la aplicación reciba y procese mensajes WAP. Una aplicación maliciosa puede controlar sus mensajes o eliminarlos sin que usted los vea."</string> - <string name="permlab_getTasks">"Obtener información de tareas"</string> - <string name="permdesc_getTasks">"Permite a la aplicación recuperar información sobre tareas que se acaban de ejecutar y tareas que se están ejecutando actualmente. Puede permitir que una aplicación maliciosa descubra información privada sobre otras aplicaciones."</string> - <string name="permlab_reorderTasks">"Reordenar tareas"</string> - <string name="permdesc_reorderTasks">"Permite que una aplicación mueva las tareas a primer plano y a segundo plano. Una aplicación maliciosa puede aparecer en primer plano sin su control."</string> - <string name="permlab_setDebugApp">"Establecer aplicación de depuración"</string> - <string name="permdesc_setDebugApp">"Permite a una aplicación activar la depuración para otra aplicación. Una aplicación maliciosa puede usar esta función para acabar con otras aplicaciones."</string> - <string name="permlab_changeConfiguration">"Cambiar configuración"</string> - <string name="permdesc_changeConfiguration">"Permite que una aplicación cambie la configuración actual, como la hora local o el tamaño general de la fuente."</string> - <!-- no translation found for permlab_restartPackages (5836367540766044606) --> - <skip /> - <!-- no translation found for permdesc_restartPackages (1764965996765573321) --> - <skip /> - <!-- no translation found for permlab_setProcessForeground (4860990420780868638) --> - <skip /> - <!-- no translation found for permdesc_setProcessForeground (3795477299954784360) --> - <skip /> - <string name="permlab_forceBack">"Restablecer"</string> - <string name="permdesc_forceBack">"Permite a la aplicación hacer que cualquier actividad que esté en primer plano se cierre y pase a segundo plano. Esta función no debería ser necesaria para las aplicaciones normales."</string> - <string name="permlab_dump">"Volcar estado del sistema"</string> - <string name="permdesc_dump">"Permite a la aplicación recuperar el estado interno del sistema. Una aplicación maliciosa puede recuperar una gran variedad de información privada y segura que normalmente no debería necesitar nunca."</string> - <string name="permlab_addSystemService">"Agregar servicio del sistema"</string> - <string name="permdesc_addSystemService">"Permite que la aplicación publique sus propios servicios de sistema de bajo nivel. Una aplicación maliciosa puede apropiarse del sistema y robar o dañar cualquier dato."</string> - <string name="permlab_runSetActivityWatcher">"Establecer vigilante de actividades"</string> - <string name="permdesc_runSetActivityWatcher">"Permite que una aplicación controle y supervise el modo en que el sistema inicia las actividades. Una aplicación maliciosa puede comprometer todo el sistema. Este permiso sólo es necesario para el desarrollo, nunca para el uso normal del dispositivo."</string> - <string name="permlab_broadcastPackageRemoved">"Paquete de emisión eliminado"</string> - <string name="permdesc_broadcastPackageRemoved">"Permite que una aplicación emita una notificación de que se ha eliminado un paquete de la aplicación. Una aplicación maliciosa puede usar esta función para acabar con cualquier otra aplicación que se esté ejecutando."</string> - <!-- no translation found for permlab_broadcastSmsReceived (1994692154847312518) --> - <skip /> - <!-- no translation found for permdesc_broadcastSmsReceived (6072362543164841432) --> - <skip /> - <!-- no translation found for permlab_broadcastWapPush (3070023012636951639) --> - <skip /> - <!-- no translation found for permdesc_broadcastWapPush (726912255218924336) --> - <skip /> - <string name="permlab_setProcessLimit">"Establecer límite de procesos"</string> - <string name="permdesc_setProcessLimit">"Permite a una aplicación controle el número máximo de procesos que se ejecutarán. Esta función no es necesaria para las aplicaciones normales."</string> - <string name="permlab_setAlwaysFinish">"Establecer finalizar siempre"</string> - <string name="permdesc_setAlwaysFinish">"Permite a una aplicación controlar si las actividades han acabado en cuanto pasan a segundo plano. Esta función no es necesaria para las aplicaciones normales."</string> - <string name="permlab_fotaUpdate">"Instalación de actualización del sistema"</string> - <string name="permdesc_fotaUpdate">"Permite a una aplicación recibir notifications about pending system updates and trigger their su instalación. Una aplicación maliciosa puede usar esta función para corromper el sistema con actualizaciones no autorizadas o interferir en el proceso de actualización."</string> - <!-- no translation found for permlab_batteryStats (1598947993704535568) --> - <skip /> - <!-- no translation found for permdesc_batteryStats (6247598531831307989) --> - <skip /> - <string name="permlab_internalSystemWindow">"Ventana de sistema interno"</string> - <string name="permdesc_internalSystemWindow">"Permite la creación de ventanas destinadas a ser usadas por la interfaz de usuario del sistema interno. No está destinada al uso por aplicaciones normales."</string> - <string name="permlab_systemAlertWindow">"Ventana de alerta del sistema"</string> - <string name="permdesc_systemAlertWindow">"Permite a una aplicación mostrar ventanas de alerta del sistema. Una aplicación maliciosa puede controlar toda la pantalla del dispositivo."</string> - <string name="permlab_setAnimationScale">"Establecer escala de animación"</string> - <string name="permdesc_setAnimationScale">"Permite que una aplicación cambie la velocidad de animación global (animaciones más rápidas o más lentas) en cualquier momento."</string> - <string name="permlab_manageAppTokens">"Administrar credenciales de aplicación"</string> - <string name="permdesc_manageAppTokens">"Permite a las aplicaciones crear y gestionar sus propios credenciales, saltándose su orden Z normal. Esta función no debería ser necesaria para las aplicaciones normales."</string> - <string name="permlab_injectEvents">"Introducir eventos de entrada"</string> - <string name="permdesc_injectEvents">"Permite a una aplicación ofrecer sus propios eventos de entrada (pulsaciones de teclas, etc.) a otras aplicaciones. Una aplicación maliciosa puede usar esta función para controlar el dispositivo."</string> - <string name="permlab_readInputState">"Leer estado de entrada"</string> - <string name="permdesc_readInputState">"Permite que las aplicaciones vigilen las teclas que pulsa incluso cuando interactúe con otra aplicación (como al introducir una contraseña). Esta función no debería ser necesaria para las aplicaciones normales."</string> - <string name="permlab_setOrientation">"Establecer orientación"</string> - <string name="permdesc_setOrientation">"Permite que una aplicación cambie la rotación de la pantalla en cualquier momento. No debería ser necesaria en aplicaciones normales."</string> - <string name="permlab_signalPersistentProcesses">"Señalar procesos persistentes"</string> - <string name="permdesc_signalPersistentProcesses">"Permite a la aplicación solicitar que la señal suministrada se envíe a todos los procesos persistentes."</string> - <string name="permlab_persistentActivity">"Actividades persistentes"</string> - <string name="permdesc_persistentActivity">"Permite que una aplicación convierta partes de sí misma en persistentes, de forma que el sistema no pueda usarlas para otras aplicaciones."</string> - <string name="permlab_deletePackages">"Borrar paquetes"</string> - <string name="permdesc_deletePackages">"Permite que una aplicación elimine paquetes Android. Una aplicación maliciosa puede usar esta función para eliminar importantes aplicaciones."</string> - <!-- no translation found for permlab_clearAppUserData (3858185484601410171) --> - <skip /> - <!-- no translation found for permdesc_clearAppUserData (7233537744753081136) --> - <skip /> - <string name="permlab_deleteCacheFiles">"Borrar archivos de caché"</string> - <string name="permdesc_deleteCacheFiles">"Permite que una aplicación elimine archivos de la caché."</string> - <!-- no translation found for permlab_getPackageSize (6743556676630447973) --> - <skip /> - <!-- no translation found for permdesc_getPackageSize (2893996655828539776) --> - <skip /> - <string name="permlab_installPackages">"Instalar paquetes"</string> - <string name="permdesc_installPackages">"Permite a una aplicación instalar paquetes Android nuevos o actualizados. Una aplicación maliciosa puede usar esta función para agregar nuevas aplicaciones con permisos arbitrariamente poderosos."</string> - <string name="permlab_clearAppCache">"Borrar datos de la caché de la aplicación"</string> - <string name="permdesc_clearAppCache">"Permite que una aplicación libere memoria de almacenamiento del dispositivo eliminando archivos del directorio caché de la aplicación. El acceso suele estar muy restringido al proceso del sistema."</string> - <!-- no translation found for permlab_readLogs (6653488552442991707) --> - <skip /> - <!-- no translation found for permdesc_readLogs (356352685800884319) --> - <skip /> - <!-- no translation found for permlab_diagnostic (2955142476313469329) --> - <skip /> - <!-- no translation found for permdesc_diagnostic (1282409892215520166) --> - <skip /> - <string name="permlab_changeComponentState">"Habilitar o deshabilitar componentes de la aplicación"</string> - <string name="permdesc_changeComponentState">"Permite que una aplicación cambie si un componente de otra aplicación está habilitado o no. Una aplicación maliciosa puede usar esta función para deshabilitar importantes capacidades del dispositivo. Se debe tener cuidado con el permiso, ya que los componentes de la aplicación se pueden volver inestables o inconsistentes."</string> - <string name="permlab_setPreferredApplications">"Establecer aplicaciones preferidas"</string> - <string name="permdesc_setPreferredApplications">"Permite que una aplicación modifique sus aplicaciones preferidas. Esta función puede permitir que una aplicación maliciosa cambie silenciosamente las aplicaciones que se están ejecutando y haga que las aplicaciones existentes recopilen datos privados."</string> - <string name="permlab_writeSettings">"Escribir configuración del sistema"</string> - <string name="permdesc_writeSettings">"Permite a una aplicación modificar los datos de configuración del sistema. Una aplicación maliciosa puede corromper la configuración del sistema."</string> - <!-- no translation found for permlab_writeSecureSettings (4851801872124242319) --> - <skip /> - <!-- no translation found for permdesc_writeSecureSettings (2080620249472761366) --> - <skip /> - <!-- no translation found for permlab_writeGservices (296370685945777755) --> - <skip /> - <!-- no translation found for permdesc_writeGservices (2496928471286495053) --> - <skip /> - <string name="permlab_receiveBootCompleted">"Ejecutar al arrancar"</string> - <string name="permdesc_receiveBootCompleted">"Permite que una aplicación se inicie en cuanto el sistema haya terminado de arrancar. Esto puede hacer que el dispositivo tarde más en iniciarse y que la aplicación ralentice todo el dispositivo al ejecutarse siempre."</string> - <string name="permlab_broadcastSticky">"Intento de emisión permanente"</string> - <string name="permdesc_broadcastSticky">"Permite que una aplicación envíe emisiones continuas, que permanecen una vez que termina la emisión. Una aplicación maliciosa puede hacer que el dispositivo sea lento o inestable y use demasiada memoria."</string> - <string name="permlab_readContacts">"Leer datos de contacto"</string> - <string name="permdesc_readContacts">"Permite que una aplicación lea todos los datos (de dirección) de contacto almacenados en su dispositivo. Una aplicación maliciosa puede usar esta función para enviar sus datos a otras personas."</string> - <string name="permlab_writeContacts">"Escribir datos de contactos"</string> - <string name="permdesc_writeContacts">"Permite a una aplicación modificar los datos (de dirección) de contacto almacenados en su dispositivo. Una aplicación maliciosa puede usar esta función para borrar o modificar sus datos de contacto."</string> - <!-- no translation found for permlab_writeOwnerData (8036840529708535113) --> - <skip /> - <!-- no translation found for permdesc_writeOwnerData (5873447528845878348) --> - <skip /> - <!-- no translation found for permlab_readOwnerData (1847040178513733757) --> - <skip /> - <!-- no translation found for permdesc_readOwnerData (7563299529149214764) --> - <skip /> - <!-- no translation found for permlab_readCalendar (2111238731453410895) --> - <skip /> - <!-- no translation found for permdesc_readCalendar (4408253940601239114) --> - <skip /> - <!-- no translation found for permlab_writeCalendar (7518052789370653396) --> - <skip /> - <!-- no translation found for permdesc_writeCalendar (8057304232140147596) --> - <skip /> - <!-- no translation found for permlab_accessMockLocation (321094551062270213) --> - <skip /> - <!-- no translation found for permdesc_accessMockLocation (3651565866471419739) --> - <skip /> - <!-- no translation found for permlab_accessLocationExtraCommands (8291822077788811687) --> - <skip /> - <!-- no translation found for permdesc_accessLocationExtraCommands (5135782633548630731) --> - <skip /> - <string name="permlab_accessFineLocation">"Acceder a ubicación de GPS"</string> - <string name="permdesc_accessFineLocation">"Acceda al sistema de posicionamiento global (GPS) del dispositivo, en caso de que esté disponible. Una aplicación maliciosa puede usar esta función para determinar dónde está y puede consumir batería adicional."</string> - <string name="permlab_accessCoarseLocation">"Acceder a ubicación de red"</string> - <string name="permdesc_accessCoarseLocation">"Utilice la base de datos de la red para determinar una ubicación aproximada del dispositivo, en caso de que esté disponible. Una aplicación maliciosa puede usar esta opción para determinar aproximadamente dónde está."</string> - <string name="permlab_accessSurfaceFlinger">"Acceder a SurfaceFlinger"</string> - <string name="permdesc_accessSurfaceFlinger">"Permite que la aplicación use las características de bajo nivel de SurfaceFlinger."</string> - <string name="permlab_readFrameBuffer">"Leer búfer de trama"</string> - <string name="permdesc_readFrameBuffer">"Permite que la aplicación lea el contenido del búfer de trama."</string> - <string name="permlab_modifyAudioSettings">"Modificar configuración de audio"</string> - <string name="permdesc_modifyAudioSettings">"Permite que la aplicación modifique la configuración de audio global, como el volumen y el enrutamiento."</string> - <string name="permlab_recordAudio">"Grabar audio"</string> - <string name="permdesc_recordAudio">"Permite que la aplicación acceda a la ruta de grabación de audio."</string> - <string name="permlab_camera">"Cámara"</string> - <string name="permdesc_camera">"Permite que la aplicación saque fotos con la cámara. Esto permite que la aplicación capture en cualquier momento imágenes que la cámara esté viendo."</string> - <string name="permlab_brick">"Deshabilitar dispositivo"</string> - <string name="permdesc_brick">"Permite que la aplicación deshabilite de forma permanente todo el dispositivo. Esto es muy peligroso."</string> - <!-- no translation found for permlab_reboot (8844650672567077423) --> - <skip /> - <!-- no translation found for permdesc_reboot (4704919552870918328) --> - <skip /> - <string name="permlab_mount_unmount_filesystems">"Montar y desmontar archivos del sistema"</string> - <string name="permdesc_mount_unmount_filesystems">"Permite que la aplicación monte y desmonte archivos del sistema para el almacenamiento extraíble."</string> - <string name="permlab_vibrate">"Vibrador"</string> - <string name="permdesc_vibrate">"Permite a la aplicación controlar el vibrador."</string> - <string name="permlab_flashlight">"Linterna"</string> - <string name="permdesc_flashlight">"Permite a la aplicación controlar la linterna."</string> - <string name="permlab_hardware_test">"Prueba de hardware"</string> - <string name="permdesc_hardware_test">"Permite a la aplicación controlar distintos periféricos para realizar una prueba de hardware."</string> - <string name="permlab_callPhone">"Llamar a números de teléfono"</string> - <string name="permdesc_callPhone">"Permite que la aplicación llame a los números de teléfono sin que usted intervenga. Una aplicación maliciosa puede hacer que aparezcan llamadas inesperadas en su factura del teléfono."</string> - <!-- no translation found for permlab_callPrivileged (2166923597287697159) --> - <skip /> - <!-- no translation found for permdesc_callPrivileged (5109789447971735501) --> - <skip /> - <!-- no translation found for permlab_locationUpdates (4216418293360456836) --> - <skip /> - <!-- no translation found for permdesc_locationUpdates (7635814693478743648) --> - <skip /> - <!-- no translation found for permlab_checkinProperties (2260796787386280708) --> - <skip /> - <!-- no translation found for permdesc_checkinProperties (3508022022841741945) --> - <skip /> - <!-- no translation found for permlab_modifyPhoneState (7791696535097912313) --> - <skip /> - <!-- no translation found for permdesc_modifyPhoneState (6352405226410454770) --> - <skip /> - <!-- no translation found for permlab_readPhoneState (7320082586621086653) --> - <skip /> - <!-- no translation found for permdesc_readPhoneState (8004450067066407969) --> - <skip /> - <!-- no translation found for permlab_wakeLock (1591164750935072136) --> - <skip /> - <!-- no translation found for permdesc_wakeLock (160471538196734936) --> - <skip /> - <string name="permlab_devicePower">"Alimentación del dispositivo"</string> - <string name="permdesc_devicePower">"Permite a la aplicación encender o apagar el dispositivo o mantenerlo encendido."</string> - <string name="permlab_factoryTest">"Prueba de fábrica"</string> - <string name="permdesc_factoryTest">"Se ejecuta como una prueba de fabricante de bajo nivel, permitiendo el acceso completo al hardware del dispositivo. Sólo disponible cuando un dispositivo se ejecuta en el modo de prueba del fabricante."</string> - <string name="permlab_setWallpaper">"Establecer fondo de escritorio"</string> - <string name="permdesc_setWallpaper">"Permite a la aplicación establecer el fondo de escritorio del sistema."</string> - <!-- no translation found for permlab_setWallpaperHints (4192438316932517807) --> - <skip /> - <!-- no translation found for permdesc_setWallpaperHints (738757439960921674) --> - <skip /> - <string name="permlab_masterClear">"Restablecimiento de todo el sistema"</string> - <string name="permdesc_masterClear">"Permite que una aplicación restablezca por completo los valores de fábrica del sistema, borrando todos los datos, la configuración y las aplicaciones instaladas."</string> - <!-- no translation found for permlab_setTimeZone (477196167239548690) --> - <skip /> - <!-- no translation found for permdesc_setTimeZone (8564892020460841198) --> - <skip /> - <!-- no translation found for permlab_getAccounts (2764070033402295170) --> - <skip /> - <!-- no translation found for permdesc_getAccounts (1203491378748649898) --> - <skip /> - <!-- no translation found for permlab_accessNetworkState (2032916924886010827) --> - <skip /> - <!-- no translation found for permdesc_accessNetworkState (7081329402551195933) --> - <skip /> - <!-- no translation found for permlab_createNetworkSockets (4706698319966917864) --> - <skip /> - <!-- no translation found for permdesc_createNetworkSockets (2580337178778551792) --> - <skip /> - <!-- no translation found for permlab_writeApnSettings (3190585220761979369) --> - <skip /> - <!-- no translation found for permdesc_writeApnSettings (4093875220468761052) --> - <skip /> - <!-- no translation found for permlab_changeNetworkState (2710779001260856872) --> - <skip /> - <!-- no translation found for permdesc_changeNetworkState (8076109230787022270) --> - <skip /> - <!-- no translation found for permlab_accessWifiState (3613679494230374297) --> - <skip /> - <!-- no translation found for permdesc_accessWifiState (8226508433563326925) --> - <skip /> - <!-- no translation found for permlab_changeWifiState (6043889338995432957) --> - <skip /> - <!-- no translation found for permdesc_changeWifiState (7829372845909567994) --> - <skip /> - <!-- no translation found for permlab_bluetoothAdmin (5513286736585647334) --> - <skip /> - <!-- no translation found for permdesc_bluetoothAdmin (1838208497914347365) --> - <skip /> - <!-- no translation found for permlab_bluetooth (6378797624765639115) --> - <skip /> - <!-- no translation found for permdesc_bluetooth (8592386018922265273) --> - <skip /> - <!-- no translation found for permlab_disableKeyguard (4574886811903233903) --> - <skip /> - <!-- no translation found for permdesc_disableKeyguard (815972646344251271) --> - <skip /> - <!-- no translation found for permlab_readSyncSettings (8818819977141505127) --> - <skip /> - <!-- no translation found for permdesc_readSyncSettings (8454705401908767847) --> - <skip /> - <!-- no translation found for permlab_writeSyncSettings (4514911143753152941) --> - <skip /> - <!-- no translation found for permdesc_writeSyncSettings (7630627689635091836) --> - <skip /> - <!-- no translation found for permlab_readSyncStats (5748337739678952863) --> - <skip /> - <!-- no translation found for permdesc_readSyncStats (582551457321957183) --> - <skip /> - <!-- no translation found for permlab_subscribedFeedsRead (2043206814904506589) --> - <skip /> - <!-- no translation found for permdesc_subscribedFeedsRead (6977343942680042449) --> - <skip /> - <!-- no translation found for permlab_subscribedFeedsWrite (2556727307229571556) --> - <skip /> - <!-- no translation found for permdesc_subscribedFeedsWrite (4134783294590266220) --> - <skip /> - <!-- no translation found for phoneTypes:0 (6070018634209800981) --> - <!-- no translation found for phoneTypes:1 (1514509689885965711) --> - <!-- no translation found for phoneTypes:2 (497473201754095234) --> - <!-- no translation found for phoneTypes:3 (5554432614281047787) --> - <!-- no translation found for phoneTypes:4 (2222084401110150993) --> - <!-- no translation found for phoneTypes:5 (2290007103906353121) --> - <!-- no translation found for phoneTypes:6 (6930783706213719251) --> - <!-- no translation found for phoneTypes:7 (1326005699931077792) --> - <!-- no translation found for emailAddressTypes:0 (1540640638077615417) --> - <!-- no translation found for emailAddressTypes:1 (4252853367575831977) --> - <!-- no translation found for emailAddressTypes:2 (7158046581744435718) --> - <!-- no translation found for emailAddressTypes:3 (3625034471181268169) --> - <!-- no translation found for postalAddressTypes:0 (5732960259696659380) --> - <!-- no translation found for postalAddressTypes:1 (7132240704786130285) --> - <!-- no translation found for postalAddressTypes:2 (1317604357745852817) --> - <!-- no translation found for postalAddressTypes:3 (1582953598462826702) --> - <!-- no translation found for imAddressTypes:0 (7806620012096518833) --> - <!-- no translation found for imAddressTypes:1 (5748846799950672787) --> - <!-- no translation found for imAddressTypes:2 (6196536810275073680) --> - <!-- no translation found for imAddressTypes:3 (8519128375350623648) --> - <!-- no translation found for organizationTypes:0 (1299224825223821142) --> - <!-- no translation found for organizationTypes:1 (2455717447227299354) --> - <!-- no translation found for organizationTypes:2 (7027570839313438290) --> - <!-- no translation found for imProtocols:0 (3318725788774688043) --> - <!-- no translation found for imProtocols:1 (1787713387022932886) --> - <!-- no translation found for imProtocols:2 (6751174158442316516) --> - <!-- no translation found for imProtocols:3 (1151283347465052653) --> - <!-- no translation found for imProtocols:4 (2157980008878817934) --> - <!-- no translation found for imProtocols:5 (7836237460308230767) --> - <!-- no translation found for imProtocols:6 (1180789904462172516) --> - <!-- no translation found for imProtocols:7 (21955111672779862) --> - <string name="keyguard_password_enter_pin_code">"Introducir código PIN"</string> - <string name="keyguard_password_wrong_pin_code">"¡Código PIN incorrecto!"</string> - <string name="keyguard_label_text">"Para desbloquear, pulse Menú y luego 0."</string> - <string name="emergency_call_dialog_number_for_display">"Número de emergencia"</string> - <string name="lockscreen_carrier_default">"(Fuera de servicio)"</string> - <string name="lockscreen_screen_locked">"Pantalla bloqueada"</string> - <string name="lockscreen_instructions_when_pattern_enabled">"Pulse Menú para desbloquear o realice una llamada de emergencia"</string> - <string name="lockscreen_instructions_when_pattern_disabled">"Pulse Menú para desbloquear"</string> - <string name="lockscreen_pattern_instructions">"Trazar patrón de desbloqueo"</string> - <string name="lockscreen_emergency_call">"Llamada de emergencia"</string> - <string name="lockscreen_pattern_correct">"¡Correcto!"</string> - <string name="lockscreen_pattern_wrong">"¡Patrón incorrecto! Inténtelo de nuevo"</string> - <string name="lockscreen_plugged_in">"Cargando (<xliff:g id="NUMBER">%d%%</xliff:g>)"</string> - <string name="lockscreen_low_battery">"Conectar cargador"</string> - <!-- no translation found for lockscreen_missing_sim_message_short (5051192587315492957) --> - <skip /> - <string name="lockscreen_missing_sim_message">"No hay ninguna tarjeta SIM en el dispositivo"</string> - <string name="lockscreen_missing_sim_instructions">"Inserte una tarjeta SIM"</string> - <!-- no translation found for lockscreen_network_locked_message (323609607922245071) --> - <skip /> - <!-- no translation found for lockscreen_sim_puk_locked_message (1005803622871256359) --> - <skip /> - <!-- no translation found for lockscreen_sim_puk_locked_instructions (5033160098036646955) --> - <skip /> - <!-- no translation found for lockscreen_sim_locked_message (7398401200962556379) --> - <skip /> - <!-- no translation found for lockscreen_sim_unlock_progress_dialog_message (5939537246164692076) --> - <skip /> - <!-- unknown placeholder BREAK in lockscreen_too_many_failed_attempts_dialog_message --> - <skip /> - <!-- no translation found for lockscreen_failed_attempts_almost_glogin (1569017295989454551) --> - <skip /> - <string name="lockscreen_too_many_failed_attempts_countdown">"Inténtelo de nuevo en <xliff:g id="NUMBER">%d</xliff:g> segundos"</string> - <!-- no translation found for lockscreen_forgot_pattern_button_text (4219994639843985488) --> - <skip /> - <!-- no translation found for lockscreen_glogin_too_many_attempts (7504679498838839295) --> - <skip /> - <!-- no translation found for lockscreen_glogin_instructions (6542400673357252011) --> - <skip /> - <!-- no translation found for lockscreen_glogin_username_hint (6378418320242015111) --> - <skip /> - <!-- no translation found for lockscreen_glogin_password_hint (3224230234042131153) --> - <skip /> - <!-- no translation found for lockscreen_glogin_submit_button (5562051040043760034) --> - <skip /> - <!-- no translation found for lockscreen_glogin_invalid_input (4881057177478491580) --> - <skip /> - <!-- unknown placeholder FORMAT in status_bar_time_format --> - <skip /> - <!-- no translation found for hour_minute_ampm (1850330605794978742) --> - <skip /> - <!-- no translation found for hour_minute_cap_ampm (1122840227537374196) --> - <skip /> - <!-- no translation found for hour_ampm (7665432130905376251) --> - <skip /> - <!-- no translation found for hour_cap_ampm (3600295014648400268) --> - <skip /> - <!-- no translation found for status_bar_clear_all_button (2202004591253243750) --> - <skip /> - <string name="status_bar_no_notifications_title">"Notificaciones"</string> - <string name="status_bar_ongoing_events_title">"En curso"</string> - <string name="status_bar_latest_events_title">"Últimos eventos"</string> - <string name="battery_status_text_percent_format">"<xliff:g id="NUMBER">%d</xliff:g>"</string> - <string name="battery_status_charging">"Cargando…"</string> - <string name="battery_low_title">"Conectar cargador"</string> - <string name="battery_low_subtitle">"Batería baja"</string> - <string name="battery_low_percent_format">"Menos de <xliff:g id="NUMBER">%d%%</xliff:g> restantes"</string> - <string name="factorytest_failed">"Fallo al realizar la prueba de fábrica"</string> - <string name="factorytest_not_system">"La acción FACTORY_TEST sólo es compatible con los paquetes instalados en /system/app."</string> - <string name="factorytest_no_action">"No se ha encontrado ningún paquete que proporcione la acción FACTORY_TEST."</string> - <string name="factorytest_reboot">"Reiniciar"</string> - <string name="save_password_label">"Confirmar"</string> - <string name="save_password_message">"¿Quiere que el explorador recuerde esta contraseña?"</string> - <string name="save_password_notnow">"Ahora no"</string> - <string name="save_password_remember">"Recordar"</string> - <string name="save_password_never">"Nunca"</string> - <string name="open_permission_deny">"No tiene permiso para abrir esta página."</string> - <!-- no translation found for text_copied (6106873823411904723) --> - <skip /> - <string name="more_item_label">"Más"</string> - <string name="prepend_shortcut_label">"Menú+"</string> - <!-- no translation found for menu_space_shortcut_label (194586306440382711) --> - <skip /> - <!-- no translation found for menu_enter_shortcut_label (7214761412193519345) --> - <skip /> - <!-- no translation found for menu_delete_shortcut_label (2854936426194985313) --> - <skip /> - <string name="search_go">"IR"</string> - <string name="today">"Hoy"</string> - <string name="yesterday">"Ayer"</string> - <string name="tomorrow">"Mañana"</string> - <!-- no translation found for oneMonthDurationPast (3402179395240209557) --> - <skip /> - <!-- no translation found for beforeOneMonthDurationPast (7578100953282866827) --> - <skip /> - <!-- no translation found for num_seconds_ago:one (7416512229671810725) --> - <!-- no translation found for num_seconds_ago:other (8138756910300398447) --> - <!-- no translation found for num_minutes_ago:one (8620869479299420562) --> - <!-- no translation found for num_minutes_ago:other (5065488162050522741) --> - <!-- no translation found for num_hours_ago:one (853404611989669641) --> - <!-- no translation found for num_hours_ago:other (3558873784561756849) --> - <!-- no translation found for num_days_ago:one (4222479980812128212) --> - <!-- no translation found for num_days_ago:other (5445701370433601703) --> - <!-- no translation found for in_num_seconds:one (4253290037777327003) --> - <!-- no translation found for in_num_seconds:other (1280033870920841404) --> - <!-- no translation found for in_num_minutes:one (1487585791027953091) --> - <!-- no translation found for in_num_minutes:other (6274204576475209932) --> - <!-- no translation found for in_num_hours:one (6501470863235186391) --> - <!-- no translation found for in_num_hours:other (4415358752953289251) --> - <!-- no translation found for in_num_days:one (5608475533104443893) --> - <!-- no translation found for in_num_days:other (3827193006163842267) --> - <string name="preposition_for_date">"en %s"</string> - <string name="preposition_for_time">"en %s"</string> - <string name="preposition_for_year">"en %s"</string> - <string name="day">"día"</string> - <string name="days">"días"</string> - <string name="hour">"hora"</string> - <string name="hours">"horas"</string> - <string name="minute">"minuto"</string> - <string name="minutes">"minutos"</string> - <string name="second">"segundo"</string> - <string name="seconds">"segundos"</string> - <string name="week">"semana"</string> - <string name="weeks">"semanas"</string> - <!-- no translation found for year (8024790425994085153) --> - <skip /> - <!-- no translation found for years (8592090054773244417) --> - <skip /> - <string name="sunday">"Domingo"</string> - <string name="monday">"Lunes"</string> - <string name="tuesday">"Martes"</string> - <string name="wednesday">"Miércoles"</string> - <string name="thursday">"Jueves"</string> - <string name="friday">"Viernes"</string> - <string name="saturday">"Sábado"</string> - <string name="every_weekday">"Todos los días laborables (Lun–Vie)"</string> - <string name="daily">"Diario"</string> - <string name="weekly">"Semanal en <xliff:g id="DAY">%s</xliff:g>"</string> - <string name="monthly">"Mensual"</string> - <string name="yearly">"Anual"</string> - <string name="VideoView_error_title">"Error de reproducción del vídeo"</string> - <string name="VideoView_error_text_unknown">"Se ha producido un error al reproducir el vídeo seleccionado."</string> - <string name="VideoView_error_button">"Aceptar"</string> - <string name="am">"AM"</string> - <string name="pm">"PM"</string> - <!-- unknown placeholder FORMAT in numeric_date --> - <skip /> - <!-- unknown placeholder FORMAT in wday1_date1_time1_wday2_date2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in wday1_date1_wday2_date2 --> - <skip /> - <!-- unknown placeholder FORMAT in date1_time1_date2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in date1_date2 --> - <skip /> - <!-- unknown placeholder FORMAT in time1_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in time_wday_date --> - <skip /> - <!-- unknown placeholder FORMAT in wday_date --> - <skip /> - <!-- unknown placeholder FORMAT in time_date --> - <skip /> - <!-- unknown placeholder FORMAT in time_wday --> - <skip /> - <!-- no translation found for full_date_month_first (6011143962222283357) --> - <skip /> - <!-- no translation found for full_date_day_first (8621594762705478189) --> - <skip /> - <!-- no translation found for medium_date_month_first (48990963718825728) --> - <skip /> - <!-- no translation found for medium_date_day_first (2898992016440387123) --> - <skip /> - <!-- no translation found for twelve_hour_time_format (6015557937879492156) --> - <skip /> - <!-- no translation found for twenty_four_hour_time_format (5176807998669709535) --> - <skip /> - <string name="noon">"mediodía"</string> - <string name="Noon">"Mediodía"</string> - <string name="midnight">"media noche"</string> - <string name="Midnight">"Media noche"</string> - <!-- unknown placeholder FORMAT in month_day --> - <skip /> - <!-- unknown placeholder FORMAT in month --> - <skip /> - <!-- unknown placeholder FORMAT in month_day_year --> - <skip /> - <!-- unknown placeholder FORMAT in month_year --> - <skip /> - <!-- no translation found for time_of_day (8375993139317154157) --> - <skip /> - <!-- no translation found for date_and_time (9197690194373107109) --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_md1_md2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_wday1_md1_wday2_md2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_mdy1_mdy2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_wday1_mdy1_wday2_mdy2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_md1_time1_md2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_wday1_md1_time1_wday2_md2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_mdy1_time1_mdy2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_wday1_mdy1_time1_wday2_mdy2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_md1_md2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_wday1_md1_wday2_md2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_mdy1_mdy2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_wday1_mdy1_wday2_mdy2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_md1_time1_md2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_wday1_md1_time1_wday2_md2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_mdy1_time1_mdy2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_wday1_mdy1_time1_wday2_mdy2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_md1_md2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_wday1_md1_wday2_md2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_mdy1_mdy2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_wday1_mdy1_wday2_mdy2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_md1_time1_md2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_wday1_md1_time1_wday2_md2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_mdy1_time1_mdy2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_wday1_mdy1_time1_wday2_mdy2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in abbrev_month_day_year --> - <skip /> - <!-- unknown placeholder FORMAT in abbrev_month_year --> - <skip /> - <!-- unknown placeholder FORMAT in abbrev_month_day --> - <skip /> - <!-- unknown placeholder FORMAT in abbrev_month --> - <skip /> - <string name="day_of_week_long_sunday">"Domingo"</string> - <string name="day_of_week_long_monday">"Lunes"</string> - <string name="day_of_week_long_tuesday">"Martes"</string> - <string name="day_of_week_long_wednesday">"Miércoles"</string> - <string name="day_of_week_long_thursday">"Jueves"</string> - <string name="day_of_week_long_friday">"Viernes"</string> - <string name="day_of_week_long_saturday">"Sábado"</string> - <string name="day_of_week_medium_sunday">"Dom"</string> - <string name="day_of_week_medium_monday">"Lun"</string> - <string name="day_of_week_medium_tuesday">"Mar"</string> - <string name="day_of_week_medium_wednesday">"Mié"</string> - <string name="day_of_week_medium_thursday">"Jue"</string> - <string name="day_of_week_medium_friday">"Vie"</string> - <string name="day_of_week_medium_saturday">"Sáb"</string> - <string name="day_of_week_short_sunday">"Do"</string> - <string name="day_of_week_short_monday">"Lu"</string> - <string name="day_of_week_short_tuesday">"Ma"</string> - <string name="day_of_week_short_wednesday">"Mi"</string> - <string name="day_of_week_short_thursday">"Ju"</string> - <string name="day_of_week_short_friday">"Vi"</string> - <string name="day_of_week_short_saturday">"Sá"</string> - <string name="day_of_week_shorter_sunday">"D"</string> - <string name="day_of_week_shorter_monday">"L"</string> - <string name="day_of_week_shorter_tuesday">"M"</string> - <string name="day_of_week_shorter_wednesday">"X"</string> - <string name="day_of_week_shorter_thursday">"J"</string> - <string name="day_of_week_shorter_friday">"V"</string> - <string name="day_of_week_shorter_saturday">"S"</string> - <string name="day_of_week_shortest_sunday">"D"</string> - <string name="day_of_week_shortest_monday">"L"</string> - <string name="day_of_week_shortest_tuesday">"M"</string> - <string name="day_of_week_shortest_wednesday">"X"</string> - <string name="day_of_week_shortest_thursday">"J"</string> - <string name="day_of_week_shortest_friday">"V"</string> - <string name="day_of_week_shortest_saturday">"S"</string> - <string name="month_long_january">"Enero"</string> - <string name="month_long_february">"Febrero"</string> - <string name="month_long_march">"Marzo"</string> - <string name="month_long_april">"Abril"</string> - <string name="month_long_may">"Mayo"</string> - <string name="month_long_june">"Junio"</string> - <string name="month_long_july">"Julio"</string> - <string name="month_long_august">"Agosto"</string> - <string name="month_long_september">"Septiembre"</string> - <string name="month_long_october">"Octubre"</string> - <string name="month_long_november">"Noviembre"</string> - <string name="month_long_december">"Diciembre"</string> - <string name="month_medium_january">"Ene"</string> - <string name="month_medium_february">"Feb"</string> - <string name="month_medium_march">"Mar"</string> - <string name="month_medium_april">"Abr"</string> - <string name="month_medium_may">"May"</string> - <string name="month_medium_june">"Jun"</string> - <string name="month_medium_july">"Jul"</string> - <string name="month_medium_august">"Ago"</string> - <string name="month_medium_september">"Sep"</string> - <string name="month_medium_october">"Oct"</string> - <string name="month_medium_november">"Nov"</string> - <string name="month_medium_december">"Dic"</string> - <string name="month_shortest_january">"E"</string> - <string name="month_shortest_february">"F"</string> - <string name="month_shortest_march">"M"</string> - <string name="month_shortest_april">"A"</string> - <string name="month_shortest_may">"M"</string> - <string name="month_shortest_june">"E"</string> - <string name="month_shortest_july">"E"</string> - <string name="month_shortest_august">"A"</string> - <string name="month_shortest_september">"S"</string> - <string name="month_shortest_october">"O"</string> - <string name="month_shortest_november">"N"</string> - <string name="month_shortest_december">"D"</string> - <!-- unknown placeholder FORMAT in elapsed_time_short_format_mm_ss --> - <skip /> - <!-- unknown placeholder FORMAT in elapsed_time_short_format_h_mm_ss --> - <skip /> - <string name="selectAll">"Seleccionar todo"</string> - <string name="cut">"Cortar"</string> - <!-- no translation found for cutAll (4474519683293791451) --> - <skip /> - <string name="copy">"Copiar"</string> - <!-- no translation found for copyAll (4777548804630476932) --> - <skip /> - <string name="paste">"Pegar"</string> - <string name="copyUrl">"Copiar URL"</string> - <!-- no translation found for inputMethod (7911866729148111492) --> - <skip /> - <!-- no translation found for editTextMenuTitle (3984253728638788023) --> - <skip /> - <string name="low_internal_storage_view_title">"Espacio de almacenamiento interno bajo"</string> - <string name="low_internal_storage_view_text">"El dispositivo se está quedando sin espacio de almacenamiento interno"</string> - <string name="ok">"Aceptar"</string> - <string name="cancel">"Cancelar"</string> - <string name="yes">"Aceptar"</string> - <string name="no">"Cancelar"</string> - <string name="capital_on">"Activar"</string> - <string name="capital_off">"Desactivar"</string> - <string name="whichApplication">"¿Qué aplicación desea usar?"</string> - <string name="alwaysUse">"Usar siempre esta aplicación para esta actividad"</string> - <!-- no translation found for clearDefaultHintMsg (5742432113023174321) --> - <skip /> - <string name="chooseActivity">"Seleccionar una acción"</string> - <string name="noApplications">"No hay ninguna aplicación disponible para llevar a cabo la acción"</string> - <!-- no translation found for aerr_title (2654390351574026098) --> - <skip /> - <!-- no translation found for aerr_application (4917288809565116720) --> - <skip /> - <!-- no translation found for aerr_process (1273819861108073461) --> - <skip /> - <!-- no translation found for anr_title (3305935690891435915) --> - <skip /> - <!-- no translation found for anr_activity_application (1653036325679156678) --> - <skip /> - <!-- no translation found for anr_activity_process (2674027618362070465) --> - <skip /> - <!-- no translation found for anr_application_process (2163656674970221928) --> - <skip /> - <!-- no translation found for anr_process (7747550780123472160) --> - <skip /> - <!-- no translation found for force_close (9020954128872810669) --> - <skip /> - <!-- no translation found for wait (7973775702304037058) --> - <skip /> - <!-- no translation found for debug (857932504764728770) --> - <skip /> - <string name="sendText">"Seleccionar qué hacer con el texto"</string> - <!-- no translation found for volume_ringtone (4121694816346562058) --> - <skip /> - <!-- no translation found for volume_music (4869950240104717493) --> - <skip /> - <!-- no translation found for volume_call (5723421277753250395) --> - <skip /> - <!-- no translation found for volume_alarm (2752102730973081294) --> - <skip /> - <!-- no translation found for volume_unknown (6908187627672375742) --> - <skip /> - <!-- no translation found for ringtone_default (2873893375149093475) --> - <skip /> - <!-- no translation found for ringtone_default_with_actual (5474076151665761913) --> - <skip /> - <!-- no translation found for ringtone_silent (7477159279081654685) --> - <skip /> - <!-- no translation found for ringtone_picker_title (7055241890764367884) --> - <skip /> - <!-- no translation found for ringtone_unknown (6888219771401173795) --> - <skip /> - <!-- no translation found for wifi_available:one (8168012881468888470) --> - <!-- no translation found for wifi_available:other (4666122955807117718) --> - <!-- no translation found for wifi_available_detailed:one (5107769161192143259) --> - <!-- no translation found for wifi_available_detailed:other (853347657960575809) --> - <!-- no translation found for select_character (3735110139249491726) --> - <skip /> - <!-- no translation found for sms_control_default_app_name (7522184737840550841) --> - <skip /> - <!-- no translation found for sms_control_title (2742400596989418394) --> - <skip /> - <!-- no translation found for sms_control_message (3447126217666595989) --> - <skip /> - <!-- no translation found for sms_control_yes (8839660939359273650) --> - <skip /> - <!-- no translation found for sms_control_no (909756849988183801) --> - <skip /> - <!-- no translation found for date_time_set (2495199891239480952) --> - <skip /> - <!-- no translation found for default_permission_group (7742780381379652409) --> - <skip /> - <!-- no translation found for no_permissions (85461124044682315) --> - <skip /> - <!-- no translation found for perms_hide (4145325555929151849) --> - <skip /> - <!-- no translation found for perms_show_all (6040194843455403173) --> - <skip /> - <!-- no translation found for googlewebcontenthelper_loading (2140804350507245589) --> - <skip /> - <!-- no translation found for usb_storage_title (8699631567051394409) --> - <skip /> - <!-- no translation found for usb_storage_message (5344039189213308733) --> - <skip /> - <!-- no translation found for usb_storage_button_mount (6700104384375121662) --> - <skip /> - <!-- no translation found for usb_storage_button_unmount (465869657252626688) --> - <skip /> - <!-- no translation found for usb_storage_error_message (3192564550748426087) --> - <skip /> - <!-- no translation found for usb_storage_notification_title (6237028017872246940) --> - <skip /> - <!-- no translation found for usb_storage_notification_message (7371717280517625905) --> - <skip /> - <!-- no translation found for select_input_method (2658280517827502015) --> - <skip /> - <!-- no translation found for fast_scroll_alphabet (1017432309285755759) --> - <skip /> - <!-- no translation found for fast_scroll_numeric_alphabet (3092587363718901074) --> - <skip /> - <!-- no translation found for candidates_style (7738463880139922176) --> - <skip /> -</resources> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 2ceea2c..f8881b6 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -262,6 +262,10 @@ <string name="permdesc_reboot">"Permite que la aplicación fuerce al teléfono a reiniciarse."</string> <string name="permlab_mount_unmount_filesystems">"montar y desmontar sistemas de archivos"</string> <string name="permdesc_mount_unmount_filesystems">"Permite que las aplicaciones monten y desmonten sistemas de archivos para un almacenamiento extraíble."</string> + <!-- no translation found for permlab_mount_format_filesystems (5523285143576718981) --> + <skip /> + <!-- no translation found for permdesc_mount_format_filesystems (574060044906047386) --> + <skip /> <string name="permlab_vibrate">"controlar vibración"</string> <string name="permdesc_vibrate">"Permite que la aplicación controle la función de vibración."</string> <string name="permlab_flashlight">"controlar linterna"</string> @@ -276,6 +280,10 @@ <string name="permdesc_locationUpdates">"Permite habilitar/inhabilitar las notificaciones de actualización de la ubicación de la radio. No está destinado al uso por parte de aplicaciones normales."</string> <string name="permlab_checkinProperties">"acceder a propiedades de registro"</string> <string name="permdesc_checkinProperties">"Permite el acceso de lectura/escritura a las propiedades cargadas por el servicio de registro. No está destinado al uso por parte de aplicaciones normales."</string> + <!-- no translation found for permlab_bindGadget (2519859363977647275) --> + <skip /> + <!-- no translation found for permdesc_bindGadget (7865866514555126333) --> + <skip /> <string name="permlab_modifyPhoneState">"modificar estado del teléfono"</string> <string name="permdesc_modifyPhoneState">"Permite que la aplicación controle las funciones de teléfono del dispositivo. Una aplicación con este permiso puede cambiar redes, activar y desactivar la radio, etc., sin necesidad de notificar al usuario."</string> <string name="permlab_readPhoneState">"leer el estado del teléfono"</string> @@ -304,6 +312,10 @@ <string name="permdesc_writeApnSettings">"Permite que una aplicación modifique los valores de configuración de un APN como, por ejemplo, el proxy y el puerto de cualquier APN."</string> <string name="permlab_changeNetworkState">"cambiar la conectividad de red"</string> <string name="permdesc_changeNetworkState">"Permite que una aplicación cambie la conectividad de red de estado."</string> + <!-- no translation found for permlab_changeBackgroundDataSetting (1400666012671648741) --> + <skip /> + <!-- no translation found for permdesc_changeBackgroundDataSetting (1001482853266638864) --> + <skip /> <string name="permlab_accessWifiState">"ver estado de la conectividad Wi-Fi"</string> <string name="permdesc_accessWifiState">"Permite que una aplicación vea la información sobre el estado de la conectividad Wi-Fi."</string> <string name="permlab_changeWifiState">"cambiar estado de Wi-Fi"</string> @@ -546,12 +558,12 @@ <!-- no translation found for relative_time (1818557177829411417) --> <skip /> <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first">"<xliff:g id="DAY">dd</xliff:g> de <xliff:g id="MONTH">MMMM</xliff:g> de <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first">"<xliff:g id="MONTH">MMMM</xliff:g> de <xliff:g id="DAY">dd</xliff:g> de <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first">"<xliff:g id="DAY">dd</xliff:g> de <xliff:g id="MONTH">MMM</xliff:g> de <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMM</xliff:g>, <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g>"</string> + <string name="full_date_month_first">"<xliff:g id="DAY">d</xliff:g>' de '<xliff:g id="MONTH">MMMM</xliff:g>' de '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="full_date_day_first">"<xliff:g id="MONTH">MMMM</xliff:g>' de '<xliff:g id="DAY">d</xliff:g>' de '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_month_first">"<xliff:g id="DAY">d</xliff:g>' de '<xliff:g id="MONTH">MMM</xliff:g>' de '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_day_first">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> + <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"mediodía"</string> <string name="Noon">"Mediodía"</string> <string name="midnight">"medianoche"</string> @@ -679,6 +691,8 @@ <string name="paste">"Pegar"</string> <string name="copyUrl">"Copiar URL"</string> <string name="inputMethod">"Método de introducción de texto"</string> + <!-- no translation found for addToDictionary (726256909274177272) --> + <skip /> <string name="editTextMenuTitle">"Editar texto"</string> <string name="low_internal_storage_view_title">"Poco espacio"</string> <string name="low_internal_storage_view_text">"Se está agotando el espacio de almacenamiento del teléfono."</string> @@ -686,6 +700,8 @@ <string name="cancel">"Cancelar"</string> <string name="yes">"Aceptar"</string> <string name="no">"Cancelar"</string> + <!-- no translation found for dialog_alert_title (2049658708609043103) --> + <skip /> <string name="capital_on">"Activado"</string> <string name="capital_off">"Desconectado"</string> <string name="whichApplication">"Completar acción utilizando"</string> @@ -709,7 +725,8 @@ <string name="volume_music">"Volumen multimedia"</string> <string name="volume_music_hint_playing_through_bluetooth">"Reproduciendo a través de Bluetooth"</string> <string name="volume_call">"Volumen de la llamada"</string> - <string name="volume_call_hint_playing_through_bluetooth">"Reproduciendo a través de Bluetooth"</string> + <!-- no translation found for volume_bluetooth_call (2002891926351151534) --> + <skip /> <string name="volume_alarm">"Volumen de alarma"</string> <string name="volume_notification">"Volumen de notificaciones"</string> <string name="volume_unknown">"Volumen"</string> @@ -745,8 +762,61 @@ <string name="usb_storage_error_message">"Se ha producido un problema al intentar utilizar la tarjeta SD para el almacenamiento USB."</string> <string name="usb_storage_notification_title">"Conectado por USB"</string> <string name="usb_storage_notification_message">"Seleccionar para copiar archivos al/desde el equipo"</string> + <!-- no translation found for usb_storage_stop_notification_title (2336058396663516017) --> + <skip /> + <!-- no translation found for usb_storage_stop_notification_message (2591813490269841539) --> + <skip /> + <!-- no translation found for usb_storage_stop_title (6014127947456185321) --> + <skip /> + <!-- no translation found for usb_storage_stop_message (2390958966725232848) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_mount (1181858854166273345) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_unmount (3774611918660582898) --> + <skip /> + <!-- no translation found for usb_storage_stop_error_message (3917317248440072084) --> + <skip /> + <!-- no translation found for extmedia_format_title (8663247929551095854) --> + <skip /> + <!-- no translation found for extmedia_format_message (3621369962433523619) --> + <skip /> + <!-- no translation found for extmedia_format_button_format (4131064560127478695) --> + <skip /> <string name="select_input_method">"Seleccionar método de introducción de texto"</string> <string name="fast_scroll_alphabet">"ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> <string name="fast_scroll_numeric_alphabet">"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> - <string name="candidates_style"><font fgcolor="#ff000000" bgcolor="#ff8080ff"><u>"candidatos"</u>"u>"</font></string> + <!-- no translation found for candidates_style (4333913089637062257) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_title (5457603418970994050) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_message (4747432538578886744) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_title (780477838241212997) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_message (1312266820092958014) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_title (6410723906019100189) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_message (2679412884290061775) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_title (6872152882604407837) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_message (7260183293747448241) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_title (6729801130790616200) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_message (7613960686747592770) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_title (8902518030404381318) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_message (4205117227342822275) --> + <skip /> + <!-- no translation found for activity_list_empty (4168820609403385789) --> + <skip /> + <!-- no translation found for permlab_pkgUsageStats (8787352074326748892) --> + <skip /> + <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> + <skip /> + <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> + <skip /> </resources> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index ef826cb..47f1bf4 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -262,6 +262,10 @@ <string name="permdesc_reboot">"Permet à l\'application de forcer le redémarrage du téléphone."</string> <string name="permlab_mount_unmount_filesystems">"Monter et démonter des systèmes de fichiers"</string> <string name="permdesc_mount_unmount_filesystems">"Permet à l\'application de monter et démonter des systèmes de fichiers pour des périphériques de stockage amovibles."</string> + <!-- no translation found for permlab_mount_format_filesystems (5523285143576718981) --> + <skip /> + <!-- no translation found for permdesc_mount_format_filesystems (574060044906047386) --> + <skip /> <string name="permlab_vibrate">"Contrôler le vibreur"</string> <string name="permdesc_vibrate">"Permet à l\'application de contrôler le vibreur."</string> <string name="permlab_flashlight">"Contrôler la lampe de poche"</string> @@ -276,6 +280,10 @@ <string name="permdesc_locationUpdates">"Permet l\'activation/la désactivation des notifications de mises à jour de la position géographique provenant de la radio. Les applications normales n\'utilisent pas cette fonctionnalité."</string> <string name="permlab_checkinProperties">"Accéder aux propriétés d\'enregistrement"</string> <string name="permdesc_checkinProperties">"Permet un accès en lecture/écriture à des propriétés envoyées par le service d\'inscription. Les applications normales n\'utilisent pas cette fonctionnalité."</string> + <!-- no translation found for permlab_bindGadget (2519859363977647275) --> + <skip /> + <!-- no translation found for permdesc_bindGadget (7865866514555126333) --> + <skip /> <string name="permlab_modifyPhoneState">"Modifier l\'état du téléphone"</string> <string name="permdesc_modifyPhoneState">"Permet à une application de contrôler les fonctionnalités téléphoniques de l\'appareil. Une application bénéficiant de cette autorisation peut changer de réseau, éteindre et allumer la radio du téléphone, etc., sans vous en notifier."</string> <string name="permlab_readPhoneState">"Lire l\'état du téléphone"</string> @@ -304,6 +312,10 @@ <string name="permdesc_writeApnSettings">"Permet à une application de modifier les paramètres APN (Nom des points d\'accès), comme le proxy ou le port de tout APN."</string> <string name="permlab_changeNetworkState">"Modifier la connectivité du réseau"</string> <string name="permdesc_changeNetworkState">"Permet à une application de modifier la connectivité du réseau."</string> + <!-- no translation found for permlab_changeBackgroundDataSetting (1400666012671648741) --> + <skip /> + <!-- no translation found for permdesc_changeBackgroundDataSetting (1001482853266638864) --> + <skip /> <string name="permlab_accessWifiState">"Afficher l\'état du Wi-Fi"</string> <string name="permdesc_accessWifiState">"Permet à une application d\'afficher des informations concernant l\'état du Wi-Fi."</string> <string name="permlab_changeWifiState">"Modifier l\'état du Wi-Fi"</string> @@ -546,12 +558,12 @@ <!-- no translation found for relative_time (1818557177829411417) --> <skip /> <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g>"</string> + <string name="full_date_month_first">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="full_date_day_first">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_month_first">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_day_first">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> + <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"midi"</string> <string name="Noon">"Midi"</string> <string name="midnight">"minuit"</string> @@ -679,6 +691,8 @@ <string name="paste">"Coller"</string> <string name="copyUrl">"Copier l\'URL"</string> <string name="inputMethod">"Mode de saisie"</string> + <!-- no translation found for addToDictionary (726256909274177272) --> + <skip /> <string name="editTextMenuTitle">"Modifier le texte"</string> <string name="low_internal_storage_view_title">"Espace disponible faible"</string> <string name="low_internal_storage_view_text">"La mémoire du téléphone commence à être pleine."</string> @@ -686,6 +700,8 @@ <string name="cancel">"Annuler"</string> <string name="yes">"OK"</string> <string name="no">"Annuler"</string> + <!-- no translation found for dialog_alert_title (2049658708609043103) --> + <skip /> <string name="capital_on">"ON"</string> <string name="capital_off">"OFF"</string> <string name="whichApplication">"Terminer l\'action avec"</string> @@ -709,7 +725,8 @@ <string name="volume_music">"Volume des médias"</string> <string name="volume_music_hint_playing_through_bluetooth">"Lecture via Bluetooth"</string> <string name="volume_call">"Volume des appels entrants"</string> - <string name="volume_call_hint_playing_through_bluetooth">"Lecture via Bluetooth"</string> + <!-- no translation found for volume_bluetooth_call (2002891926351151534) --> + <skip /> <string name="volume_alarm">"Volume de l\'alarme"</string> <string name="volume_notification">"Volume des notifications"</string> <string name="volume_unknown">"Volume"</string> @@ -745,8 +762,61 @@ <string name="usb_storage_error_message">"Un problème est survenu lors de l\'utilisation de votre carte SD en tant que périphérique de stockage USB."</string> <string name="usb_storage_notification_title">"Connecté avec un câble USB"</string> <string name="usb_storage_notification_message">"Sélectionnez cette option pour copier des fichiers vers/à partir de votre ordinateur."</string> + <!-- no translation found for usb_storage_stop_notification_title (2336058396663516017) --> + <skip /> + <!-- no translation found for usb_storage_stop_notification_message (2591813490269841539) --> + <skip /> + <!-- no translation found for usb_storage_stop_title (6014127947456185321) --> + <skip /> + <!-- no translation found for usb_storage_stop_message (2390958966725232848) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_mount (1181858854166273345) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_unmount (3774611918660582898) --> + <skip /> + <!-- no translation found for usb_storage_stop_error_message (3917317248440072084) --> + <skip /> + <!-- no translation found for extmedia_format_title (8663247929551095854) --> + <skip /> + <!-- no translation found for extmedia_format_message (3621369962433523619) --> + <skip /> + <!-- no translation found for extmedia_format_button_format (4131064560127478695) --> + <skip /> <string name="select_input_method">"Sélectionner un mode de saisie"</string> <string name="fast_scroll_alphabet">"ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> <string name="fast_scroll_numeric_alphabet">"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> - <string name="candidates_style"><font fgcolor="#ff000000" bgcolor="#ff8080ff"><u>"candidats"</u>"u>"</font></string> + <!-- no translation found for candidates_style (4333913089637062257) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_title (5457603418970994050) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_message (4747432538578886744) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_title (780477838241212997) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_message (1312266820092958014) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_title (6410723906019100189) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_message (2679412884290061775) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_title (6872152882604407837) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_message (7260183293747448241) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_title (6729801130790616200) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_message (7613960686747592770) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_title (8902518030404381318) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_message (4205117227342822275) --> + <skip /> + <!-- no translation found for activity_list_empty (4168820609403385789) --> + <skip /> + <!-- no translation found for permlab_pkgUsageStats (8787352074326748892) --> + <skip /> + <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> + <skip /> + <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> + <skip /> </resources> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index caf63c7..710c493 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -262,6 +262,10 @@ <string name="permdesc_reboot">"Consente all\'applicazione di imporre il riavvio del telefono."</string> <string name="permlab_mount_unmount_filesystems">"montare e smontare filesystem"</string> <string name="permdesc_mount_unmount_filesystems">"Consente montaggio e smontaggio da parte dell\'applicazione dei filesystem degli archivi rimovibili."</string> + <!-- no translation found for permlab_mount_format_filesystems (5523285143576718981) --> + <skip /> + <!-- no translation found for permdesc_mount_format_filesystems (574060044906047386) --> + <skip /> <string name="permlab_vibrate">"controllare la vibrazione"</string> <string name="permdesc_vibrate">"Consente all\'applicazione di controllare la vibrazione."</string> <string name="permlab_flashlight">"controllare il flash"</string> @@ -276,6 +280,10 @@ <string name="permdesc_locationUpdates">"Consente l\'attivazione/disattivazione delle notifiche di aggiornamento della posizione. Da non usare per normali applicazioni."</string> <string name="permlab_checkinProperties">"accedere a proprietà di archiviazione"</string> <string name="permdesc_checkinProperties">"Consente l\'accesso di lettura/scrittura alle proprietà caricate dal servizio di archiviazione. Da non usare per normali applicazioni."</string> + <!-- no translation found for permlab_bindGadget (2519859363977647275) --> + <skip /> + <!-- no translation found for permdesc_bindGadget (7865866514555126333) --> + <skip /> <string name="permlab_modifyPhoneState">"modificare lo stato del telefono"</string> <string name="permdesc_modifyPhoneState">"Consente all\'applicazione di controllare le funzioni telefoniche del dispositivo. Un\'applicazione con questa autorizzazione può cambiare rete, accendere e spegnere il modulo radio del telefono e così via, il tutto automaticamente."</string> <string name="permlab_readPhoneState">"leggere lo stato del telefono"</string> @@ -304,6 +312,10 @@ <string name="permdesc_writeApnSettings">"Consente a un\'applicazione di modificare le impostazioni APN, come proxy e porta di qualsiasi APN."</string> <string name="permlab_changeNetworkState">"cambiare connettività di rete"</string> <string name="permdesc_changeNetworkState">"Consente a un\'applicazione di modificare lo stato di connettività di rete."</string> + <!-- no translation found for permlab_changeBackgroundDataSetting (1400666012671648741) --> + <skip /> + <!-- no translation found for permdesc_changeBackgroundDataSetting (1001482853266638864) --> + <skip /> <string name="permlab_accessWifiState">"visualizzare lo stato Wi-Fi"</string> <string name="permdesc_accessWifiState">"Consente a un\'applicazione di visualizzare le informazioni relative allo stato della connessione Wi-Fi."</string> <string name="permlab_changeWifiState">"cambiare stato Wi-Fi"</string> @@ -546,12 +558,12 @@ <!-- no translation found for relative_time (1818557177829411417) --> <skip /> <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first">"<xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="DAY">dd</xliff:g>, <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first">"<xliff:g id="MONTH">MMM</xliff:g> <xliff:g id="DAY">dd</xliff:g>, <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g>"</string> + <string name="full_date_month_first">"<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="full_date_day_first">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_month_first">"<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_day_first">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> + <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"mezzogiorno"</string> <string name="Noon">"Mezzogiorno"</string> <string name="midnight">"mezzanotte"</string> @@ -679,6 +691,8 @@ <string name="paste">"Incolla"</string> <string name="copyUrl">"Copia URL"</string> <string name="inputMethod">"Metodo inserimento"</string> + <!-- no translation found for addToDictionary (726256909274177272) --> + <skip /> <string name="editTextMenuTitle">"Modifica testo"</string> <string name="low_internal_storage_view_title">"Spazio in esaurimento"</string> <string name="low_internal_storage_view_text">"Spazio di archiviazione del telefono in esaurimento."</string> @@ -686,6 +700,8 @@ <string name="cancel">"Annulla"</string> <string name="yes">"OK"</string> <string name="no">"Annulla"</string> + <!-- no translation found for dialog_alert_title (2049658708609043103) --> + <skip /> <string name="capital_on">"ON"</string> <string name="capital_off">"OFF"</string> <string name="whichApplication">"Completa l\'azione con"</string> @@ -709,7 +725,8 @@ <string name="volume_music">"Volume media"</string> <string name="volume_music_hint_playing_through_bluetooth">"Riproduzione tramite Bluetooth"</string> <string name="volume_call">"Volume chiamate"</string> - <string name="volume_call_hint_playing_through_bluetooth">"Riproduzione tramite Bluetooth"</string> + <!-- no translation found for volume_bluetooth_call (2002891926351151534) --> + <skip /> <string name="volume_alarm">"Volume allarme"</string> <string name="volume_notification">"Volume notifiche"</string> <string name="volume_unknown">"Volume"</string> @@ -745,8 +762,61 @@ <string name="usb_storage_error_message">"Problema di utilizzo della scheda SD per l\'archiviazione USB."</string> <string name="usb_storage_notification_title">"USB collegata"</string> <string name="usb_storage_notification_message">"Seleziona per copiare file sul/dal computer in uso."</string> + <!-- no translation found for usb_storage_stop_notification_title (2336058396663516017) --> + <skip /> + <!-- no translation found for usb_storage_stop_notification_message (2591813490269841539) --> + <skip /> + <!-- no translation found for usb_storage_stop_title (6014127947456185321) --> + <skip /> + <!-- no translation found for usb_storage_stop_message (2390958966725232848) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_mount (1181858854166273345) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_unmount (3774611918660582898) --> + <skip /> + <!-- no translation found for usb_storage_stop_error_message (3917317248440072084) --> + <skip /> + <!-- no translation found for extmedia_format_title (8663247929551095854) --> + <skip /> + <!-- no translation found for extmedia_format_message (3621369962433523619) --> + <skip /> + <!-- no translation found for extmedia_format_button_format (4131064560127478695) --> + <skip /> <string name="select_input_method">"Seleziona metodo di inserimento"</string> <string name="fast_scroll_alphabet">"ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> <string name="fast_scroll_numeric_alphabet">"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> - <string name="candidates_style"><font fgcolor="#ff000000" bgcolor="#ff8080ff"><u>"candidati"</u>"u>"</font></string> + <!-- no translation found for candidates_style (4333913089637062257) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_title (5457603418970994050) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_message (4747432538578886744) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_title (780477838241212997) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_message (1312266820092958014) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_title (6410723906019100189) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_message (2679412884290061775) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_title (6872152882604407837) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_message (7260183293747448241) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_title (6729801130790616200) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_message (7613960686747592770) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_title (8902518030404381318) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_message (4205117227342822275) --> + <skip /> + <!-- no translation found for activity_list_empty (4168820609403385789) --> + <skip /> + <!-- no translation found for permlab_pkgUsageStats (8787352074326748892) --> + <skip /> + <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> + <skip /> + <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> + <skip /> </resources> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 055a8b1..0eba7f5 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -262,6 +262,10 @@ <string name="permdesc_reboot">"携帯電話の強制的な再起動をアプリケーションに許可します。"</string> <string name="permlab_mount_unmount_filesystems">"ファイルシステムのマウントとマウント解除"</string> <string name="permdesc_mount_unmount_filesystems">"リムーバブルメモリのファイルシステムのマウントとマウント解除をアプリケーションに許可します。"</string> + <!-- no translation found for permlab_mount_format_filesystems (5523285143576718981) --> + <skip /> + <!-- no translation found for permdesc_mount_format_filesystems (574060044906047386) --> + <skip /> <string name="permlab_vibrate">"バイブレータの制御"</string> <string name="permdesc_vibrate">"バイブレータのコントロールをアプリケーションに許可します。"</string> <string name="permlab_flashlight">"ライトのコントロール"</string> @@ -276,6 +280,10 @@ <string name="permdesc_locationUpdates">"無線通信からの位置更新通知を有効/無効にすることを許可します。通常のアプリケーションでは使用しません。"</string> <string name="permlab_checkinProperties">"チェックインプロパティへのアクセス"</string> <string name="permdesc_checkinProperties">"チェックインサービスがアップロードしたプロパティへの読み書きを許可します。通常のアプリケーションでは使用しません。"</string> + <!-- no translation found for permlab_bindGadget (2519859363977647275) --> + <skip /> + <!-- no translation found for permdesc_bindGadget (7865866514555126333) --> + <skip /> <string name="permlab_modifyPhoneState">"携帯電話の状態の変更"</string> <string name="permdesc_modifyPhoneState">"携帯端末の電話機能のコントロールをアプリケーションに許可します。この許可を受けたアプリケーションは、ネットワークの切り替え、携帯電話の無線通信のオン/オフなどを通知せずに行うことができます。"</string> <string name="permlab_readPhoneState">"携帯電話の状態の読み取り"</string> @@ -304,6 +312,10 @@ <string name="permdesc_writeApnSettings">"APNのプロキシやポートなどのAPN設定の変更をアプリケーションに許可します。"</string> <string name="permlab_changeNetworkState">"ネットワーク接続の変更"</string> <string name="permdesc_changeNetworkState">"ネットワークの接続状態の変更をアプリケーションに許可します。"</string> + <!-- no translation found for permlab_changeBackgroundDataSetting (1400666012671648741) --> + <skip /> + <!-- no translation found for permdesc_changeBackgroundDataSetting (1001482853266638864) --> + <skip /> <string name="permlab_accessWifiState">"Wi-Fi状態の表示"</string> <string name="permdesc_accessWifiState">"Wi-Fi状態に関する情報の表示をアプリケーションに許可します。"</string> <string name="permlab_changeWifiState">"Wi-Fi状態の変更"</string> @@ -546,10 +558,10 @@ <!-- no translation found for relative_time (1818557177829411417) --> <skip /> <string name="time_wday">"<xliff:g id="WEEKDAY">%2$s</xliff:g>、<xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string> - <string name="full_date_month_first">"<xliff:g id="YEAR">yyyy</xliff:g>\'年\'<xliff:g id="MONTH">MMMM</xliff:g><xliff:g id="DAY">dd</xliff:g>\'日\'"</string> - <string name="full_date_day_first">"<xliff:g id="YEAR">yyyy</xliff:g>\'年\'<xliff:g id="MONTH">MMMM</xliff:g><xliff:g id="DAY">dd</xliff:g>\'日\'"</string> - <string name="medium_date_month_first">"<xliff:g id="YEAR">yyyy</xliff:g>\'年\'<xliff:g id="MONTH">MMM</xliff:g><xliff:g id="DAY">dd</xliff:g>\'日\'"</string> - <string name="medium_date_day_first">"<xliff:g id="YEAR">yyyy</xliff:g>\'年\'<xliff:g id="MONTH">MMM</xliff:g><xliff:g id="DAY">dd</xliff:g>\'日\'"</string> + <string name="full_date_month_first">"<xliff:g id="YEAR">yyyy</xliff:g>\'年\'<xliff:g id="MONTH">MMMM</xliff:g><xliff:g id="DAY">d</xliff:g>\'日\'"</string> + <string name="full_date_day_first">"<xliff:g id="YEAR">yyyy</xliff:g>\'年\'<xliff:g id="MONTH">MMMM</xliff:g><xliff:g id="DAY">d</xliff:g>\'日\'"</string> + <string name="medium_date_month_first">"<xliff:g id="YEAR">yyyy</xliff:g>\'年\'<xliff:g id="MONTH">MMM</xliff:g><xliff:g id="DAY">d</xliff:g>\'日\'"</string> + <string name="medium_date_day_first">"<xliff:g id="YEAR">yyyy</xliff:g>\'年\'<xliff:g id="MONTH">MMM</xliff:g><xliff:g id="DAY">d</xliff:g>\'日\'"</string> <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">a</xliff:g>"</string> <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"正午"</string> @@ -561,8 +573,7 @@ <!-- no translation found for month (7026169712234774086) --> <skip /> <string name="month_day_year">"<xliff:g id="YEAR">%Y</xliff:g>年<xliff:g id="MONTH">%B</xliff:g><xliff:g id="DAY">%-d</xliff:g>日"</string> - <!-- no translation found for month_year (9219019380312413367) --> - <skip /> + <string name="month_year">"<xliff:g id="YEAR">%Y</xliff:g>年 <xliff:g id="MONTH">%B</xliff:g>"</string> <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> <string name="date_and_time">"<xliff:g id="YEAR">%Y</xliff:g>年<xliff:g id="MONTH">%B</xliff:g><xliff:g id="DAY">%-d</xliff:g>日<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> <string name="same_year_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g><xliff:g id="DAY1">%3$s</xliff:g>日~<xliff:g id="MONTH2">%7$s</xliff:g><xliff:g id="DAY2">%8$s</xliff:g>日"</string> @@ -679,6 +690,8 @@ <string name="paste">"貼り付け"</string> <string name="copyUrl">"URLをコピー"</string> <string name="inputMethod">"入力方法"</string> + <!-- no translation found for addToDictionary (726256909274177272) --> + <skip /> <string name="editTextMenuTitle">"テキストを編集"</string> <string name="low_internal_storage_view_title">"空き容量低下"</string> <string name="low_internal_storage_view_text">"携帯電話の空き容量が少なくなっています。"</string> @@ -686,6 +699,8 @@ <string name="cancel">"キャンセル"</string> <string name="yes">"OK"</string> <string name="no">"キャンセル"</string> + <!-- no translation found for dialog_alert_title (2049658708609043103) --> + <skip /> <string name="capital_on">"オン"</string> <string name="capital_off">"オフ"</string> <string name="whichApplication">"操作の完了に使用"</string> @@ -709,7 +724,8 @@ <string name="volume_music">"メディアの音量"</string> <string name="volume_music_hint_playing_through_bluetooth">"Bluetooth経由で再生中です"</string> <string name="volume_call">"着信音の音量"</string> - <string name="volume_call_hint_playing_through_bluetooth">"Bluetooth経由で再生中です"</string> + <!-- no translation found for volume_bluetooth_call (2002891926351151534) --> + <skip /> <string name="volume_alarm">"アラームの音量"</string> <string name="volume_notification">"通知音の音量"</string> <string name="volume_unknown">"音量"</string> @@ -745,8 +761,61 @@ <string name="usb_storage_error_message">"USBメモリにSDカードを使用する際に問題が発生しました。"</string> <string name="usb_storage_notification_title">"USB接続"</string> <string name="usb_storage_notification_message">"パソコンとの間でファイルをコピーします。"</string> + <!-- no translation found for usb_storage_stop_notification_title (2336058396663516017) --> + <skip /> + <!-- no translation found for usb_storage_stop_notification_message (2591813490269841539) --> + <skip /> + <!-- no translation found for usb_storage_stop_title (6014127947456185321) --> + <skip /> + <!-- no translation found for usb_storage_stop_message (2390958966725232848) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_mount (1181858854166273345) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_unmount (3774611918660582898) --> + <skip /> + <!-- no translation found for usb_storage_stop_error_message (3917317248440072084) --> + <skip /> + <!-- no translation found for extmedia_format_title (8663247929551095854) --> + <skip /> + <!-- no translation found for extmedia_format_message (3621369962433523619) --> + <skip /> + <!-- no translation found for extmedia_format_button_format (4131064560127478695) --> + <skip /> <string name="select_input_method">"入力方法の選択"</string> <string name="fast_scroll_alphabet">"ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> <string name="fast_scroll_numeric_alphabet">"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> - <string name="candidates_style"><font fgcolor="#ff000000" bgcolor="#ff8080ff"><u>"候補"</u></font></string> + <!-- no translation found for candidates_style (4333913089637062257) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_title (5457603418970994050) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_message (4747432538578886744) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_title (780477838241212997) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_message (1312266820092958014) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_title (6410723906019100189) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_message (2679412884290061775) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_title (6872152882604407837) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_message (7260183293747448241) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_title (6729801130790616200) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_message (7613960686747592770) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_title (8902518030404381318) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_message (4205117227342822275) --> + <skip /> + <!-- no translation found for activity_list_empty (4168820609403385789) --> + <skip /> + <!-- no translation found for permlab_pkgUsageStats (8787352074326748892) --> + <skip /> + <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> + <skip /> + <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> + <skip /> </resources> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml new file mode 100644 index 0000000..4272684 --- /dev/null +++ b/core/res/res/values-ko/strings.xml @@ -0,0 +1,822 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="byteShort">"B"</string> + <string name="kilobyteShort">"KB"</string> + <string name="megabyteShort">"MB"</string> + <string name="gigabyteShort">"GB"</string> + <string name="terabyteShort">"TB"</string> + <string name="petabyteShort">"PB"</string> + <string name="untitled">"<제목없음>"</string> + <string name="ellipsis">"…"</string> + <string name="emptyPhoneNumber">"(전화번호 없음)"</string> + <string name="unknownName">"(알 수 없음)"</string> + <string name="defaultVoiceMailAlphaTag">"음성메일"</string> + <string name="defaultMsisdnAlphaTag">"MSISDN1"</string> + <string name="mmiError">"연결에 문제가 있거나 MMI 코드가 잘못되었습니다."</string> + <string name="serviceEnabled">"서비스가 활성화되었습니다."</string> + <string name="serviceEnabledFor">"사용 설정된 서비스 목록:"</string> + <string name="serviceDisabled">"서비스가 비활성화되었습니다."</string> + <string name="serviceRegistered">"등록이 완료되었습니다."</string> + <string name="serviceErased">"지웠습니다."</string> + <string name="passwordIncorrect">"비밀번호가 잘못되었습니다."</string> + <string name="mmiComplete">"MMI 완료"</string> + <string name="badPin">"이전 PIN이 올바르지 않습니다."</string> + <string name="badPuk">"입력한 PUK가 올바르지 않습니다."</string> + <string name="mismatchPin">"입력한 PIN이 일치하지 않습니다."</string> + <string name="invalidPin">"4~8자리 숫자로 된 PIN을 입력하세요."</string> + <string name="needPuk">"SIM 카드의 PUK가 잠겨 있습니다. 잠금해제하려면 PUK 코드를 입력하세요."</string> + <string name="needPuk2">"SIM 카드 잠금을 해제하려면 PUK2를 입력하세요."</string> + <string name="ClipMmi">"수신 발신자 번호"</string> + <string name="ClirMmi">"발신 발신자 번호"</string> + <string name="CfMmi">"착신전환"</string> + <string name="CwMmi">"통화중 대기"</string> + <string name="BaMmi">"착발신 제한"</string> + <string name="PwdMmi">"비밀번호 변경"</string> + <string name="PinMmi">"PIN 변경"</string> + <string name="CLIRDefaultOnNextCallOn">"발신자 번호가 기본적으로 제한됨으로 설정됩니다. 다음 통화: 제한됨"</string> + <string name="CLIRDefaultOnNextCallOff">"발신자 번호가 기본적으로 제한됨으로 설정됩니다. 다음 통화: 제한되지 않음"</string> + <string name="CLIRDefaultOffNextCallOn">"발신자 번호가 기본적으로 제한되지 않음으로 설정됩니다. 다음 통화: 제한됨"</string> + <string name="CLIRDefaultOffNextCallOff">"발신자 번호가 기본적으로 제한되지 않음으로 설정됩니다. 다음 통화: 제한되지 않음"</string> + <string name="serviceNotProvisioned">"서비스가 준비되지 않았습니다."</string> + <string name="CLIRPermanent">"발신자 번호 설정을 변경할 수 없습니다."</string> + <string name="serviceClassVoice">"음성"</string> + <string name="serviceClassData">"데이터"</string> + <string name="serviceClassFAX">"팩스"</string> + <string name="serviceClassSMS">"SMS"</string> + <string name="serviceClassDataAsync">"비동기"</string> + <string name="serviceClassDataSync">"동기화"</string> + <string name="serviceClassPacket">"패킷"</string> + <string name="serviceClassPAD">"PAD"</string> + <string name="cfTemplateNotForwarded">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: 착신전환 안 됨"</string> + <string name="cfTemplateForwarded">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> + <string name="cfTemplateForwardedTime">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g>초 후 <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> + <string name="cfTemplateRegistered">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: 착신전환 안 됨"</string> + <string name="cfTemplateRegisteredTime">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: 착신전환 안 됨"</string> + <string name="httpErrorOk">"확인"</string> + <string name="httpError">"웹페이지에 오류가 있습니다."</string> + <string name="httpErrorLookup">"URL을 찾을 수 없습니다."</string> + <string name="httpErrorUnsupportedAuthScheme">"사이트 인증 스키마가 지원되지 않습니다."</string> + <string name="httpErrorAuth">"인증에 실패했습니다."</string> + <string name="httpErrorProxyAuth">"프록시 서버를 통한 인증에 실패했습니다."</string> + <string name="httpErrorConnect">"서버에 연결하지 못했습니다."</string> + <string name="httpErrorIO">"서버와 통신할 수 없습니다. 나중에 다시 시도하세요."</string> + <string name="httpErrorTimeout">"서버 연결 제한시간이 초과되었습니다."</string> + <string name="httpErrorRedirectLoop">"페이지에 서버 리디렉션이 너무 많이 포함되어 있습니다."</string> + <string name="httpErrorUnsupportedScheme">"지원되지 않는 프로토콜입니다."</string> + <string name="httpErrorFailedSslHandshake">"보안 연결을 설정하지 못했습니다."</string> + <string name="httpErrorBadUrl">"URL이 올바르지 않아 페이지를 열 수 없습니다."</string> + <string name="httpErrorFile">"파일에 액세스할 수 없습니다."</string> + <string name="httpErrorFileNotFound">"요청한 파일을 찾을 수 없습니다."</string> + <string name="httpErrorTooManyRequests">"처리 중인 요청이 너무 많습니다. 잠시 후에 다시 시도하세요."</string> + <string name="contentServiceSync">"동기화"</string> + <string name="contentServiceSyncNotificationTitle">"동기화"</string> + <string name="contentServiceTooManyDeletesNotificationDesc">"<xliff:g id="CONTENT_TYPE">%s</xliff:g> 삭제가 너무 많습니다."</string> + <string name="low_memory">"전화기 저장공간이 꽉 찼습니다. 일부 파일을 삭제하여 저장 여유 공간을 늘리세요."</string> + <string name="me">"나"</string> + <string name="power_dialog">"전화기 옵션"</string> + <string name="silent_mode">"무음 모드"</string> + <string name="turn_on_radio">"무선 켜기"</string> + <string name="turn_off_radio">"무선 끄기"</string> + <string name="screen_lock">"화면 잠금"</string> + <string name="power_off">"끄기"</string> + <string name="shutdown_progress">"종료 중..."</string> + <string name="shutdown_confirm">"전화기가 종료됩니다."</string> + <string name="no_recent_tasks">"최신 응용프로그램이 아닙니다."</string> + <string name="global_actions">"전화기 옵션"</string> + <string name="global_action_lock">"화면 잠금"</string> + <string name="global_action_power_off">"끄기"</string> + <string name="global_action_toggle_silent_mode">"무음 모드"</string> + <string name="global_action_silent_mode_on_status">"소리 꺼짐"</string> + <string name="global_action_silent_mode_off_status">"소리 켜짐"</string> + <string name="safeMode">"안전 모드"</string> + <string name="permgrouplab_costMoney">"요금이 부과되는 서비스"</string> + <string name="permgroupdesc_costMoney">"응용프로그램이 요금이 부과될 수 있는 작업을 할 수 있습니다."</string> + <string name="permgrouplab_messages">"메시지"</string> + <string name="permgroupdesc_messages">"SMS, 이메일 및 기타 메시지를 읽고 씁니다."</string> + <string name="permgrouplab_personalInfo">"개인 정보"</string> + <string name="permgroupdesc_personalInfo">"전화기에 저장된 연락처 및 캘린더에 직접 액세스합니다."</string> + <string name="permgrouplab_location">"위치"</string> + <string name="permgroupdesc_location">"물리적 위치 모니터링"</string> + <string name="permgrouplab_network">"네트워크 통신"</string> + <string name="permgroupdesc_network">"응용프로그램이 다양한 네트워크 기능에 액세스할 수 있습니다."</string> + <string name="permgrouplab_accounts">"Google 계정"</string> + <string name="permgroupdesc_accounts">"사용가능한 Google 계정에 액세스합니다."</string> + <string name="permgrouplab_hardwareControls">"하드웨어 제어"</string> + <string name="permgroupdesc_hardwareControls">"핸드셋의 하드웨어에 직접 액세스합니다."</string> + <string name="permgrouplab_phoneCalls">"전화 통화"</string> + <string name="permgroupdesc_phoneCalls">"전화 통화를 모니터링, 기록 및 처리합니다."</string> + <string name="permgrouplab_systemTools">"시스템 도구"</string> + <string name="permgroupdesc_systemTools">"하위 수준의 액세스 및 시스템 제어"</string> + <string name="permgrouplab_developmentTools">"개발도구"</string> + <string name="permgroupdesc_developmentTools">"응용프로그램 개발자에게만 필요한 기능입니다."</string> + <string name="permlab_statusBar">"상태 표시줄 사용 안 함 또는 수정"</string> + <string name="permdesc_statusBar">"응용프로그램이 상태 표시줄을 비활성화하거나 시스템 아이콘을 추가 및 제거할 수 있습니다."</string> + <string name="permlab_expandStatusBar">"상태 표시줄 확장/축소"</string> + <string name="permdesc_expandStatusBar">"응용프로그램이 상태 표시줄을 확장하거나 축소할 수 있습니다."</string> + <string name="permlab_processOutgoingCalls">"발신전화 가로채기"</string> + <string name="permdesc_processOutgoingCalls">"응용프로그램이 발신전화를 처리하고 전화를 걸 번호를 변경할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 발신전화를 모니터링하거나, 다른 방향으로 돌리거나, 중단시킬 수 있습니다."</string> + <string name="permlab_receiveSms">"SMS 받기"</string> + <string name="permdesc_receiveSms">"응용프로그램이 SMS 메시지를 받고 처리할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 메시지를 모니터링하거나 사용자가 보기 전에 삭제할 수 있습니다."</string> + <string name="permlab_receiveMms">"MMS 받기"</string> + <string name="permdesc_receiveMms">"응용프로그램이 MMS 메시지를 받고 처리할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 메시지를 모니터링하거나 사용자가 보기 전에 삭제할 수 있습니다."</string> + <string name="permlab_sendSms">"SMS 메시지 보내기"</string> + <string name="permdesc_sendSms">"응용프로그램이 SMS 메시지를 보낼 수 있습니다. 악성 응용프로그램은 사용자의 확인 없이 메시지를 전송하여 요금을 부과할 수 있습니다."</string> + <string name="permlab_readSms">"SMS 또는 MMS 읽기"</string> + <string name="permdesc_readSms">"응용프로그램이 전화기 또는 SIM 카드에 저장된 SMS 메시지를 읽을 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 기밀 메시지를 읽을 수 있습니다."</string> + <string name="permlab_writeSms">"SMS 또는 MMS 편집"</string> + <string name="permdesc_writeSms">"응용프로그램이 전화기 또는 SIM 카드에 저장된 SMS 메시지에 쓸 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 메시지를 삭제할 수 있습니다."</string> + <string name="permlab_receiveWapPush">"WAP 받기"</string> + <string name="permdesc_receiveWapPush">"응용프로그램이 WAP 메시지를 받고 처리할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 메시지를 모니터링하거나 사용자가 보기 전에 삭제할 수 있습니다."</string> + <string name="permlab_getTasks">"실행 중인 응용프로그램 검색"</string> + <string name="permdesc_getTasks">"응용프로그램이 현재 실행 중이거나 최근에 실행된 작업에 대한 정보를 검색할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 다른 응용프로그램에 대한 개인 정보를 검색할 수 있습니다."</string> + <string name="permlab_reorderTasks">"실행 중인 응용프로그램 순서 재지정"</string> + <string name="permdesc_reorderTasks">"응용프로그램이 작업을 포그라운드나 백그라운드로 이동할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 사용자의 조작 없이 작업을 강제로 앞으로 이동할 수 있습니다."</string> + <string name="permlab_setDebugApp">"응용프로그램 디버깅 사용"</string> + <string name="permdesc_setDebugApp">"응용프로그램이 다른 응용프로그램에 대한 디버깅을 설정할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 다른 응용프로그램을 중지시킬 수 있습니다."</string> + <string name="permlab_changeConfiguration">"UI 설정 변경"</string> + <string name="permdesc_changeConfiguration">"응용프로그램이 로케일 또는 전체 글꼴 크기 같은 현재 구성을 변경할 수 있습니다."</string> + <string name="permlab_restartPackages">"다른 응용프로그램 다시 시작"</string> + <string name="permdesc_restartPackages">"응용프로그램이 다른 응용프로그램을 강제로 다시 시작할 수 있습니다."</string> + <string name="permlab_setProcessForeground">"중지되지 않도록 하기"</string> + <string name="permdesc_setProcessForeground">"응용프로그램이 프로세스를 포그라운드에서 실행되도록 하여 프로세스를 중지할 수 있습니다. 일반 응용프로그램에는 필요하지 않습니다."</string> + <string name="permlab_forceBack">"강제로 응용프로그램 닫기"</string> + <string name="permdesc_forceBack">"응용프로그램이 포그라운드에 있는 활동을 강제로 닫을 수 있습니다. 일반 응용프로그램에는 필요하지 않습니다."</string> + <string name="permlab_dump">"시스템 내부 상태 검색"</string> + <string name="permdesc_dump">"응용프로그램이 시스템의 내부 상태를 검색할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 일반적으로 필요하지 않은 다양한 개인 정보와 보안 정보를 검색할 수 있습니다."</string> + <string name="permlab_addSystemService">"하위 수준 서비스 게시"</string> + <string name="permdesc_addSystemService">"응용프로그램이 자체 하위 수준 시스템 서비스를 게시할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 시스템을 하이재킹하거나 시스템의 데이터를 도용 또는 손상시킬 수 있습니다."</string> + <string name="permlab_runSetActivityWatcher">"실행 중인 모든 응용프로그램 모니터링 및 제어"</string> + <string name="permdesc_runSetActivityWatcher">"응용프로그램이 시스템에서 활동이 시작되는 방식을 모니터링하고 제어할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 시스템을 완전히 손상시킬 수 있습니다. 이 권한은 개발 과정에만 필요하며 일반 전화기 사용 시에는 필요하지 않습니다."</string> + <string name="permlab_broadcastPackageRemoved">"패키지 제거 브로드캐스트 보내기"</string> + <string name="permdesc_broadcastPackageRemoved">"응용프로그램이 응용프로그램 패키지가 제거되었다는 알림을 브로드캐스트할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 실행 중인 다른 응용프로그램을 중지시킬 수 있습니다."</string> + <string name="permlab_broadcastSmsReceived">"SMS 수신 브로드캐스트 보내기"</string> + <string name="permdesc_broadcastSmsReceived">"응용프로그램이 SMS 메시지를 받았다는 알림을 브로드캐스트할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 들어오는 SMS 메시지처럼 위장할 수 있습니다."</string> + <string name="permlab_broadcastWapPush">"WAP-PUSH-수신 브로드캐스트 보내기"</string> + <string name="permdesc_broadcastWapPush">"응용프로그램이 WAP PUSH 메시지를 받았다는 알림을 브로드캐스트할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 MMS 메시지를 받은 것처럼 위장하거나 웹페이지의 콘텐츠를 악성 변종으로 바꿀 수 있습니다."</string> + <string name="permlab_setProcessLimit">"실행 중인 프로세스 수 제한"</string> + <string name="permdesc_setProcessLimit">"응용프로그램이 실행할 최대 프로세스 수를 제어할 수 있습니다. 일반 응용프로그램에는 필요하지 않습니다."</string> + <string name="permlab_setAlwaysFinish">"모든 백그라운드 응용프로그램이 닫히도록 하기"</string> + <string name="permdesc_setAlwaysFinish">"응용프로그램이 백그라운드로 이동한 활동을 항상 바로 마칠지 여부를 제어할 수 있습니다. 일반 응용프로그램에는 필요하지 않습니다."</string> + <string name="permlab_fotaUpdate">"시스템 업데이트 자동으로 설치"</string> + <string name="permdesc_fotaUpdate">"응용프로그램이 대기 중인 시스템 업데이트에 대한 알림을 받고 설치를 트리거할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 인증되지 않은 업데이트로 시스템을 손상시키거나 업데이트 절차를 방해할 수 있습니다."</string> + <string name="permlab_batteryStats">"배터리 통계 수정"</string> + <string name="permdesc_batteryStats">"수집된 배터리 통계를 수정할 수 있습니다. 일반 응용프로그램에서는 사용하지 않습니다."</string> + <string name="permlab_internalSystemWindow">"인증되지 않은 창 표시"</string> + <string name="permdesc_internalSystemWindow">"내부 시스템 사용자 인터페이스에서 사용하는 창을 만들 수 있습니다. 일반 응용프로그램에서는 사용하지 않습니다."</string> + <string name="permlab_systemAlertWindow">"시스템 수준 경고 표시"</string> + <string name="permdesc_systemAlertWindow">"응용프로그램이 시스템 경고 창을 표시할 수 있습니다. 악성 응용프로그램은 전화기 화면 전체를 차지할 수 있습니다."</string> + <string name="permlab_setAnimationScale">"전체 애니메이션 속도 수정"</string> + <string name="permdesc_setAnimationScale">"응용프로그램이 언제든지 전체 애니메이션 속도를 빠르게 또는 느리게 변경할 수 있습니다."</string> + <string name="permlab_manageAppTokens">"응용프로그램 토큰 관리"</string> + <string name="permdesc_manageAppTokens">"응용프로그램이 일반적인 Z-순서를 무시하여 자체 토큰을 만들고 관리할 수 있습니다. 일반 응용프로그램에는 필요하지 않습니다."</string> + <string name="permlab_injectEvents">"키 및 컨트롤 버튼 누르기"</string> + <string name="permdesc_injectEvents">"응용프로그램이 입력 이벤트(예: 키 누름)를 다른 응용프로그램에 전달할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 전화기를 완전히 제어할 수 있습니다."</string> + <string name="permlab_readInputState">"사용자가 입력한 내용 및 수행한 작업 기록"</string> + <string name="permdesc_readInputState">"응용프로그램이 다른 응용프로그램과 상호작용할 때에도 사용자가 누르는 키(예: 비밀번호 입력)를 볼 수 있습니다. 일반 응용프로그램에는 필요하지 않습니다."</string> + <string name="permlab_bindInputMethod">"입력 방법에 고정"</string> + <string name="permdesc_bindInputMethod">"보유자가 입력 방법의 최상위 인터페이스에 고정할 수 있습니다. 일반 응용프로그램에는 필요하지 않습니다."</string> + <string name="permlab_setOrientation">"화면 방향 변경"</string> + <string name="permdesc_setOrientation">"응용프로그램이 언제든지 화면 회전을 변경할 수 있습니다. 일반 응용프로그램에는 필요하지 않습니다."</string> + <string name="permlab_signalPersistentProcesses">"응용프로그램에 Linux 신호 보내기"</string> + <string name="permdesc_signalPersistentProcesses">"응용프로그램이 제공된 신호를 모든 영구 프로세스로 보내도록 요청할 수 있습니다."</string> + <string name="permlab_persistentActivity">"응용프로그램이 항상 실행되도록 설정"</string> + <string name="permdesc_persistentActivity">"응용프로그램이 연결된 일부 구성요소를 지속하여 다른 응용프로그램에 사용할 수 없도록 합니다."</string> + <string name="permlab_deletePackages">"응용프로그램 삭제"</string> + <string name="permdesc_deletePackages">"응용프로그램이 Android 패키지를 삭제할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 중요한 응용프로그램을 삭제할 수 있습니다."</string> + <string name="permlab_clearAppUserData">"다른 응용프로그램의 데이터 삭제"</string> + <string name="permdesc_clearAppUserData">"응용프로그램이 사용자 데이터를 지울 수 있습니다."</string> + <string name="permlab_deleteCacheFiles">"다른 응용프로그램의 캐시 삭제"</string> + <string name="permdesc_deleteCacheFiles">"응용프로그램이 캐시 파일을 삭제할 수 있습니다."</string> + <string name="permlab_getPackageSize">"응용프로그램 저장공간 계산"</string> + <string name="permdesc_getPackageSize">"응용프로그램이 해당 코드, 데이터 및 캐시 크기를 검색할 수 있습니다."</string> + <string name="permlab_installPackages">"응용프로그램 직접 설치"</string> + <string name="permdesc_installPackages">"응용프로그램이 새로운 또는 업데이트된 Android 패키지를 설치할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 임의의 강력한 권한으로 새 응용프로그램을 추가할 수 있습니다."</string> + <string name="permlab_clearAppCache">"모든 응용프로그램 캐시 데이터 삭제"</string> + <string name="permdesc_clearAppCache">"응용프로그램이 응용프로그램 캐시 디렉토리에 있는 파일을 삭제하여 전화기의 저장공간을 늘릴 수 있습니다. 액세스는 일반적으로 시스템 프로세스로 제한됩니다."</string> + <string name="permlab_readLogs">"시스템 로그 파일 읽기"</string> + <string name="permdesc_readLogs">"응용프로그램이 시스템의 다양한 로그 파일을 읽을 수 있습니다. 이 경우 응용프로그램은 사용자가 전화기로 수행하는 작업에 대한 일반적인 정보를 검색할 수 있지만 여기에 개인 정보는 포함되어서는 안 됩니다."</string> + <string name="permlab_diagnostic">"진단 그룹 소유의 리소스 읽기/작성"</string> + <string name="permdesc_diagnostic">"응용프로그램이 진단 그룹 소유의 리소스(예: /dev에 있는 파일)를 읽고 쓸 수 있습니다. 이 기능은 시스템 안정성 및 보안에 영향을 미칠 수 있으므로, 제조업체 또는 사업자가 하드웨어 관련 진단을 수행하는 경우에만 사용해야 합니다."</string> + <string name="permlab_changeComponentState">"응용프로그램 구성요소 사용 또는 사용 안 함"</string> + <string name="permdesc_changeComponentState">"응용프로그램이 다른 응용프로그램 구성요소의 사용 여부를 변경할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 중요한 전화기 기능을 사용하지 않도록 설정할 수 있습니다. 응용프로그램 구성요소가 쓸모없게 되거나, 일관성이 없어지거나, 불안정해질 수 있으므로 이 권한을 설정할 때는 주의해야 합니다."</string> + <string name="permlab_setPreferredApplications">"기본 응용프로그램 설정"</string> + <string name="permdesc_setPreferredApplications">"응용프로그램이 기본 응용프로그램을 수정할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 기존 응용프로그램이 사용자의 개인 데이터를 수집하도록 스푸핑하여 실행되는 응용프로그램을 변경할 수 있습니다."</string> + <string name="permlab_writeSettings">"전체 시스템 설정 수정"</string> + <string name="permdesc_writeSettings">"응용프로그램이 시스템의 설정 데이터를 수정할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 시스템 구성을 손상시킬 수 있습니다."</string> + <string name="permlab_writeSecureSettings">"보안 시스템 설정 수정"</string> + <string name="permdesc_writeSecureSettings">"응용프로그램이 시스템 보안 설정값 데이터를 수정할 수 있습니다. 일반 응용프로그램에서는 사용하지 않습니다."</string> + <string name="permlab_writeGservices">"Google 서비스 지도 수정"</string> + <string name="permdesc_writeGservices">"응용프로그램이 Google 서비스 지도를 수정할 수 있습니다. 일반 응용프로그램에서는 사용하지 않습니다."</string> + <string name="permlab_receiveBootCompleted">"부팅할 때 자동 시작"</string> + <string name="permdesc_receiveBootCompleted">"응용프로그램이 시스템 부팅이 끝난 후 바로 시작할 수 있습니다. 이 경우 전화기가 시작하는 데 시간이 오래 걸리고 응용프로그램이 항상 실행되어 전체 전화기 속도가 느려질 수 있습니다."</string> + <string name="permlab_broadcastSticky">"남은 브로드캐스트 보내기"</string> + <string name="permdesc_broadcastSticky">"응용프로그램이 브로드캐스트가 끝난 후에 남은 브로드캐스트를 보낼 수 있습니다. 악성 응용프로그램은 전화기가 메모리를 너무 많이 사용하도록 하여 속도를 저하시키거나 불안정하게 만들 수 있습니다."</string> + <string name="permlab_readContacts">"연락처 데이터 읽기"</string> + <string name="permdesc_readContacts">"응용프로그램이 전화기에 저장된 모든 연락처(주소) 데이터를 읽을 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 데이터를 다른 사람에게 보낼 수 있습니다."</string> + <string name="permlab_writeContacts">"연락처 데이터 작성"</string> + <string name="permdesc_writeContacts">"응용프로그램이 전화기에 저장된 연락처(주소) 데이터를 수정할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 연락처 데이터를 지우거나 수정할 수 있습니다."</string> + <string name="permlab_writeOwnerData">"소유자 데이터 작성"</string> + <string name="permdesc_writeOwnerData">"응용프로그램이 전화기에 저장된 전화기 소유자 데이터를 수정할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 소유자 데이터를 지우거나 수정할 수 있습니다."</string> + <string name="permlab_readOwnerData">"소유자 데이터 읽기"</string> + <string name="permdesc_readOwnerData">"응용프로그램이 전화기에 저장된 전화기 소유자 데이터를 읽을 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 전화기 소유자 데이터를 읽을 수 있습니다."</string> + <string name="permlab_readCalendar">"캘린더 데이터 읽기"</string> + <string name="permdesc_readCalendar">"응용프로그램이 전화기에 저장된 모든 캘린더 일정을 읽을 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 캘린더 일정을 다른 사람에게 보낼 수 있습니다."</string> + <string name="permlab_writeCalendar">"캘린더 데이터 작성"</string> + <string name="permdesc_writeCalendar">"응용프로그램이 전화기에 저장된 캘린더 일정을 수정할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 캘린더 데이터를 지우거나 수정할 수 있습니다."</string> + <string name="permlab_accessMockLocation">"테스트를 위해 위치 소스로 가장"</string> + <string name="permdesc_accessMockLocation">"테스트용 가짜 위치 소스를 만듭니다. 악성 응용프로그램은 이 기능을 이용하여 GPS, 네트워크 제공업체 같은 실제 위치 소스에서 반환한 위치 및/또는 상태를 덮어쓸 수 있습니다."</string> + <string name="permlab_accessLocationExtraCommands">"추가 위치 제공업체 명령 액세스"</string> + <string name="permdesc_accessLocationExtraCommands">"추가 위치 제공업체 명령에 액세스합니다. 악성 응용프로그램은 이 기능을 이용하여 GPS 또는 기타 위치 소스의 작동을 방해할 수 있습니다."</string> + <string name="permlab_accessFineLocation">"자세한 (GPS) 위치"</string> + <string name="permdesc_accessFineLocation">"가능한 경우 전화기에서 GPS(범지구 위치 측정 시스템) 등의 자세한 위치 소스에 액세스합니다. 악성 응용프로그램은 이 기능을 이용하여 사용자의 위치를 측정하고 추가 배터리 전원을 소비할 수 있습니다."</string> + <string name="permlab_accessCoarseLocation">"광범위한 네트워크 기반 위치"</string> + <string name="permdesc_accessCoarseLocation">"전화기의 대략적인 위치를 측정하기 위해 셀룰러 네트워크 데이터베이스와 같은 광범위한 위치 소스에 액세스합니다. 악성 응용프로그램은 이 기능을 이용하여 사용자의 위치를 대략적으로 측정할 수 있습니다."</string> + <string name="permlab_accessSurfaceFlinger">"SurfaceFlinger 액세스"</string> + <string name="permdesc_accessSurfaceFlinger">"응용프로그램이 SurfaceFlinger의 하위 수준 기능을 사용할 수 있습니다."</string> + <string name="permlab_readFrameBuffer">"프레임 버퍼 읽기"</string> + <string name="permdesc_readFrameBuffer">"응용프로그램이 프레임 버퍼의 콘텐츠를 읽을 수 있습니다."</string> + <string name="permlab_modifyAudioSettings">"오디오 설정 변경"</string> + <string name="permdesc_modifyAudioSettings">"응용프로그램이 볼륨 및 경로 지정 같은 전체 오디오 설정을 수정할 수 있습니다."</string> + <string name="permlab_recordAudio">"오디오 녹음"</string> + <string name="permdesc_recordAudio">"응용프로그램이 오디오 레코드 경로에 액세스할 수 있습니다."</string> + <string name="permlab_camera">"사진 촬영"</string> + <string name="permdesc_camera">"응용프로그램이 카메라로 사진을 찍을 수 있습니다. 이 경우 응용프로그램은 카메라에 표시되는 이미지를 언제든지 수집할 수 있습니다."</string> + <string name="permlab_brick">"전화기를 영구적으로 비활성화"</string> + <string name="permdesc_brick">"응용프로그램이 전화기를 영구적으로 사용하지 않도록 설정할 수 있습니다. 이 기능은 매우 위험합니다."</string> + <string name="permlab_reboot">"전화기 강제로 다시 부팅"</string> + <string name="permdesc_reboot">"응용프로그램이 전화기를 강제로 다시 부팅할 수 있습니다."</string> + <string name="permlab_mount_unmount_filesystems">"파일시스템 마운트 및 마운트 해제"</string> + <string name="permdesc_mount_unmount_filesystems">"응용프로그램이 이동식 저장소의 파일시스템을 마운트하고 마운트 해제할 수 있습니다."</string> + <!-- no translation found for permlab_mount_format_filesystems (5523285143576718981) --> + <skip /> + <!-- no translation found for permdesc_mount_format_filesystems (574060044906047386) --> + <skip /> + <string name="permlab_vibrate">"진동 제어"</string> + <string name="permdesc_vibrate">"응용프로그램이 진동을 제어할 수 있습니다."</string> + <string name="permlab_flashlight">"손전등 제어"</string> + <string name="permdesc_flashlight">"응용프로그램이 손전등을 제어할 수 있습니다."</string> + <string name="permlab_hardware_test">"하드웨어 테스트"</string> + <string name="permdesc_hardware_test">"응용프로그램이 하드웨어를 테스트할 목적으로 다양한 주변장치를 제어할 수 있습니다."</string> + <string name="permlab_callPhone">"전화번호로 직접 전화걸기"</string> + <string name="permdesc_callPhone">"응용프로그램이 사용자의 조작 없이 전화번호로 전화를 걸 수 있습니다. 악성 응용프로그램은 전화요금 청구서에 예상치 못한 통화 요금이 부과되도록 할 수 있습니다. 이 권한으로 응용프로그램은 비상 전화를 걸 수 없습니다."</string> + <string name="permlab_callPrivileged">"전화번호로 직접 전화걸기"</string> + <string name="permdesc_callPrivileged">"응용프로그램이 사용자의 조작 없이 비상 번호를 포함한 전화번호로 전화를 걸 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 응급 서비스를 불필요하거나 불법적으로 호출할 수 있습니다."</string> + <string name="permlab_locationUpdates">"위치 업데이트 알림 제어"</string> + <string name="permdesc_locationUpdates">"무선의 위치 업데이트 알림을 활성화하거나 비활성화할 수 있습니다. 일반 응용프로그램에서는 사용하지 않습니다."</string> + <string name="permlab_checkinProperties">"체크인 속성 액세스"</string> + <string name="permdesc_checkinProperties">"체크인 서비스에서 업로드한 속성에 대한 읽기/쓰기 액세스를 허용합니다. 일반 응용프로그램에서는 사용하지 않습니다."</string> + <!-- no translation found for permlab_bindGadget (2519859363977647275) --> + <skip /> + <!-- no translation found for permdesc_bindGadget (7865866514555126333) --> + <skip /> + <string name="permlab_modifyPhoneState">"전화기 상태 수정"</string> + <string name="permdesc_modifyPhoneState">"응용프로그램이 장치의 전화 기능을 제어할 수 있습니다. 이 권한을 갖는 응용프로그램은 사용자에게 알리지 않고 네트워크를 전환하거나, 전화 무선 기능을 켜고 끌 수 있습니다."</string> + <string name="permlab_readPhoneState">"전화기 상태 읽기"</string> + <string name="permdesc_readPhoneState">"응용프로그램이 장치의 전화 기능에 액세스할 수 있습니다. 이 권한을 갖는 응용프로그램은 전화기의 전화번호, 통화가 활성인지 여부, 통화가 연결된 번호 등을 확인할 수 있습니다."</string> + <string name="permlab_wakeLock">"전화기가 절전 모드로 전환되지 않도록 설정"</string> + <string name="permdesc_wakeLock">"응용프로그램이 전화기가 절전 모드로 전환되지 않도록 할 수 있습니다."</string> + <string name="permlab_devicePower">"전화기 전원 켜고 끄기"</string> + <string name="permdesc_devicePower">"응용프로그램이 전화기를 켜거나 끌 수 있습니다."</string> + <string name="permlab_factoryTest">"출고 테스트 모드로 실행"</string> + <string name="permdesc_factoryTest">"전화기 하드웨어에 대한 완전한 액세스를 허용하는 하위 수준의 제조업체 테스트로 실행됩니다. 전화기가 제조업체 테스트 모드로 실행 중일 때만 사용할 수 있습니다."</string> + <string name="permlab_setWallpaper">"배경화면 설정"</string> + <string name="permdesc_setWallpaper">"응용프로그램이 시스템 배경화면을 설정할 수 있습니다."</string> + <string name="permlab_setWallpaperHints">"배경화면 크기 힌트 설정"</string> + <string name="permdesc_setWallpaperHints">"응용프로그램이 시스템 배경화면 크기 힌트를 설정할 수 있습니다."</string> + <string name="permlab_masterClear">"시스템을 공장 기본값으로 재설정"</string> + <string name="permdesc_masterClear">"응용프로그램이 모든 데이터, 구성 및 설치된 응용프로그램을 지워서 시스템을 공장 기본값으로 완전히 재설정할 수 있습니다."</string> + <string name="permlab_setTimeZone">"표준시간대 설정"</string> + <string name="permdesc_setTimeZone">"응용프로그램이 전화기의 표준시간대를 변경할 수 있습니다."</string> + <string name="permlab_getAccounts">"알려진 계정 검색"</string> + <string name="permdesc_getAccounts">"응용프로그램이 전화기에 알려진 계정 목록을 가져올 수 있습니다."</string> + <string name="permlab_accessNetworkState">"네트워크 상태 보기"</string> + <string name="permdesc_accessNetworkState">"응용프로그램이 모든 네트워크의 상태를 볼 수 있습니다."</string> + <string name="permlab_createNetworkSockets">"인터넷에 완전히 액세스"</string> + <string name="permdesc_createNetworkSockets">"응용프로그램이 네트워크 소켓을 만들 수 있습니다."</string> + <string name="permlab_writeApnSettings">"액세스포인트 이름 설정 쓰기"</string> + <string name="permdesc_writeApnSettings">"응용프로그램이 APN의 프록시 및 포트 같은 APN 설정을 수정할 수 있습니다."</string> + <string name="permlab_changeNetworkState">"네트워크 연결 변경"</string> + <string name="permdesc_changeNetworkState">"응용프로그램이 네트워크 연결 상태를 변경할 수 있습니다."</string> + <!-- no translation found for permlab_changeBackgroundDataSetting (1400666012671648741) --> + <skip /> + <!-- no translation found for permdesc_changeBackgroundDataSetting (1001482853266638864) --> + <skip /> + <string name="permlab_accessWifiState">"Wi-Fi 상태 보기"</string> + <string name="permdesc_accessWifiState">"응용프로그램이 Wi-Fi의 상태에 대한 정보를 볼 수 있습니다."</string> + <string name="permlab_changeWifiState">"Wi-Fi 상태 변경"</string> + <string name="permdesc_changeWifiState">"응용프로그램이 Wi-Fi 액세스포인트에 연결하거나 연결을 끊고, 구성된 Wi-Fi 네트워크를 변경할 수 있습니다."</string> + <string name="permlab_bluetoothAdmin">"Bluetooth 관리"</string> + <string name="permdesc_bluetoothAdmin">"응용프로그램이 로컬 Bluetooth 전화를 구성한 다음 원격 장치를 검색하여 페어링할 수 있습니다."</string> + <string name="permlab_bluetooth">"Bluetooth 연결 만들기"</string> + <string name="permdesc_bluetooth">"응용프로그램이 로컬 Bluetooth 전화의 구성을 보고 페어링된 장치에 연결하거나 연결을 수락할 수 있습니다."</string> + <string name="permlab_disableKeyguard">"키 잠금 사용 안 함"</string> + <string name="permdesc_disableKeyguard">"응용프로그램이 키 잠금 및 연결된 비밀번호 보안을 비활성화할 수 있습니다. 예를 들어, 전화기가 수신전화를 받을 때 키 잠금을 비활성화했다가 통화가 끝나면 키 잠금을 다시 활성화할 수 있습니다."</string> + <string name="permlab_readSyncSettings">"동기화 설정 읽기"</string> + <string name="permdesc_readSyncSettings">"응용프로그램이 연락처에 대해 동기화를 활성화할지 여부 같은 동기화 설정을 읽을 수 있습니다."</string> + <string name="permlab_writeSyncSettings">"동기화 설정 쓰기"</string> + <string name="permdesc_writeSyncSettings">"응용프로그램이 연락처에 대해 동기화를 활성화할지 여부 같은 동기화 설정을 수정할 수 있습니다."</string> + <string name="permlab_readSyncStats">"동기화 통계 읽기"</string> + <string name="permdesc_readSyncStats">"응용프로그램이 동기화 통계(예: 실행된 동기화 기록)을 읽을 수 있습니다."</string> + <string name="permlab_subscribedFeedsRead">"가입된 피드 읽기"</string> + <string name="permdesc_subscribedFeedsRead">"응용프로그램이 현재 동기화된 피드에 대한 상세정보를 가져올 수 있습니다."</string> + <string name="permlab_subscribedFeedsWrite">"가입 피드 작성"</string> + <string name="permdesc_subscribedFeedsWrite">"응용프로그램이 현재 동기화된 피드를 수정할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 동기화된 피드를 변경할 수 있습니다."</string> + <!-- no translation found for permlab_readDictionary (432535716804748781) --> + <skip /> + <!-- no translation found for permdesc_readDictionary (1082972603576360690) --> + <skip /> + <!-- no translation found for permlab_writeDictionary (6703109511836343341) --> + <skip /> + <!-- no translation found for permdesc_writeDictionary (2241256206524082880) --> + <skip /> + <string-array name="phoneTypes"> + <item>"집"</item> + <item>"휴대전화"</item> + <item>"회사"</item> + <item>"회사 팩스"</item> + <item>"집(팩스)"</item> + <item>"호출기"</item> + <item>"기타"</item> + <item>"사용자설정"</item> + </string-array> + <string-array name="emailAddressTypes"> + <item>"집"</item> + <item>"회사"</item> + <item>"기타"</item> + <item>"사용자설정"</item> + </string-array> + <string-array name="postalAddressTypes"> + <item>"집"</item> + <item>"회사"</item> + <item>"기타"</item> + <item>"사용자설정"</item> + </string-array> + <string-array name="imAddressTypes"> + <item>"집"</item> + <item>"회사"</item> + <item>"기타"</item> + <item>"사용자설정"</item> + </string-array> + <string-array name="organizationTypes"> + <item>"회사"</item> + <item>"기타"</item> + <item>"사용자설정"</item> + </string-array> + <string-array name="imProtocols"> + <item>"AIM"</item> + <item>"Windows Live"</item> + <item>"Yahoo"</item> + <item>"Skype"</item> + <item>"QQ"</item> + <item>"Google 토크"</item> + <item>"ICQ"</item> + <item>"Jabber"</item> + </string-array> + <string name="keyguard_password_enter_pin_code">"PIN 코드 입력"</string> + <string name="keyguard_password_wrong_pin_code">"PIN 코드가 잘못되었습니다."</string> + <string name="keyguard_label_text">"잠금해제하려면 메뉴를 누른 다음 0을 누릅니다."</string> + <string name="emergency_call_dialog_number_for_display">"비상 전화번호"</string> + <string name="lockscreen_carrier_default">"(서비스 안 됨)"</string> + <string name="lockscreen_screen_locked">"화면 잠김"</string> + <string name="lockscreen_instructions_when_pattern_enabled">"비상 전화를 걸거나 잠금해제하려면 메뉴를 누르세요."</string> + <string name="lockscreen_instructions_when_pattern_disabled">"잠금해제하려면 메뉴를 누르세요."</string> + <string name="lockscreen_pattern_instructions">"잠금해제를 위해 패턴 그리기"</string> + <string name="lockscreen_emergency_call">"비상 전화"</string> + <string name="lockscreen_pattern_correct">"맞습니다."</string> + <string name="lockscreen_pattern_wrong">"죄송합니다. 다시 시도하세요."</string> + <string name="lockscreen_plugged_in">"충전 중(<xliff:g id="NUMBER">%d%%</xliff:g>)"</string> + <string name="lockscreen_low_battery">"충전기를 연결하세요."</string> + <string name="lockscreen_missing_sim_message_short">"SIM 카드가 없습니다."</string> + <string name="lockscreen_missing_sim_message">"전화기에 SIM 카드가 없습니다."</string> + <string name="lockscreen_missing_sim_instructions">"SIM 카드를 삽입하세요."</string> + <string name="lockscreen_network_locked_message">"네트워크 잠김"</string> + <string name="lockscreen_sim_puk_locked_message">"SIM 카드의 PUK가 잠겨 있습니다."</string> + <string name="lockscreen_sim_puk_locked_instructions">"고객지원팀에 문의하세요."</string> + <string name="lockscreen_sim_locked_message">"SIM 카드가 잠겨 있습니다."</string> + <string name="lockscreen_sim_unlock_progress_dialog_message">"SIM 카드 잠금해제 중..."</string> + <string name="lockscreen_too_many_failed_attempts_dialog_message">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 가져왔습니다. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도하세요."</string> + <string name="lockscreen_failed_attempts_almost_glogin">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 그렸습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 실패하면 Google 로그인을 통해 전화기를 잠금해제하도록 요청됩니다."\n\n" <xliff:g id="NUMBER_2">%d</xliff:g>초 후에 다시 시도하세요."</string> + <string name="lockscreen_too_many_failed_attempts_countdown">"<xliff:g id="NUMBER">%d</xliff:g>초 후에 다시 입력하세요."</string> + <string name="lockscreen_forgot_pattern_button_text">"패턴을 잊으셨나요?"</string> + <string name="lockscreen_glogin_too_many_attempts">"패턴을 너무 많이 시도했습니다."</string> + <string name="lockscreen_glogin_instructions">"잠금해제하려면"\n"Google 계정으로 로그인하세요."</string> + <string name="lockscreen_glogin_username_hint">"사용자 이름(이메일)"</string> + <string name="lockscreen_glogin_password_hint">"비밀번호"</string> + <string name="lockscreen_glogin_submit_button">"로그인"</string> + <string name="lockscreen_glogin_invalid_input">"사용자 이름 또는 비밀번호가 잘못되었습니다."</string> + <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string> + <string name="hour_minute_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> + <string name="hour_minute_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> + <!-- no translation found for hour_ampm (7618670480400517084) --> + <skip /> + <!-- no translation found for hour_cap_ampm (5117798389811605468) --> + <skip /> + <string name="status_bar_clear_all_button">"알림 지우기"</string> + <string name="status_bar_no_notifications_title">"알림 없음"</string> + <string name="status_bar_ongoing_events_title">"사용 중"</string> + <string name="status_bar_latest_events_title">"알림"</string> + <!-- no translation found for battery_status_text_percent_format (8818848472818880005) --> + <skip /> + <string name="battery_status_charging">"충전 중..."</string> + <string name="battery_low_title">"충전기를 연결하세요."</string> + <string name="battery_low_subtitle">"배터리 전원이 부족합니다."</string> + <string name="battery_low_percent_format">"<xliff:g id="NUMBER">%d%%</xliff:g> 미만 남음"</string> + <string name="factorytest_failed">"출고 테스트 불합격"</string> + <string name="factorytest_not_system">"FACTORY_TEST 작업은 /system/app 디렉토리에 설치된 패키지에 대해서만 지원됩니다."</string> + <string name="factorytest_no_action">"FACTORY_TEST 작업을 제공하는 패키지가 없습니다."</string> + <string name="factorytest_reboot">"다시 부팅"</string> + <!-- no translation found for js_dialog_title (8143918455087008109) --> + <skip /> + <!-- no translation found for js_dialog_title_default (6961903213729667573) --> + <skip /> + <!-- no translation found for js_dialog_before_unload (1901675448179653089) --> + <skip /> + <string name="save_password_label">"확인"</string> + <string name="save_password_message">"브라우저에 이 비밀번호를 저장하시겠습니까?"</string> + <string name="save_password_notnow">"나중에"</string> + <string name="save_password_remember">"저장"</string> + <string name="save_password_never">"저장 안 함"</string> + <string name="open_permission_deny">"페이지를 열 수 있는 권한이 없습니다."</string> + <string name="text_copied">"텍스트가 클립보드에 복사되었습니다."</string> + <string name="more_item_label">"자세히"</string> + <string name="prepend_shortcut_label">"Menu+"</string> + <string name="menu_space_shortcut_label">"스페이스바"</string> + <string name="menu_enter_shortcut_label">"입력"</string> + <string name="menu_delete_shortcut_label">"삭제"</string> + <string name="search_go">"검색"</string> + <string name="today">"오늘"</string> + <string name="yesterday">"어제"</string> + <string name="tomorrow">"내일"</string> + <string name="oneMonthDurationPast">"한 달 전"</string> + <string name="beforeOneMonthDurationPast">"한 달 전"</string> + <plurals name="num_seconds_ago"> + <item quantity="one">"1초 전"</item> + <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g>초 전"</item> + </plurals> + <plurals name="num_minutes_ago"> + <item quantity="one">"1분 전"</item> + <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g>분 전"</item> + </plurals> + <plurals name="num_hours_ago"> + <item quantity="one">"1시간 전"</item> + <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g>시간 전"</item> + </plurals> + <plurals name="num_days_ago"> + <item quantity="one">"어제"</item> + <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g>일 전"</item> + </plurals> + <plurals name="in_num_seconds"> + <item quantity="one">"1초 내"</item> + <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g>초 후"</item> + </plurals> + <plurals name="in_num_minutes"> + <item quantity="one">"1분 내"</item> + <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g>분 후"</item> + </plurals> + <plurals name="in_num_hours"> + <item quantity="one">"1시간 내"</item> + <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g>시간 후"</item> + </plurals> + <plurals name="in_num_days"> + <item quantity="one">"내일"</item> + <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g>일 후"</item> + </plurals> + <!-- no translation found for abbrev_num_seconds_ago:one (1849036840200069118) --> + <!-- no translation found for abbrev_num_seconds_ago:other (3699169366650930415) --> + <!-- no translation found for abbrev_num_minutes_ago:one (6361490147113871545) --> + <!-- no translation found for abbrev_num_minutes_ago:other (851164968597150710) --> + <!-- no translation found for abbrev_num_hours_ago:one (4796212039724722116) --> + <!-- no translation found for abbrev_num_hours_ago:other (6889970745748538901) --> + <!-- no translation found for abbrev_num_days_ago:one (8463161711492680309) --> + <!-- no translation found for abbrev_num_days_ago:other (3453342639616481191) --> + <!-- no translation found for abbrev_in_num_seconds:one (5842225370795066299) --> + <!-- no translation found for abbrev_in_num_seconds:other (5495880108825805108) --> + <!-- no translation found for abbrev_in_num_minutes:one (562786149928284878) --> + <!-- no translation found for abbrev_in_num_minutes:other (4216113292706568726) --> + <!-- no translation found for abbrev_in_num_hours:one (3274708118124045246) --> + <!-- no translation found for abbrev_in_num_hours:other (3705373766798013406) --> + <!-- no translation found for abbrev_in_num_days:one (2178576254385739855) --> + <!-- no translation found for abbrev_in_num_days:other (2973062968038355991) --> + <string name="preposition_for_date">"%s"</string> + <string name="preposition_for_time">"%s"</string> + <string name="preposition_for_year">"%s년"</string> + <string name="day">"일"</string> + <string name="days">"일"</string> + <string name="hour">"시간"</string> + <string name="hours">"시간"</string> + <string name="minute">"분"</string> + <string name="minutes">"분"</string> + <string name="second">"초"</string> + <string name="seconds">"초"</string> + <string name="week">"주"</string> + <string name="weeks">"주"</string> + <string name="year">"년"</string> + <string name="years">"년"</string> + <string name="sunday">"일요일"</string> + <string name="monday">"월요일"</string> + <string name="tuesday">"화요일"</string> + <string name="wednesday">"수요일"</string> + <string name="thursday">"목요일"</string> + <string name="friday">"금요일"</string> + <string name="saturday">"토요일"</string> + <string name="every_weekday">"주중 매일(월-금)"</string> + <string name="daily">"매일"</string> + <string name="weekly">"매주 <xliff:g id="DAY">%s</xliff:g>"</string> + <string name="monthly">"매월"</string> + <string name="yearly">"매년"</string> + <string name="VideoView_error_title">"동영상 재생 안 됨"</string> + <string name="VideoView_error_text_unknown">"죄송합니다. 동영상을 재생할 수 없습니다."</string> + <string name="VideoView_error_button">"확인"</string> + <string name="am">"AM"</string> + <string name="pm">"PM"</string> + <string name="numeric_date">"<xliff:g id="MONTH">%m</xliff:g>/<xliff:g id="DAY">%d</xliff:g>/<xliff:g id="YEAR">%Y</xliff:g>"</string> + <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> + <string name="wday1_date1_wday2_date2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="WEEKDAY2">%4$s</xliff:g>"</string> + <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> + <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>"</string> + <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> – <xliff:g id="TIME2">%2$s</xliff:g>"</string> + <string name="time_wday_date">"<xliff:g id="DATE">%3$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string> + <string name="wday_date">"<xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> + <string name="time_date">"<xliff:g id="DATE">%3$s</xliff:g>, <xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string> + <!-- no translation found for date_time (6104442718633642836) --> + <skip /> + <!-- no translation found for relative_time (1818557177829411417) --> + <skip /> + <string name="time_wday">"<xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string> + <string name="full_date_month_first">"<xliff:g id="YEAR">yyyy</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>', '<xliff:g id="DAY">d</xliff:g>"</string> + <string name="full_date_day_first">"<xliff:g id="YEAR">yyyy</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>', '<xliff:g id="DAY">d</xliff:g>"</string> + <string name="medium_date_month_first">"<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_day_first">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> + <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> + <string name="noon">"정오"</string> + <string name="Noon">"정오"</string> + <string name="midnight">"자정"</string> + <string name="Midnight">"자정"</string> + <!-- no translation found for month_day (5565829181417740906) --> + <skip /> + <!-- no translation found for month (7026169712234774086) --> + <skip /> + <string name="month_day_year">"<xliff:g id="YEAR">%Y</xliff:g>, <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="DAY">%-d</xliff:g>"</string> + <!-- no translation found for month_year (9219019380312413367) --> + <skip /> + <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> + <string name="date_and_time">"<xliff:g id="YEAR">%Y</xliff:g>, <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="DAY">%-d</xliff:g>, <xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> + <string name="same_year_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>"</string> + <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> + <string name="same_year_mdy1_mdy2">"<xliff:g id="YEAR">%9$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>"</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="YEAR">%9$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> + <string name="same_year_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="numeric_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>"</string> + <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> + <string name="numeric_mdy1_mdy2">"<xliff:g id="YEAR1">%4$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>/"</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="YEAR1">%4$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> + <string name="numeric_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="same_month_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>"</string> + <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> + <string name="same_month_mdy1_mdy2">"<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>"</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> + <string name="same_month_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="abbrev_month_day_year">"<xliff:g id="YEAR">%Y</xliff:g> <xliff:g id="MONTH">%b</xliff:g>, <xliff:g id="DAY">%-d</xliff:g>"</string> + <!-- no translation found for abbrev_month_year (3856424847226891943) --> + <skip /> + <!-- no translation found for abbrev_month_day (5028815883653985933) --> + <skip /> + <!-- no translation found for abbrev_month (3131032032850777433) --> + <skip /> + <string name="day_of_week_long_sunday">"일요일"</string> + <string name="day_of_week_long_monday">"월요일"</string> + <string name="day_of_week_long_tuesday">"화요일"</string> + <string name="day_of_week_long_wednesday">"수요일"</string> + <string name="day_of_week_long_thursday">"목요일"</string> + <string name="day_of_week_long_friday">"금요일"</string> + <string name="day_of_week_long_saturday">"토요일"</string> + <string name="day_of_week_medium_sunday">"일요일"</string> + <string name="day_of_week_medium_monday">"월"</string> + <string name="day_of_week_medium_tuesday">"화"</string> + <string name="day_of_week_medium_wednesday">"수"</string> + <string name="day_of_week_medium_thursday">"목"</string> + <string name="day_of_week_medium_friday">"금"</string> + <string name="day_of_week_medium_saturday">"토"</string> + <string name="day_of_week_short_sunday">"일"</string> + <string name="day_of_week_short_monday">"월"</string> + <string name="day_of_week_short_tuesday">"화"</string> + <string name="day_of_week_short_wednesday">"수"</string> + <string name="day_of_week_short_thursday">"목"</string> + <string name="day_of_week_short_friday">"금"</string> + <string name="day_of_week_short_saturday">"토"</string> + <string name="day_of_week_shorter_sunday">"일"</string> + <string name="day_of_week_shorter_monday">"월"</string> + <string name="day_of_week_shorter_tuesday">"화"</string> + <string name="day_of_week_shorter_wednesday">"수"</string> + <string name="day_of_week_shorter_thursday">"목"</string> + <string name="day_of_week_shorter_friday">"금"</string> + <string name="day_of_week_shorter_saturday">"토"</string> + <string name="day_of_week_shortest_sunday">"일"</string> + <string name="day_of_week_shortest_monday">"3월"</string> + <string name="day_of_week_shortest_tuesday">"목"</string> + <string name="day_of_week_shortest_wednesday">"수"</string> + <string name="day_of_week_shortest_thursday">"목"</string> + <string name="day_of_week_shortest_friday">"금"</string> + <string name="day_of_week_shortest_saturday">"토"</string> + <string name="month_long_january">"1월"</string> + <string name="month_long_february">"2월"</string> + <string name="month_long_march">"3월"</string> + <string name="month_long_april">"4월"</string> + <string name="month_long_may">"5월"</string> + <string name="month_long_june">"6월"</string> + <string name="month_long_july">"7월"</string> + <string name="month_long_august">"8월"</string> + <string name="month_long_september">"9월"</string> + <string name="month_long_october">"10월"</string> + <string name="month_long_november">"11월"</string> + <string name="month_long_december">"12월"</string> + <string name="month_medium_january">"1월"</string> + <string name="month_medium_february">"2월"</string> + <string name="month_medium_march">"3월"</string> + <string name="month_medium_april">"4월"</string> + <string name="month_medium_may">"5월"</string> + <string name="month_medium_june">"6월"</string> + <string name="month_medium_july">"7월"</string> + <string name="month_medium_august">"8월"</string> + <string name="month_medium_september">"9월"</string> + <string name="month_medium_october">"10월"</string> + <string name="month_medium_november">"11월"</string> + <string name="month_medium_december">"12월"</string> + <string name="month_shortest_january">"1월"</string> + <string name="month_shortest_february">"금"</string> + <string name="month_shortest_march">"3월"</string> + <string name="month_shortest_april">"4월"</string> + <string name="month_shortest_may">"5월"</string> + <string name="month_shortest_june">"6월"</string> + <string name="month_shortest_july">"7월"</string> + <string name="month_shortest_august">"8월"</string> + <string name="month_shortest_september">"9월"</string> + <string name="month_shortest_october">"10월"</string> + <string name="month_shortest_november">"11월"</string> + <string name="month_shortest_december">"12월"</string> + <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> + <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> + <string name="selectAll">"모두 선택"</string> + <string name="selectText">"텍스트 선택"</string> + <string name="stopSelectingText">"텍스트 선택 중지"</string> + <string name="cut">"잘라내기"</string> + <string name="cutAll">"모두 잘라내기"</string> + <string name="copy">"복사"</string> + <string name="copyAll">"모두 복사"</string> + <string name="paste">"붙여넣기"</string> + <string name="copyUrl">"URL 복사"</string> + <string name="inputMethod">"입력 방법"</string> + <!-- no translation found for addToDictionary (726256909274177272) --> + <skip /> + <string name="editTextMenuTitle">"텍스트 수정"</string> + <string name="low_internal_storage_view_title">"저장공간 부족"</string> + <string name="low_internal_storage_view_text">"전화기 저장공간이 부족합니다."</string> + <string name="ok">"확인"</string> + <string name="cancel">"취소"</string> + <string name="yes">"확인"</string> + <string name="no">"취소"</string> + <!-- no translation found for dialog_alert_title (2049658708609043103) --> + <skip /> + <string name="capital_on">"켜짐"</string> + <string name="capital_off">"끄기"</string> + <string name="whichApplication">"작업을 수행할 때 사용하는 권한"</string> + <string name="alwaysUse">"이 작업에 대해 기본값으로 사용"</string> + <string name="clearDefaultHintMsg">"홈 설정 > 응용프로그램 > 응용프로그램 관리에서 기본값을 지웁니다."</string> + <string name="chooseActivity">"작업 선택"</string> + <string name="noApplications">"작업을 수행할 수 있는 응용프로그램이 없습니다."</string> + <string name="aerr_title">"죄송합니다."</string> + <string name="aerr_application">"<xliff:g id="APPLICATION">%1$s</xliff:g> 응용프로그램(<xliff:g id="PROCESS">%2$s</xliff:g> 프로세스)이 예상치 않게 중지되었습니다. 다시 시도하세요."</string> + <string name="aerr_process">"<xliff:g id="PROCESS">%1$s</xliff:g> 프로세스가 예상치 않게 중지되었습니다. 다시 시도하세요."</string> + <string name="anr_title">"죄송합니다."</string> + <string name="anr_activity_application">"<xliff:g id="ACTIVITY">%1$s</xliff:g> 활동(<xliff:g id="APPLICATION">%2$s</xliff:g> 응용프로그램)이 응답하지 않습니다."</string> + <string name="anr_activity_process">"<xliff:g id="ACTIVITY">%1$s</xliff:g> 활동(<xliff:g id="PROCESS">%2$s</xliff:g> 프로세스)이 응답하지 않습니다."</string> + <string name="anr_application_process">"<xliff:g id="APPLICATION">%1$s</xliff:g> 응용프로그램(<xliff:g id="PROCESS">%2$s</xliff:g> 프로세스)이 응답하지 않습니다."</string> + <string name="anr_process">"<xliff:g id="PROCESS">%1$s</xliff:g> 프로세스가 응답하지 않습니다."</string> + <string name="force_close">"강제로 닫기"</string> + <string name="wait">"대기"</string> + <string name="debug">"디버깅"</string> + <string name="sendText">"텍스트에 대한 작업 선택"</string> + <string name="volume_ringtone">"벨소리 볼륨"</string> + <string name="volume_music">"미디어 볼륨"</string> + <string name="volume_music_hint_playing_through_bluetooth">"Bluetooth를 통해 재생"</string> + <string name="volume_call">"통화볼륨"</string> + <!-- no translation found for volume_bluetooth_call (2002891926351151534) --> + <skip /> + <string name="volume_alarm">"알람 볼륨"</string> + <string name="volume_notification">"알림 볼륨"</string> + <string name="volume_unknown">"볼륨"</string> + <string name="ringtone_default">"기본 벨소리"</string> + <string name="ringtone_default_with_actual">"기본 벨소리(<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string> + <string name="ringtone_silent">"무음"</string> + <string name="ringtone_picker_title">"벨소리"</string> + <string name="ringtone_unknown">"알 수 없는 벨소리"</string> + <plurals name="wifi_available"> + <item quantity="one">"Wi-Fi 네트워크 사용가능"</item> + <item quantity="other">"Wi-Fi 네트워크 사용가능"</item> + </plurals> + <plurals name="wifi_available_detailed"> + <item quantity="one">"개방형 Wi-Fi 네트워크 사용가능"</item> + <item quantity="other">"개방형 Wi-Fi 네트워크 사용가능"</item> + </plurals> + <string name="select_character">"문자 삽입"</string> + <string name="sms_control_default_app_name">"알 수 없는 응용프로그램"</string> + <string name="sms_control_title">"SMS 메시지를 보내는 중"</string> + <string name="sms_control_message">"여러 개의 SMS 메시지를 보내는 중입니다. 계속하려면 \'확인\'을 선택하고 전송을 중지하려면 \'취소\'를 선택하세요."</string> + <string name="sms_control_yes">"확인"</string> + <string name="sms_control_no">"취소"</string> + <string name="date_time_set">"설정"</string> + <string name="default_permission_group">"기본값"</string> + <string name="no_permissions">"권한이 필요하지 않음"</string> + <string name="perms_hide"><b>"숨기기"</b></string> + <string name="perms_show_all"><b>"모두 표시"</b></string> + <string name="googlewebcontenthelper_loading">"로드 중..."</string> + <string name="usb_storage_title">"USB 연결됨"</string> + <string name="usb_storage_message">"USB를 통해 전화기를 컴퓨터에 연결했습니다. 컴퓨터와 전화기 SD 카드 간에 파일을 복사하려면 \'마운트\'를 선택하세요."</string> + <string name="usb_storage_button_mount">"마운트"</string> + <string name="usb_storage_button_unmount">"마운트 안 함"</string> + <string name="usb_storage_error_message">"USB 저장소에 SD 카드를 사용하는 동안 문제가 발생했습니다."</string> + <string name="usb_storage_notification_title">"USB 연결됨"</string> + <string name="usb_storage_notification_message">"컴퓨터에 파일을 복사하거나 컴퓨터의 파일을 복사하려면 선택합니다."</string> + <!-- no translation found for usb_storage_stop_notification_title (2336058396663516017) --> + <skip /> + <!-- no translation found for usb_storage_stop_notification_message (2591813490269841539) --> + <skip /> + <!-- no translation found for usb_storage_stop_title (6014127947456185321) --> + <skip /> + <!-- no translation found for usb_storage_stop_message (2390958966725232848) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_mount (1181858854166273345) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_unmount (3774611918660582898) --> + <skip /> + <!-- no translation found for usb_storage_stop_error_message (3917317248440072084) --> + <skip /> + <!-- no translation found for extmedia_format_title (8663247929551095854) --> + <skip /> + <!-- no translation found for extmedia_format_message (3621369962433523619) --> + <skip /> + <!-- no translation found for extmedia_format_button_format (4131064560127478695) --> + <skip /> + <string name="select_input_method">"입력 방법 선택"</string> + <string name="fast_scroll_alphabet">"ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> + <string name="fast_scroll_numeric_alphabet">"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> + <!-- no translation found for candidates_style (4333913089637062257) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_title (5457603418970994050) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_message (4747432538578886744) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_title (780477838241212997) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_message (1312266820092958014) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_title (6410723906019100189) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_message (2679412884290061775) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_title (6872152882604407837) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_message (7260183293747448241) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_title (6729801130790616200) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_message (7613960686747592770) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_title (8902518030404381318) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_message (4205117227342822275) --> + <skip /> + <!-- no translation found for activity_list_empty (4168820609403385789) --> + <skip /> + <!-- no translation found for permlab_pkgUsageStats (8787352074326748892) --> + <skip /> + <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> + <skip /> + <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> + <skip /> +</resources> diff --git a/core/res/res/values-mcc204-ko/strings.xml b/core/res/res/values-mcc204-ko/strings.xml new file mode 100644 index 0000000..7d96230 --- /dev/null +++ b/core/res/res/values-mcc204-ko/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="locale_replacement">"nl_nl"</string> +</resources> diff --git a/core/res/res/values-mcc230-ko/strings.xml b/core/res/res/values-mcc230-ko/strings.xml new file mode 100644 index 0000000..d3ecdbb --- /dev/null +++ b/core/res/res/values-mcc230-ko/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="locale_replacement">"cs_cz"</string> +</resources> diff --git a/core/res/res/values-mcc232-ko/strings.xml b/core/res/res/values-mcc232-ko/strings.xml new file mode 100644 index 0000000..4773838 --- /dev/null +++ b/core/res/res/values-mcc232-ko/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="locale_replacement">"de_at"</string> +</resources> diff --git a/core/res/res/values-mcc234-ko/strings.xml b/core/res/res/values-mcc234-ko/strings.xml new file mode 100644 index 0000000..2538b73 --- /dev/null +++ b/core/res/res/values-mcc234-ko/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="locale_replacement">"en_gb"</string> +</resources> diff --git a/core/res/res/values-mcc260-ko/strings.xml b/core/res/res/values-mcc260-ko/strings.xml new file mode 100644 index 0000000..1161f9a --- /dev/null +++ b/core/res/res/values-mcc260-ko/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="locale_replacement">"pl_pl"</string> +</resources> diff --git a/core/res/res/values-mcc262-ko/strings.xml b/core/res/res/values-mcc262-ko/strings.xml new file mode 100644 index 0000000..9505cf4 --- /dev/null +++ b/core/res/res/values-mcc262-ko/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="locale_replacement">"de_de"</string> +</resources> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml new file mode 100644 index 0000000..9f49557 --- /dev/null +++ b/core/res/res/values-nb/strings.xml @@ -0,0 +1,842 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="byteShort">"B"</string> + <string name="kilobyteShort">"kB"</string> + <string name="megabyteShort">"MB"</string> + <string name="gigabyteShort">"GB"</string> + <string name="terabyteShort">"TB"</string> + <string name="petabyteShort">"PB"</string> + <string name="untitled">"<uten navn>"</string> + <string name="ellipsis">"…"</string> + <string name="emptyPhoneNumber">"(Mangler telefonnummer)"</string> + <string name="unknownName">"(Ukjent)"</string> + <string name="defaultVoiceMailAlphaTag">"Telefonsvarer"</string> + <string name="defaultMsisdnAlphaTag">"MSISDN1"</string> + <string name="mmiError">"Tilkoblingsproblem eller ugyldig MMI-kode."</string> + <string name="serviceEnabled">"Tjenesten ble aktivert."</string> + <string name="serviceEnabledFor">"Tjenesten ble aktivert for:"</string> + <string name="serviceDisabled">"Tjenesten ble deaktivert."</string> + <string name="serviceRegistered">"Registreringen er vellykket."</string> + <string name="serviceErased">"Registreringen ble fjernet."</string> + <string name="passwordIncorrect">"Ugyldig passord."</string> + <string name="mmiComplete">"MMI utført."</string> + <string name="badPin">"Den gamle PIN-koden du skrev inn er feil."</string> + <string name="badPuk">"PUK-koden du skrev inn er feil."</string> + <string name="mismatchPin">"PIN-kodene stemmer ikke overens."</string> + <string name="invalidPin">"PIN-koden må være mellom fire og åtte siffer."</string> + <string name="needPuk">"SIM-kortet ditt er PUK-låst. Skriv inn PUK-koden for å låse det opp."</string> + <string name="needPuk2">"Skriv inn PUK2 for å låse opp SIM-kortet."</string> + <string name="ClipMmi">"Inngående nummervisning"</string> + <string name="ClirMmi">"Utgående nummervisning"</string> + <string name="CfMmi">"Viderekobling"</string> + <string name="CwMmi">"Samtale venter"</string> + <string name="BaMmi">"Samtaleblokkering"</string> + <string name="PwdMmi">"Passordbytte"</string> + <string name="PinMmi">"PIN-kode-bytte"</string> + <string name="CLIRDefaultOnNextCallOn">"Nummervisning er begrenset som standard. Neste anrop: Begrenset"</string> + <string name="CLIRDefaultOnNextCallOff">"Nummervisning er begrenset som standard. Neste anrop: Ikke begrenset"</string> + <string name="CLIRDefaultOffNextCallOn">"Nummervisning er ikke begrenset som standard. Neste anrop: Begrenset"</string> + <string name="CLIRDefaultOffNextCallOff">"Nummervisning er ikke begrenset som standard. Neste anrop: Ikke begrenset"</string> + <string name="serviceNotProvisioned">"SIM-kortet er ikke tilrettelagt for tjenesten."</string> + <string name="CLIRPermanent">"Kunne ikke endre innstilling for nummervisning."</string> + <string name="serviceClassVoice">"Tale"</string> + <string name="serviceClassData">"Data"</string> + <string name="serviceClassFAX">"Fax"</string> + <string name="serviceClassSMS">"SMS"</string> + <string name="serviceClassDataAsync">"Asynkron"</string> + <string name="serviceClassDataSync">"Synkron"</string> + <string name="serviceClassPacket">"Pakkedata"</string> + <string name="serviceClassPAD">"PAD"</string> + <string name="cfTemplateNotForwarded">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ikke viderekoblet"</string> + <string name="cfTemplateForwarded">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> + <string name="cfTemplateForwardedTime">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> etter <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunder"</string> + <string name="cfTemplateRegistered">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ikke viderekoblet"</string> + <string name="cfTemplateRegisteredTime">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ikke viderekoblet"</string> + <string name="httpErrorOk">"OK"</string> + <string name="httpError">"Nettsiden inneholder en feil."</string> + <string name="httpErrorLookup">"Kunne ikke finne adressen."</string> + <string name="httpErrorUnsupportedAuthScheme">"Støtter ikke sidens autentiseringsmetode."</string> + <string name="httpErrorAuth">"Autentiseringen feilet."</string> + <string name="httpErrorProxyAuth">"Autentisering via mellomtjeneren feilet."</string> + <string name="httpErrorConnect">"Kunne ikke koble til tjeneren."</string> + <string name="httpErrorIO">"Klarte ikke å kommunisere med tjeneren. Prøv igjen senere."</string> + <string name="httpErrorTimeout">"Det oppsto et tidsavbrudd under tilkobling til tjeneren."</string> + <string name="httpErrorRedirectLoop">"Siden inneholder for mange videresendinger."</string> + <string name="httpErrorUnsupportedScheme">"Protokollen er ikke støttet."</string> + <string name="httpErrorFailedSslHandshake">"Kunne ikke opprette en sikker tilkobling."</string> + <string name="httpErrorBadUrl">"Kunne ikke åpne siden, siden adressen er ugyldig."</string> + <string name="httpErrorFile">"Kunne ikke åpne filen."</string> + <string name="httpErrorFileNotFound">"Fant ikke den forespurte filen."</string> + <string name="httpErrorTooManyRequests">"For mange forespørsler blir behandlet. Prøv igjen senere."</string> + <string name="contentServiceSync">"Synkronisering"</string> + <string name="contentServiceSyncNotificationTitle">"Synkronisering"</string> + <string name="contentServiceTooManyDeletesNotificationDesc">"For mange slettinger av <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string> + <string name="low_memory">"Telefonens lagringsminne er fullt! Slett noen filer for å frigjøre plass."</string> + <string name="me">"Meg"</string> + <string name="power_dialog">"Telefoninnstillinger"</string> + <string name="silent_mode">"Stillemodus"</string> + <string name="turn_on_radio">"Slå på trådløst nett"</string> + <string name="turn_off_radio">"Slå av trådløst nett"</string> + <string name="screen_lock">"Lås skjermen"</string> + <string name="power_off">"Slå av"</string> + <string name="shutdown_progress">"Avslutter…"</string> + <string name="shutdown_confirm">"Telefonen vil bli slått av."</string> + <string name="no_recent_tasks">"Ingen nylig brukte applikasjoner."</string> + <string name="global_actions">"Telefoninnstillinger"</string> + <string name="global_action_lock">"Lås skjermen"</string> + <string name="global_action_power_off">"Slå av"</string> + <string name="global_action_toggle_silent_mode">"Stillemodus"</string> + <string name="global_action_silent_mode_on_status">"Lyden er av"</string> + <string name="global_action_silent_mode_off_status">"Lyden er på"</string> + <string name="safeMode">"Sikkermodus"</string> + <string name="permgrouplab_costMoney">"Betaltjenester"</string> + <string name="permgroupdesc_costMoney">"Lar applikasjoner utføre operasjoner som kan koste deg penger."</string> + <string name="permgrouplab_messages">"Meldinger"</string> + <string name="permgroupdesc_messages">"Lese og skrive SMS, e-post og andre meldinger på telefonen."</string> + <string name="permgrouplab_personalInfo">"Personlig informasjon"</string> + <string name="permgroupdesc_personalInfo">"Direkte tilgang til kontakter og kalendre lagret på telefonen."</string> + <string name="permgrouplab_location">"Plassering"</string> + <string name="permgroupdesc_location">"Overvåking av telefonens fysiske plassering"</string> + <string name="permgrouplab_network">"Nettverkstilgang"</string> + <string name="permgroupdesc_network">"Gir applikasjoner tilgang til diverse nettverksfunksjoner."</string> + <string name="permgrouplab_accounts">"Google-kontoer"</string> + <string name="permgroupdesc_accounts">"Tilgang til tilgjengelige Google-kontoer."</string> + <string name="permgrouplab_hardwareControls">"Maskinvarekontroll"</string> + <string name="permgroupdesc_hardwareControls">"Direkte tilgang til maskinvaren på telefonen."</string> + <string name="permgrouplab_phoneCalls">"Telefonsamtaler"</string> + <string name="permgroupdesc_phoneCalls">"Overvåk, ta opp, og behandle telefonsamtaler."</string> + <string name="permgrouplab_systemTools">"Systemverktøy"</string> + <string name="permgroupdesc_systemTools">"Lavnivå tilgang og kontroll over systemet."</string> + <string name="permgrouplab_developmentTools">"Utviklingsverktøy"</string> + <string name="permgroupdesc_developmentTools">"Funksjonalitet kun utviklere trenger."</string> + <string name="permlab_statusBar">"deaktivere eller endre statusfeltet"</string> + <string name="permdesc_statusBar">"Lar applikasjonen deaktivere statusfeltet, samt legge til og fjerne systemikoner."</string> + <string name="permlab_expandStatusBar">"utvide/slå sammen statusfeltet"</string> + <string name="permdesc_expandStatusBar">"Lar applikasjonen utvide eller slå sammen statusfeltet."</string> + <string name="permlab_processOutgoingCalls">"avskjære utgående anrop"</string> + <string name="permdesc_processOutgoingCalls">"Lar applikasjonen behandle utgående anrop og endre nummeret som ringes. Ondsinnede applikasjoner kan overvåke, videresende, eller hindre utgående anrop."</string> + <string name="permlab_receiveSms">"motta SMS"</string> + <string name="permdesc_receiveSms">"Lar applikasjonen motta og behandle SMS-meldinger. Ondsinnede applikasjoner kan overvåke meldinger eller slette dem uten at de vises."</string> + <string name="permlab_receiveMms">"motta MMS"</string> + <string name="permdesc_receiveMms">"Lar applikasjonen motta og behandle MMS-meldinger. Ondsinnede applikasjoner kan overvåke meldinger eller slette dem uten at de vises."</string> + <string name="permlab_sendSms">"sende SMS-meldinger"</string> + <string name="permdesc_sendSms">"Lar applikasjonen sende SMS-meldinger. Ondsinnede applikasjoner kan koste deg penger ved å sende meldinger uten bekreftelse."</string> + <string name="permlab_readSms">"lese SMS- og MMS-meldinger"</string> + <string name="permdesc_readSms">"Lar applikasjonen lese SMS-meldinger lagret i telefonen eller på SIM-kortet. Ondsinnede applikasjoner kan lese private meldinger."</string> + <string name="permlab_writeSms">"redigere SMS- og MMS-meldinger"</string> + <string name="permdesc_writeSms">"Lar applikasjonen skrive til SMS-meldinger lagret i telefonen eller på SIM-kortet. Ondsinnede applikasjoner kan slette meldinger."</string> + <string name="permlab_receiveWapPush">"motta WAP"</string> + <string name="permdesc_receiveWapPush">"Lar applikasjonen motta og behandle WAP-meldinger. Ondsinnede applikasjoner kan overvåke meldinger eller slette dem uten at de vises."</string> + <string name="permlab_getTasks">"se kjørende applikasjoner"</string> + <string name="permdesc_getTasks">"Tillater applikasjonen å hente informasjon om aktive og nylig kjørte programmer. Kan tillate ondsinnede applikasjoner å oppdage privat informasjon om andre applikasjoner."</string> + <string name="permlab_reorderTasks">"omordne kjørende applikasjoner"</string> + <string name="permdesc_reorderTasks">"Tillater applikasjonen å flytte programmer til forgrunnen eller bakgrunnen. Ondsinnede applikasjoner kan tvinge seg selv til fronten."</string> + <string name="permlab_setDebugApp">"aktiver applikasjonsdebugging"</string> + <string name="permdesc_setDebugApp">"Lar applikasjonen skru på debugging for en annen applikasjon. Ondsinnede applikasjoner kan bruke dette til å drepe andre applikasjoner."</string> + <string name="permlab_changeConfiguration">"endre innstillingene for brukergrensesnitt"</string> + <string name="permdesc_changeConfiguration">"Tillater applikasjonen å endre gjeldende innstillinger, slik som språk eller skriftstørrelse."</string> + <string name="permlab_restartPackages">"omstarte andre applikasjoner"</string> + <string name="permdesc_restartPackages">"Lar applikasjonen tvinge andre applikasjoner til å starte på nytt."</string> + <string name="permlab_setProcessForeground">"unngå å bli stoppet"</string> + <string name="permdesc_setProcessForeground">"Lar applikasjonen sette en vilkårlig prosess i forgrunnen, så den ikke kan bli drept. Vanlige applikasjoner bør aldri trenge dette."</string> + <string name="permlab_forceBack">"tvinge applikasjoner til å lukkes"</string> + <string name="permdesc_forceBack">"Lar applikasjonen tvinge enhver aktivitet som er i forgrunnen til å lukkes og gå tilbake. Vanlige applikasjoner bør aldri trenge dette."</string> + <string name="permlab_dump">"hente intern systemtilstand"</string> + <string name="permdesc_dump">"Lar applikasjonen hente intern tilstand fra systemet. Onsdinnede applikasjoner kan hente et bredt spekter av privat og sikker informasjon som de vanligvis aldri burde ha behov for."</string> + <string name="permlab_addSystemService">"publisere lavnivåtjenester"</string> + <string name="permdesc_addSystemService">"Lar applikasjonen publisere sine egne lavnivås systemtjenester. Ondsinnede applikasjoner kan kapre systemet, og stjele eller ødelegge alle data på det."</string> + <string name="permlab_runSetActivityWatcher">"overvåke og kontrollere all applikasjonsoppstart"</string> + <string name="permdesc_runSetActivityWatcher">"Lar applikasjonen overvåke og kontrollere hvordan systemet starter applikasjoner. Ondsinnede applikasjoner kan ta over systemet helt. Denne rettigheten behøves bare for utvikling, aldri for vanlig bruk av telefonen."</string> + <string name="permlab_broadcastPackageRemoved">"kringkaste melding om fjernet pakke"</string> + <string name="permdesc_broadcastPackageRemoved">"Lar applikasjonen kringkaste en melding om at en applikasjonspakke er blitt fjernet. Ondsinnede applikasjoner kan bruke dette til å drepe vilkårlige andre kjørende applikasjoner."</string> + <string name="permlab_broadcastSmsReceived">"kringkaste melding om mottatt SMS"</string> + <string name="permdesc_broadcastSmsReceived">"Lar applikasjonen kringkaste en melding om at en SMS-melding er mottatt. Ondsinnede applikasjoner kan bruke dette til å forfalske innkommende SMS-meldinger."</string> + <string name="permlab_broadcastWapPush">"kringkaste melding om mottatt WAP-PUSH"</string> + <string name="permdesc_broadcastWapPush">"Lar applikasjonen kringkaste en melding om at en WAP-PUSH-melding er blitt mottatt. Ondsinnede applikasjoner kan bruke dette for å forfalske MMS-kvitteringer eller i det stille erstatte innholdet av vilkårlige nettsider med ondsinnede varianter."</string> + <string name="permlab_setProcessLimit">"begrense antallet kjørende prosesser"</string> + <string name="permdesc_setProcessLimit">"Lar applikasjonen kontrollere maksimalt antall kjørende prosesser. Behøves aldri for vanlige applikasjoner."</string> + <string name="permlab_setAlwaysFinish">"få alle bakgrunnsapplikasjoner til å lukkes"</string> + <string name="permdesc_setAlwaysFinish">"Lar applikasjonen kontrollere om aktiviteter alltid avsluttes når de sendes til bakgrunnen. Behøves aldri for vanlige applikasjoner."</string> + <string name="permlab_fotaUpdate">"installere systemoppdateringer automatisk"</string> + <string name="permdesc_fotaUpdate">"Lar applikasjonen motta meldinger om pågående systemoppdateringer, og starte installeringen av dem. Ondsinnede applikasjoner kan bruke dette for å skade systemet med uautoriserte oppdateringer, eller generelt forstyrre oppdateringsprosessen."</string> + <string name="permlab_batteryStats">"endre batteristatistikk"</string> + <string name="permdesc_batteryStats">"Lar applikasjonen endre på innsamlet batteristatistikk. Ikke ment for vanlige applikasjoner."</string> + <string name="permlab_internalSystemWindow">"vis uautoriserte vinduer"</string> + <string name="permdesc_internalSystemWindow">"Tillater at det opprettes vinduer ment for bruk av systemets interne brukergrensesnitt. Ikke ment for vanlige applikasjoner."</string> + <string name="permlab_systemAlertWindow">"vise advarsler på systemnivå"</string> + <string name="permdesc_systemAlertWindow">"Lar applikasjonen vise systemadvarselvinduer. Ondsinnede applikasjoner kan ta over hele skjermen."</string> + <string name="permlab_setAnimationScale">"endre global animasjonshastighet"</string> + <string name="permdesc_setAnimationScale">"Lar applikasjonen endre den globale animasjonshastigheten (raskere eller tregere animasjoner) når som helst."</string> + <string name="permlab_manageAppTokens">"styre applikasjonssymboler"</string> + <string name="permdesc_manageAppTokens">"Lar applikasjoner lage og vedlikeholde sine egne symboler, noe som lar dem overstyre den vanlige Z-ordningen. Vanlige applikasjoner bør aldri trenge dette."</string> + <string name="permlab_injectEvents">"trykke taster og kontrolknapper"</string> + <string name="permdesc_injectEvents">"Lar applikasjonen levere sine egne inndatahendelser (tastetrykk osv.) til andre applikasjoner. Ondsinnede applikasjoner kan bruke dette for å ta over telefonen."</string> + <string name="permlab_readInputState">"ta opp hva som skrives og gjøres"</string> + <string name="permdesc_readInputState">"Lar applikasjonen overvåke tastetrykk selv når interaksjonen er med et annet program (som å skrive inn et passord). Vanlige applikasjoner bør aldri trenge dette."</string> + <string name="permlab_bindInputMethod">"binde til en inndatametode"</string> + <string name="permdesc_bindInputMethod">"Lar applikasjonen binde til toppnivågrensesnittet for en inndatametode. Vanlige applikasjoner bør aldri trenge dette."</string> + <string name="permlab_setOrientation">"snu skjermen"</string> + <string name="permdesc_setOrientation">"Lar applikasjonen rotere skjermen når som helst. Vanlige applikasjoner bør aldri trenge dette."</string> + <string name="permlab_signalPersistentProcesses">"sende Linux-signaler til applikasjoner"</string> + <string name="permdesc_signalPersistentProcesses">"Lar applikasjonen spørre om at et gitt signal blir sendt til alle varige prosesser."</string> + <string name="permlab_persistentActivity">"forbli kjørende"</string> + <string name="permdesc_persistentActivity">"Lar applikasjonen gjøre deler av seg selv varig, så systemet ikke kan bruke det til andre applikasjoner."</string> + <string name="permlab_deletePackages">"slette applikasjoner"</string> + <string name="permdesc_deletePackages">"Lar applikasjonen slette Android-pakker. Ondsinnede applikasjoner kan bruke dette for å slette viktige applikasjoner."</string> + <string name="permlab_clearAppUserData">"slette andre applikasjoners data"</string> + <string name="permdesc_clearAppUserData">"Lar applikasjonen fjerne brukerdata."</string> + <string name="permlab_deleteCacheFiles">"slette andre applikasjoners hurtigbuffer"</string> + <string name="permdesc_deleteCacheFiles">"Lar applikasjonen to slette hurtigbufferfiler."</string> + <string name="permlab_getPackageSize">"måle lagringsplass for applikasjon"</string> + <string name="permdesc_getPackageSize">"Lar applikasjonen hente kode-, data- og hurtigbufferstørrelser"</string> + <string name="permlab_installPackages">"installere applikasjoner direkte"</string> + <string name="permdesc_installPackages">"Lar applikasjonen installere nye eller oppdaterte Android-pakker. Ondsinnede applikasjoner kan bruke dette for å legge til nye applikasjoner med vilkårlig kraftige rettigheter."</string> + <string name="permlab_clearAppCache">"slette hurtigbufferdata for alle applikasjoner"</string> + <string name="permdesc_clearAppCache">"Lar applikasjonen frigjøre lagringsplass ved å slette filer i applikasjoners hurtigbufferkatalog. Tilgangen er vanligvis sterkt begrenset, til systemprosesser."</string> + <string name="permlab_readLogs">"lese systemets loggfiler"</string> + <string name="permdesc_readLogs">"Lar applikasjonen to lese fra diverse loggfiler på systemet. Disse inneholder generell informasjon om hva som gjøres med telefonen, men skal ikke inneholde personlig eller privat informasjon."</string> + <string name="permlab_diagnostic">"lese/skrive ressurser eid av diag"</string> + <string name="permdesc_diagnostic">"Lar applikasjonen to lese og skrive enhver ressurs eid av gruppen diag; for eksempel, filer i /dev. Dette kan potensielt påvirke systemets sikkerhet og stabilitet. Dette bør KUN brukes for maskinvarespesifikke diagnoseverktøy laget av operatøren eller produsenten."</string> + <string name="permlab_changeComponentState">"aktivere eller deaktigere applikasjonskomponenter"</string> + <string name="permdesc_changeComponentState">"Lar applikasjonen endre om en komponent i en annen applikasjon er aktivert eller ikke. Ondsinnede applikasjoner kan bruke dette for å deaktivere viktige telefonfunksjoner. Denne rettigheten må brukes med forsiktighet, ettersom det er mulig å få applikasjonskomponenter inn i en ubrukelig, inkonsistent eller ustabil tilstand."</string> + <string name="permlab_setPreferredApplications">"velge foretrukne applikasjoner"</string> + <string name="permdesc_setPreferredApplications">"Lar applikasjonen endre valgene for foretrukne applikasjoner. Dette kan gi ondsinnede applikasjoner tilgang til i det stille å endre hvilke applikasjoner som kjøres, og slik gi seg ut for å være en eksisterende applikasjon og samle private data."</string> + <string name="permlab_writeSettings">"endre globale systeminnstillinger"</string> + <string name="permdesc_writeSettings">"Lar applikasjonen endre systemets innstillingsdata. Ondsinnede applikasjoner kan skade systemets innstillinger."</string> + <string name="permlab_writeSecureSettings">"endre sikre systeminnstillinger"</string> + <string name="permdesc_writeSecureSettings">"Lar applikasjonen endre systemets sikre innstillingsdata. Ikke ment for bruk av vanlige applikasjoner."</string> + <string name="permlab_writeGservices">"redigere Google-tjenestekartet"</string> + <string name="permdesc_writeGservices">"Lar applikasjonen redigere Google-tjenestekartet. Ikke ment for bruk av vanlige applikasjoner."</string> + <string name="permlab_receiveBootCompleted">"starte automatisk sammen med systemet"</string> + <string name="permdesc_receiveBootCompleted">"Lar applikasjonen sette opp at den selv skal starte så fort systemet er ferdig med å slå seg på. Dette kan gjøre at det tar lengre tid å starte telefonen, og at den kan bli tregere fordi applikasjonen alltid kjører."</string> + <string name="permlab_broadcastSticky">"sende varige kringkastinger"</string> + <string name="permdesc_broadcastSticky">"Lar applikasjonen sende varige kringkastinger, som forblir på systemet etter at kringkastingen er avsluttet. Ondsinnede applikasjoner kan gjøre telefonen treg eller ustabil ved å få den til å bruke for mye minne."</string> + <string name="permlab_readContacts">"lese kontaktinformasjon"</string> + <string name="permdesc_readContacts">"Lar applikasjonen lese all kontakt- og adresseinformasjon lagret på telefonen. Ondsinnede applikasjoner kan bruke dette for å sende personlige data til andre."</string> + <string name="permlab_writeContacts">"skrive kontaktinformasjon"</string> + <string name="permdesc_writeContacts">"Lar applikasjonen endre kontakt- og adresseinformasjon lagret på telefonen. Ondsinnede applikasjoner kan bruke dette for å redigere eller endre kontaktinformasjonen."</string> + <string name="permlab_writeOwnerData">"skrive eierinformasjon"</string> + <string name="permdesc_writeOwnerData">"Lar applikasjonen endre dataene om telefoneieren. Ondsinnede applikasjoner kan bruke dette til å slette eller redigere telefonens eierdata."</string> + <string name="permlab_readOwnerData">"lese eierinformasjon"</string> + <string name="permdesc_readOwnerData">"Lar applikasjonen lese dataene om telefoneieren. Ondsinnede applikasjoner kan bruke dette til å lese telefonens eierdata."</string> + <string name="permlab_readCalendar">"lese kalenderinformasjon"</string> + <string name="permdesc_readCalendar">"Lar applikasjonen lese alle kalenderhendelser lagret på telefonen. Ondsinnede applikasjoner kan bruke dette til å sende kalenderhendelser til andre."</string> + <string name="permlab_writeCalendar">"skrive kalenderinformasjon"</string> + <string name="permdesc_writeCalendar">"Lar applikasjonen endre kalenderhendelser lagret på telefonen. Ondsinnede applikasjoner kan bruke dette til å slette eller endre kalenderinformasjon."</string> + <string name="permlab_accessMockLocation">"lage falske plasseringskilder for testing"</string> + <string name="permdesc_accessMockLocation">"Lage falske plassingskilder for testing. Ondsinnede applikasjoner kan bruke dette for å overstyre plasseringen og/eller statusen rapportert av ekte plasseringskilder slik som GPS eller nettverksoperatører."</string> + <string name="permlab_accessLocationExtraCommands">"få tilgang til ekstra plasseringskommandoer"</string> + <string name="permdesc_accessLocationExtraCommands">"Få tilgang til ekstra kommandoer for plasseringskilder. Ondsinnede applikasjoner kan bruke dette til å forstyrre GPS eller andre plasseringskilder."</string> + <string name="permlab_accessFineLocation">"nøyaktig (GPS-) plassering"</string> + <string name="permdesc_accessFineLocation">"Få tilgang til nøyaktige plasseringskilder som Global Positioning System (GPS) på telefonen, når det er tilgjengelig. Ondsinnede applikasjoner kan bruke dette for å finne ut hvor du er, og kan bruke mer batteri."</string> + <string name="permlab_accessCoarseLocation">"grov (nettverksbasert) plassering"</string> + <string name="permdesc_accessCoarseLocation">"Få tilgang til grove plasseringskilder som databasen over basestasjoner for å finne ut omtrent hvor telefonen er, når det er tilgjengelig. Ondsinnede applikasjoner kan bruke dette for å finne ut omtrent hvor du er."</string> + <string name="permlab_accessSurfaceFlinger">"få tilgang til SurfaceFlinger"</string> + <string name="permdesc_accessSurfaceFlinger">"Lar applikasjonen bruke lavnivåfunksjonalitet i SurfaceFlinger."</string> + <string name="permlab_readFrameBuffer">"lese skjermbufferet"</string> + <string name="permdesc_readFrameBuffer">"Lar applikasjonen lese innholdet i skjermbufferet."</string> + <string name="permlab_modifyAudioSettings">"endre lydinnstillinger"</string> + <string name="permdesc_modifyAudioSettings">"Lar applikasjonen endre globale lydinnstillinger som volum og ruting."</string> + <string name="permlab_recordAudio">"ta opp lyd"</string> + <string name="permdesc_recordAudio">"Gir applikasjonen tilgang til opptaksstien for lyd."</string> + <string name="permlab_camera">"ta bilder"</string> + <string name="permdesc_camera">"Lar applikasjonen ta bilder med kameraet. Dette gir applikasjonen til når som helst å se og lagre det kameraet ser."</string> + <string name="permlab_brick">"deaktivere telefonen permanent"</string> + <string name="permdesc_brick">"Lar applikasjonen deaktivere hele telefonen permanent. Dette er svært farlig."</string> + <string name="permlab_reboot">"tvinge omstart av telefon"</string> + <string name="permdesc_reboot">"Lar applikasjonen tvinge telefonen til å starte på nytt."</string> + <string name="permlab_mount_unmount_filesystems">"montere og avmontere filsystemer"</string> + <string name="permdesc_mount_unmount_filesystems">"Lar applikasjonen montere og avmontere filsystemer for uttagbar lagring."</string> + <!-- no translation found for permlab_mount_format_filesystems (5523285143576718981) --> + <skip /> + <!-- no translation found for permdesc_mount_format_filesystems (574060044906047386) --> + <skip /> + <string name="permlab_vibrate">"kontrollere vibratoren"</string> + <string name="permdesc_vibrate">"Lar applikasjonen kontrollere vibratoren."</string> + <string name="permlab_flashlight">"kontrollere lommelykten"</string> + <string name="permdesc_flashlight">"Lar applikasjonen kontrollere lommelykten."</string> + <string name="permlab_hardware_test">"teste maskinvare"</string> + <string name="permdesc_hardware_test">"Lar applikasjonen styre diverse enheter med det formål å teste maskinvaren."</string> + <string name="permlab_callPhone">"ringe telefonnummer direkte"</string> + <string name="permdesc_callPhone">"Lar applikasjonen ringe telefonnummer uten inngripen fra brukeren. Ondsinnede applikasjoner kan forårsake uventede oppringinger på telefonregningen. Merk at dette ikke gir applikasjonen lov til å ringe nødnummer."</string> + <string name="permlab_callPrivileged">"ringe vilkårlige telefonnummer direkte"</string> + <string name="permdesc_callPrivileged">"Lar applikasjonen ringe hvilket som helst telefonnummer, inkludert nødnummer, uten inngripen fra brukeren. Ondsinnede applikasjoner kan forårsake unødvendige og ulovlige samtaler til nødtjenester."</string> + <string name="permlab_locationUpdates">"kontrollere varsling for plasseringsendring"</string> + <string name="permdesc_locationUpdates">"Lar applikasjonen slå av/på varsling om plasseringsendringer fra radioen. Ikke ment for vanlige applikasjoner."</string> + <string name="permlab_checkinProperties">"få tilgang til egenskaper for innsjekking"</string> + <string name="permdesc_checkinProperties">"Gir lese- og skrivetilgang til egenskaper lastet opp av innsjekkingstjenesten. Ikke ment for vanlige applikasjoner."</string> + <!-- no translation found for permlab_bindGadget (2519859363977647275) --> + <skip /> + <!-- no translation found for permdesc_bindGadget (7865866514555126333) --> + <skip /> + <string name="permlab_modifyPhoneState">"endre telefontilstand"</string> + <string name="permdesc_modifyPhoneState">"Lar applikasjonen kontrollere telefonfunksjonaliteten i enheten. En applikasjon med denne rettigheten kan endre nettverk, slå telefonens radio av eller på og lignende uten noensinne å varsle brukeren."</string> + <string name="permlab_readPhoneState">"lese telefontilstand"</string> + <string name="permdesc_readPhoneState">"Lar applikasjonen få tilgang til telefonfunksjonaliteten i enheten. En applikasjon med denne rettigheten kan få vite enhetens telefonnummer, om en samtale pågår, nummeret samtalen er koblet til og lignende."</string> + <string name="permlab_wakeLock">"forhindre telefonen fra å sove"</string> + <string name="permdesc_wakeLock">"Lar applikasjonen forhindre telefonen fra å gå i hvilemodus."</string> + <string name="permlab_devicePower">"slå telefonen av eller på"</string> + <string name="permdesc_devicePower">"Lar applikasjonen skru telefonen av eller på."</string> + <string name="permlab_factoryTest">"kjøre i fabrikktestmodus"</string> + <string name="permdesc_factoryTest">"Kjøre som en lavnivås produsenttest, med full tilgang til telefonens maskinvare. Kun tilgjengelig når telefonen kjører i produsenttestmodus."</string> + <string name="permlab_setWallpaper">"endre bakgrunnsbilde"</string> + <string name="permdesc_setWallpaper">"Lar applikasjonen sette systemets bakgrunnsbilde."</string> + <string name="permlab_setWallpaperHints">"sette størrelseshint for bakgrunn"</string> + <string name="permdesc_setWallpaperHints">"Lar applikasjonen sette størrelseshint for systemets bakgrunnsbilde."</string> + <string name="permlab_masterClear">"nullstille systemet til fabrikkinnstillinger"</string> + <string name="permdesc_masterClear">"Lar applikasjonen nullstille systemet til fabrikkinnstillinger, noe som vil fjerne alle data, alt oppsett, og alle installerte applikasjoner."</string> + <string name="permlab_setTimeZone">"endre tidssone"</string> + <string name="permdesc_setTimeZone">"Lar applikasjonen endre telefonens tidssone."</string> + <string name="permlab_getAccounts">"oppdage kjente kontoer"</string> + <string name="permdesc_getAccounts">"Lar applikasjonen hente listen over kontoer telefonen kjenner til."</string> + <string name="permlab_accessNetworkState">"se nettverkstilstand"</string> + <string name="permdesc_accessNetworkState">"Lar applikasjonen se tilstanden til alle nettverk."</string> + <string name="permlab_createNetworkSockets">"full internett-tilgang"</string> + <string name="permdesc_createNetworkSockets">"Lar applikasjonen opprette vilkårlige nettverkstilkoblinger."</string> + <string name="permlab_writeApnSettings">"skrive APN-innstillinger"</string> + <string name="permdesc_writeApnSettings">"Lar applikasjonen to endre APN-innstillinger slik som mellomtjener eller port for hvilket som helst aksesspunkt."</string> + <string name="permlab_changeNetworkState">"endre nettverkskonnektivitet"</string> + <string name="permdesc_changeNetworkState">"Lar applikasjonen endre tilstanden til nettverkskonnektivitet."</string> + <!-- no translation found for permlab_changeBackgroundDataSetting (1400666012671648741) --> + <skip /> + <!-- no translation found for permdesc_changeBackgroundDataSetting (1001482853266638864) --> + <skip /> + <string name="permlab_accessWifiState">"se tilstand for trådløse nettverk"</string> + <string name="permdesc_accessWifiState">"Lar applikasjonen få se informasjon om tilstanden til de trådløse nettene."</string> + <string name="permlab_changeWifiState">"endre tilstand for trådløse nettverk"</string> + <string name="permdesc_changeWifiState">"Lar applikasjonen koble til og fra trådløse aksesspunkt, og å gjøre endringer i konfigurerte trådløse nettverk."</string> + <string name="permlab_bluetoothAdmin">"Bluetooth-administrasjon"</string> + <string name="permdesc_bluetoothAdmin">"Lar applikasjonen konfigurere den lokale Bluetooth-telefonen, og å oppdage og pare med andre enheter."</string> + <string name="permlab_bluetooth">"opprette Bluetooth-tilkoblinger"</string> + <string name="permdesc_bluetooth">"Lar applikasjonen se konfigurasjonen til den lokale Bluetooth-telefonen, og å opprette og godta tilkoblinger med parede enheter."</string> + <string name="permlab_disableKeyguard">"slå av tastaturlås"</string> + <string name="permdesc_disableKeyguard">"Lar applikasjonen slå av tastaturlåsen og enhver tilknyttet passordsikkerhet. Et legitimt eksempel på dette er at telefonen slår av tastaturlåsen når den mottar et innkommende anrop, og så slår den på igjen når samtalen er over."</string> + <string name="permlab_readSyncSettings">"lese synkroniseringsinnstillinger"</string> + <string name="permdesc_readSyncSettings">"Lar applikasjonen lese synkroniseringsinnstillingene, som for eksempel om kontakter blir synkronisert."</string> + <string name="permlab_writeSyncSettings">"skrive synkroniseringsinnstillinger"</string> + <string name="permdesc_writeSyncSettings">"Lar applikasjonen to endre på synkroniseringsinnstillingene, som for eksempel om kontakter blir synkronisert."</string> + <string name="permlab_readSyncStats">"lese synkroniseringsstatistikk"</string> + <string name="permdesc_readSyncStats">"Lar applikasjonen lese synkroniseringsstatistikk, som for eksempel historien over alle synkroniseringer utført."</string> + <string name="permlab_subscribedFeedsRead">"lese abonnement på nyhetskilder"</string> + <string name="permdesc_subscribedFeedsRead">"Lar applikasjonen hente detaljer om hvilke nyhetskilder som synkroniseres."</string> + <string name="permlab_subscribedFeedsWrite">"endre abonnement på nyhetskilder"</string> + <string name="permdesc_subscribedFeedsWrite">"Lar applikasjonen redigere hvilke nyhetskilder som synkroniseres. Dette kan gi en ondsinnet applikasjon tilgang til å endre hvilke nyhetskilder som synkroniseres."</string> + <!-- no translation found for permlab_readDictionary (432535716804748781) --> + <skip /> + <!-- no translation found for permdesc_readDictionary (1082972603576360690) --> + <skip /> + <!-- no translation found for permlab_writeDictionary (6703109511836343341) --> + <skip /> + <!-- no translation found for permdesc_writeDictionary (2241256206524082880) --> + <skip /> + <string-array name="phoneTypes"> + <item>"Hjemme"</item> + <item>"Mobil"</item> + <item>"Arbeid"</item> + <item>"Faks arbeid"</item> + <item>"Faks hjemme"</item> + <item>"Personsøker"</item> + <item>"Annen"</item> + <item>"Egendefinert…"</item> + </string-array> + <string-array name="emailAddressTypes"> + <item>"Hjemme"</item> + <item>"Arbeid"</item> + <item>"Annen"</item> + <item>"Egendefinert…"</item> + </string-array> + <string-array name="postalAddressTypes"> + <item>"Hjemme"</item> + <item>"Arbeid"</item> + <item>"Annen"</item> + <item>"Egendefinert…"</item> + </string-array> + <string-array name="imAddressTypes"> + <item>"Hjemme"</item> + <item>"Arbeid"</item> + <item>"Annen"</item> + <item>"Egendefinert…"</item> + </string-array> + <string-array name="organizationTypes"> + <item>"Arbeid"</item> + <item>"Annen"</item> + <item>"Egendefinert…"</item> + </string-array> + <string-array name="imProtocols"> + <item>"AIM"</item> + <item>"Windows Live"</item> + <item>"Yahoo"</item> + <item>"Skype"</item> + <item>"QQ"</item> + <item>"Google Talk"</item> + <item>"ICQ"</item> + <item>"Jabber"</item> + </string-array> + <string name="keyguard_password_enter_pin_code">"Skriv inn PIN-kode:"</string> + <string name="keyguard_password_wrong_pin_code">"Gal PIN-kode!"</string> + <string name="keyguard_label_text">"For å låse opp, trykk menuknappen fulgt av 0."</string> + <string name="emergency_call_dialog_number_for_display">"Nødnummer"</string> + <string name="lockscreen_carrier_default">"(Ingen operatør)"</string> + <string name="lockscreen_screen_locked">"Skjermen er låst"</string> + <string name="lockscreen_instructions_when_pattern_enabled">"Trykk på menyknappen for å låse opp eller ringe et nødnummer."</string> + <string name="lockscreen_instructions_when_pattern_disabled">"Trykk på menyknappen for å låse opp."</string> + <string name="lockscreen_pattern_instructions">"Tegn mønster for å låse opp"</string> + <string name="lockscreen_emergency_call">"Nødanrop"</string> + <string name="lockscreen_pattern_correct">"Riktig!"</string> + <string name="lockscreen_pattern_wrong">"Beklager, prøv igjen:"</string> + <string name="lockscreen_plugged_in">"Lader (<xliff:g id="NUMBER">%d%%</xliff:g>)"</string> + <string name="lockscreen_low_battery">"Koble til en batterilader."</string> + <string name="lockscreen_missing_sim_message_short">"Mangler SIM-kort."</string> + <string name="lockscreen_missing_sim_message">"Ikke noe SIM-kort i telefonen."</string> + <string name="lockscreen_missing_sim_instructions">"Sett inn et SIM-kort."</string> + <string name="lockscreen_network_locked_message">"Nettverk ikke tillatt"</string> + <string name="lockscreen_sim_puk_locked_message">"SIM-kortet er PUK-låst."</string> + <string name="lockscreen_sim_puk_locked_instructions">"Vennligst ring kundeservice."</string> + <string name="lockscreen_sim_locked_message">"SIM-kortet er låst."</string> + <string name="lockscreen_sim_unlock_progress_dialog_message">"Låser opp SIM-kort…"</string> + <string name="lockscreen_too_many_failed_attempts_dialog_message">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. "\n\n"Please try again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string> + <string name="lockscreen_failed_attempts_almost_glogin">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using your Google sign-in."\n\n" Please try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string> + <string name="lockscreen_too_many_failed_attempts_countdown">"Prøv igjen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string> + <string name="lockscreen_forgot_pattern_button_text">"Glemt mønsteret?"</string> + <string name="lockscreen_glogin_too_many_attempts">"Too many pattern attempts!"</string> + <string name="lockscreen_glogin_instructions">"To unlock,"\n"sign in with your Google account"</string> + <string name="lockscreen_glogin_username_hint">"Username (email)"</string> + <string name="lockscreen_glogin_password_hint">"Password"</string> + <string name="lockscreen_glogin_submit_button">"Sign in"</string> + <string name="lockscreen_glogin_invalid_input">"Invalid username or password."</string> + <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string> + <string name="hour_minute_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> + <string name="hour_minute_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> + <!-- no translation found for hour_ampm (7618670480400517084) --> + <skip /> + <!-- no translation found for hour_cap_ampm (5117798389811605468) --> + <skip /> + <string name="status_bar_clear_all_button">"Fjern varslinger"</string> + <string name="status_bar_no_notifications_title">"Ingen varslinger"</string> + <string name="status_bar_ongoing_events_title">"Aktiviteter"</string> + <string name="status_bar_latest_events_title">"Varslinger"</string> + <!-- no translation found for battery_status_text_percent_format (8818848472818880005) --> + <skip /> + <string name="battery_status_charging">"Lader…"</string> + <string name="battery_low_title">"Koble til en lader"</string> + <string name="battery_low_subtitle">"Batteriet er nesten tomt:"</string> + <string name="battery_low_percent_format">"mindre enn <xliff:g id="NUMBER">%d%%</xliff:g> igjen."</string> + <string name="factorytest_failed">"Factory test failed"</string> + <string name="factorytest_not_system">"The FACTORY_TEST action is only supported for packages installed in /system/app."</string> + <string name="factorytest_no_action">"No package was found that provides the FACTORY_TEST action."</string> + <string name="factorytest_reboot">"Reboot"</string> + <!-- no translation found for js_dialog_title (8143918455087008109) --> + <skip /> + <!-- no translation found for js_dialog_title_default (6961903213729667573) --> + <skip /> + <!-- no translation found for js_dialog_before_unload (1901675448179653089) --> + <skip /> + <string name="save_password_label">"Bekreft"</string> + <string name="save_password_message">"Ønsker du at nettleseren skal huske dette passordet?"</string> + <string name="save_password_notnow">"Ikke nå"</string> + <string name="save_password_remember">"Husk"</string> + <string name="save_password_never">"Aldri"</string> + <string name="open_permission_deny">"Du har ikke de nødvendige rettighetene til å åpne denne siden."</string> + <string name="text_copied">"Kopierte tekst til utklippstavlen."</string> + <string name="more_item_label">"Mer"</string> + <string name="prepend_shortcut_label">"menyknapp+"</string> + <string name="menu_space_shortcut_label">"mellomrom"</string> + <string name="menu_enter_shortcut_label">"enter"</string> + <string name="menu_delete_shortcut_label">"slett"</string> + <string name="search_go">"Søk"</string> + <string name="today">"I dag"</string> + <string name="yesterday">"I går"</string> + <string name="tomorrow">"I morgen"</string> + <string name="oneMonthDurationPast">"For en måned siden"</string> + <string name="beforeOneMonthDurationPast">"For over en måned siden"</string> + <plurals name="num_seconds_ago"> + <item quantity="one">"for et sekund siden"</item> + <item quantity="other">"for <xliff:g id="COUNT">%d</xliff:g> sekunder siden"</item> + </plurals> + <plurals name="num_minutes_ago"> + <item quantity="one">"for et minutt siden"</item> + <item quantity="other">"for <xliff:g id="COUNT">%d</xliff:g> minutter siden"</item> + </plurals> + <plurals name="num_hours_ago"> + <item quantity="one">"for en time siden"</item> + <item quantity="other">"for <xliff:g id="COUNT">%d</xliff:g> timer siden"</item> + </plurals> + <plurals name="num_days_ago"> + <item quantity="one">"i går"</item> + <item quantity="other">"for <xliff:g id="COUNT">%d</xliff:g> dager siden"</item> + </plurals> + <plurals name="in_num_seconds"> + <item quantity="one">"om et sekund"</item> + <item quantity="other">"om <xliff:g id="COUNT">%d</xliff:g> sekunder"</item> + </plurals> + <plurals name="in_num_minutes"> + <item quantity="one">"om et minutt"</item> + <item quantity="other">"om <xliff:g id="COUNT">%d</xliff:g> minutter"</item> + </plurals> + <plurals name="in_num_hours"> + <item quantity="one">"om et minutt"</item> + <item quantity="other">"om <xliff:g id="COUNT">%d</xliff:g> timer"</item> + </plurals> + <plurals name="in_num_days"> + <item quantity="one">"i morgen"</item> + <item quantity="other">"om <xliff:g id="COUNT">%d</xliff:g> dager"</item> + </plurals> + <plurals name="abbrev_num_seconds_ago"> + <item quantity="one">"1 sek siden"</item> + <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> sek siden"</item> + </plurals> + <plurals name="abbrev_num_minutes_ago"> + <item quantity="one">"1 min siden"</item> + <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> min siden"</item> + </plurals> + <plurals name="abbrev_num_hours_ago"> + <item quantity="one">"1 t siden"</item> + <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> t siden"</item> + </plurals> + <plurals name="abbrev_num_days_ago"> + <item quantity="one">"i går"</item> + <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> d siden"</item> + </plurals> + <plurals name="abbrev_in_num_seconds"> + <item quantity="one">"om 1 sek"</item> + <item quantity="other">"om <xliff:g id="COUNT">%d</xliff:g> sek"</item> + </plurals> + <plurals name="abbrev_in_num_minutes"> + <item quantity="one">"om 1 min"</item> + <item quantity="other">"om <xliff:g id="COUNT">%d</xliff:g> min"</item> + </plurals> + <plurals name="abbrev_in_num_hours"> + <item quantity="one">"om 1 t"</item> + <item quantity="other">"om <xliff:g id="COUNT">%d</xliff:g> t"</item> + </plurals> + <plurals name="abbrev_in_num_days"> + <item quantity="one">"i morgen"</item> + <item quantity="other">"om <xliff:g id="COUNT">%d</xliff:g> d"</item> + </plurals> + <string name="preposition_for_date">"%s"</string> + <string name="preposition_for_time">"%s"</string> + <string name="preposition_for_year">"%s"</string> + <string name="day">"dag"</string> + <string name="days">"dager"</string> + <string name="hour">"time"</string> + <string name="hours">"timer"</string> + <string name="minute">"min"</string> + <string name="minutes">"min"</string> + <string name="second">"s"</string> + <string name="seconds">"s"</string> + <string name="week">"uke"</string> + <string name="weeks">"uker"</string> + <string name="year">"år"</string> + <string name="years">"år"</string> + <string name="sunday">"søndag"</string> + <string name="monday">"mandag"</string> + <string name="tuesday">"tirsdag"</string> + <string name="wednesday">"onsdag"</string> + <string name="thursday">"torsdag"</string> + <string name="friday">"fredag"</string> + <string name="saturday">"lørdag"</string> + <string name="every_weekday">"Hverdager (man–fre)"</string> + <string name="daily">"Hver dag"</string> + <string name="weekly">"Hver <xliff:g id="DAY">%s</xliff:g>"</string> + <string name="monthly">"En gang i måneden"</string> + <string name="yearly">"En gang i året"</string> + <string name="VideoView_error_title">"Cannot play video"</string> + <string name="VideoView_error_text_unknown">"Sorry, this video cannot be played."</string> + <string name="VideoView_error_button">"OK"</string> + <string name="am">"AM"</string> + <string name="pm">"PM"</string> + <string name="numeric_date">"<xliff:g id="YEAR">%Y</xliff:g>-<xliff:g id="MONTH">%m</xliff:g>-<xliff:g id="DAY">%d</xliff:g>"</string> + <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DATE1">%2$s</xliff:g> <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g> <xliff:g id="DATE2">%5$s</xliff:g> <xliff:g id="TIME2">%6$s</xliff:g>"</string> + <string name="wday1_date1_wday2_date2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g> <xliff:g id="DATE2">%5$s</xliff:g>"</string> + <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g> <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g> <xliff:g id="TIME2">%6$s</xliff:g>"</string> + <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>"</string> + <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> – <xliff:g id="TIME2">%2$s</xliff:g>"</string> + <string name="time_wday_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g> <xliff:g id="DATE">%3$s</xliff:g>"</string> + <string name="wday_date">"<xliff:g id="WEEKDAY">%2$s</xliff:g> <xliff:g id="DATE">%3$s</xliff:g>"</string> + <string name="time_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> + <!-- no translation found for date_time (6104442718633642836) --> + <skip /> + <!-- no translation found for relative_time (1818557177829411417) --> + <skip /> + <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> + <string name="full_date_month_first">"<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>'., '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="full_date_day_first">"<xliff:g id="DAY">d</xliff:g>'. '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_month_first">"<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_day_first">"<xliff:g id="DAY">d</xliff:g>'. '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> + <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> + <string name="noon">"middag"</string> + <string name="Noon">"Middag"</string> + <string name="midnight">"midnatt"</string> + <string name="Midnight">"Midnatt"</string> + <!-- no translation found for month_day (5565829181417740906) --> + <skip /> + <!-- no translation found for month (7026169712234774086) --> + <skip /> + <string name="month_day_year">"<xliff:g id="DAY">%-d</xliff:g>. <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> + <!-- no translation found for month_year (9219019380312413367) --> + <skip /> + <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> + <string name="date_and_time">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g> <xliff:g id="DAY">%-d</xliff:g>. <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> + <string name="same_year_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>"</string> + <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>"</string> + <string name="same_year_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR">%9$s</xliff:g>"</string> + <!-- no translation found for same_year_wday1_mdy1_wday2_mdy2 (1345612987720788874) --> + <skip /> + <string name="same_year_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <!-- no translation found for same_year_mdy1_time1_mdy2_time2 (2959466512848524124) --> + <skip /> + <!-- no translation found for same_year_wday1_mdy1_time1_wday2_mdy2_time2 (5129735426508861428) --> + <skip /> + <string name="numeric_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>.<xliff:g id="MONTH1">%2$s</xliff:g>. – <xliff:g id="DAY2">%8$s</xliff:g>.<xliff:g id="MONTH2">%7$s</xliff:g>."</string> + <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>.<xliff:g id="MONTH1">%2$s</xliff:g>. – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>.<xliff:g id="MONTH2">%7$s</xliff:g>."</string> + <!-- no translation found for numeric_mdy1_mdy2 (3933951218078638018) --> + <skip /> + <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>.<xliff:g id="MONTH1">%2$s</xliff:g>.<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>.<xliff:g id="MONTH2">%7$s</xliff:g>.<xliff:g id="YEAR2">%9$s</xliff:g>"</string> + <string name="numeric_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>.<xliff:g id="MONTH1">%2$s</xliff:g>. <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>.<xliff:g id="MONTH2">%7$s</xliff:g>. <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>.<xliff:g id="MONTH1">%2$s</xliff:g>. <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>.<xliff:g id="MONTH2">%7$s</xliff:g>. <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>.<xliff:g id="MONTH1">%2$s</xliff:g>.<xliff:g id="YEAR1">%4$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>.<xliff:g id="MONTH2">%7$s</xliff:g>.<xliff:g id="YEAR2">%9$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>.<xliff:g id="MONTH1">%2$s</xliff:g>.<xliff:g id="YEAR1">%4$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>.<xliff:g id="MONTH2">%7$s</xliff:g>.<xliff:g id="YEAR2">%9$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="same_month_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>.–<xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g>"</string> + <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>"</string> + <string name="same_month_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>.–<xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string> + <string name="same_month_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> + <string name="abbrev_month_day_year">"<xliff:g id="DAY">%-d</xliff:g>. <xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> + <!-- no translation found for abbrev_month_year (3856424847226891943) --> + <skip /> + <!-- no translation found for abbrev_month_day (5028815883653985933) --> + <skip /> + <!-- no translation found for abbrev_month (3131032032850777433) --> + <skip /> + <string name="day_of_week_long_sunday">"søndag"</string> + <string name="day_of_week_long_monday">"mandag"</string> + <string name="day_of_week_long_tuesday">"tirsdag"</string> + <string name="day_of_week_long_wednesday">"onsdag"</string> + <string name="day_of_week_long_thursday">"torsdag"</string> + <string name="day_of_week_long_friday">"fredag"</string> + <string name="day_of_week_long_saturday">"lørdag"</string> + <string name="day_of_week_medium_sunday">"søn"</string> + <string name="day_of_week_medium_monday">"man"</string> + <string name="day_of_week_medium_tuesday">"tir"</string> + <string name="day_of_week_medium_wednesday">"ons"</string> + <string name="day_of_week_medium_thursday">"tor"</string> + <string name="day_of_week_medium_friday">"fre"</string> + <string name="day_of_week_medium_saturday">"lør"</string> + <string name="day_of_week_short_sunday">"sø"</string> + <string name="day_of_week_short_monday">"ma"</string> + <string name="day_of_week_short_tuesday">"ti"</string> + <string name="day_of_week_short_wednesday">"on"</string> + <string name="day_of_week_short_thursday">"to"</string> + <string name="day_of_week_short_friday">"fr"</string> + <string name="day_of_week_short_saturday">"lø"</string> + <string name="day_of_week_shorter_sunday">"S"</string> + <string name="day_of_week_shorter_monday">"M"</string> + <string name="day_of_week_shorter_tuesday">"Ti"</string> + <string name="day_of_week_shorter_wednesday">"O"</string> + <string name="day_of_week_shorter_thursday">"To"</string> + <string name="day_of_week_shorter_friday">"F"</string> + <string name="day_of_week_shorter_saturday">"L"</string> + <string name="day_of_week_shortest_sunday">"S"</string> + <string name="day_of_week_shortest_monday">"M"</string> + <string name="day_of_week_shortest_tuesday">"T"</string> + <string name="day_of_week_shortest_wednesday">"O"</string> + <string name="day_of_week_shortest_thursday">"T"</string> + <string name="day_of_week_shortest_friday">"F"</string> + <string name="day_of_week_shortest_saturday">"L"</string> + <string name="month_long_january">"januar"</string> + <string name="month_long_february">"februar"</string> + <string name="month_long_march">"mars"</string> + <string name="month_long_april">"april"</string> + <string name="month_long_may">"mai"</string> + <string name="month_long_june">"juni"</string> + <string name="month_long_july">"juli"</string> + <string name="month_long_august">"august"</string> + <string name="month_long_september">"september"</string> + <string name="month_long_october">"oktober"</string> + <string name="month_long_november">"november"</string> + <string name="month_long_december">"desember"</string> + <string name="month_medium_january">"jan"</string> + <string name="month_medium_february">"feb"</string> + <string name="month_medium_march">"mar"</string> + <string name="month_medium_april">"apr"</string> + <string name="month_medium_may">"mai"</string> + <string name="month_medium_june">"jun"</string> + <string name="month_medium_july">"jul"</string> + <string name="month_medium_august">"aug"</string> + <string name="month_medium_september">"sep"</string> + <string name="month_medium_october">"okt"</string> + <string name="month_medium_november">"nov"</string> + <string name="month_medium_december">"des"</string> + <string name="month_shortest_january">"J"</string> + <string name="month_shortest_february">"F"</string> + <string name="month_shortest_march">"M"</string> + <string name="month_shortest_april">"A"</string> + <string name="month_shortest_may">"M"</string> + <string name="month_shortest_june">"J"</string> + <string name="month_shortest_july">"J"</string> + <string name="month_shortest_august">"A"</string> + <string name="month_shortest_september">"S"</string> + <string name="month_shortest_october">"O"</string> + <string name="month_shortest_november">"N"</string> + <string name="month_shortest_december">"D"</string> + <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> + <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> + <string name="selectAll">"Merk alt"</string> + <string name="selectText">"Merk tekst"</string> + <string name="stopSelectingText">"Slutt å merke tekst"</string> + <string name="cut">"Klipp ut"</string> + <string name="cutAll">"Klipp ut alt"</string> + <string name="copy">"Kopier"</string> + <string name="copyAll">"Koiper alt"</string> + <string name="paste">"Lim inn"</string> + <string name="copyUrl">"Kopier URL"</string> + <string name="inputMethod">"Inndatametode"</string> + <!-- no translation found for addToDictionary (726256909274177272) --> + <skip /> + <string name="editTextMenuTitle">"Rediger tekst"</string> + <string name="low_internal_storage_view_title">"Lite plass"</string> + <string name="low_internal_storage_view_text">"Det begynner å bli lite lagringsplass på telefonen."</string> + <string name="ok">"OK"</string> + <string name="cancel">"Avbryt"</string> + <string name="yes">"OK"</string> + <string name="no">"Avbryt"</string> + <!-- no translation found for dialog_alert_title (2049658708609043103) --> + <skip /> + <string name="capital_on">"På"</string> + <string name="capital_off">"Av"</string> + <string name="whichApplication">"Complete action using"</string> + <string name="alwaysUse">"Use by default for this action."</string> + <string name="clearDefaultHintMsg">"Clear default in Home Settings > Applications > Manage applications."</string> + <string name="chooseActivity">"Select an action"</string> + <string name="noApplications">"No applications can perform this action."</string> + <string name="aerr_title">"Sorry!"</string> + <string name="aerr_application">"The application <xliff:g id="APPLICATION">%1$s</xliff:g> (process <xliff:g id="PROCESS">%2$s</xliff:g>) has stopped unexpectedly. Please try again."</string> + <string name="aerr_process">"The process <xliff:g id="PROCESS">%1$s</xliff:g> has stopped unexpectedly. Please try again."</string> + <string name="anr_title">"Sorry!"</string> + <string name="anr_activity_application">"Activity <xliff:g id="ACTIVITY">%1$s</xliff:g> (in application <xliff:g id="APPLICATION">%2$s</xliff:g>) is not responding."</string> + <string name="anr_activity_process">"Activity <xliff:g id="ACTIVITY">%1$s</xliff:g> (in process <xliff:g id="PROCESS">%2$s</xliff:g>) is not responding."</string> + <string name="anr_application_process">"Application <xliff:g id="APPLICATION">%1$s</xliff:g> (in process <xliff:g id="PROCESS">%2$s</xliff:g>) is not responding."</string> + <string name="anr_process">"Process <xliff:g id="PROCESS">%1$s</xliff:g> is not responding."</string> + <string name="force_close">"Force close"</string> + <string name="wait">"Wait"</string> + <string name="debug">"Debug"</string> + <string name="sendText">"Select an action for text"</string> + <string name="volume_ringtone">"Ringetonevolum"</string> + <string name="volume_music">"Medievolum"</string> + <string name="volume_music_hint_playing_through_bluetooth">"Spiller over Bluetooth"</string> + <string name="volume_call">"Samtalevolum"</string> + <!-- no translation found for volume_bluetooth_call (2002891926351151534) --> + <skip /> + <string name="volume_alarm">"Alarmvolum"</string> + <string name="volume_notification">"Varslingsvolum"</string> + <string name="volume_unknown">"Volum"</string> + <string name="ringtone_default">"Standard ringetone"</string> + <string name="ringtone_default_with_actual">"Standard ringetone (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string> + <string name="ringtone_silent">"Stille"</string> + <string name="ringtone_picker_title">"Ringetoner"</string> + <string name="ringtone_unknown">"Ukjent ringetone"</string> + <plurals name="wifi_available"> + <item quantity="one">"Trådløsnett i nærheten"</item> + <item quantity="other">"Trådløsnett i nærheten"</item> + </plurals> + <plurals name="wifi_available_detailed"> + <item quantity="one">"Åpent trådløsnett i nærheten"</item> + <item quantity="other">"Åpne trådløsnett i nærheten"</item> + </plurals> + <string name="select_character">"Sett inn tegn"</string> + <string name="sms_control_default_app_name">"Ukjent applikasjon"</string> + <string name="sms_control_title">"Sending SMS messages"</string> + <string name="sms_control_message">"A large number of SMS messages are being sent. Select \\\"OK\\\" to continue, or \\\"Cancel\\\" to stop sending."</string> + <string name="sms_control_yes">"OK"</string> + <string name="sms_control_no">"Avbryt"</string> + <string name="date_time_set">"Lagre"</string> + <string name="default_permission_group">"Standard"</string> + <string name="no_permissions">"Trenger ingen rettigheter"</string> + <string name="perms_hide"><b>"Skjul"</b></string> + <string name="perms_show_all"><b>"Vis alle"</b></string> + <string name="googlewebcontenthelper_loading">"Laster inn…"</string> + <string name="usb_storage_title">"USB koblet til"</string> + <string name="usb_storage_message">"Du har koblet telefonen til en datamaskin via USB. Velg \\\"Monter\\\" dersom du ønsker å kopiere filer mellom datmaskinen og minnekortet i telefonen."</string> + <string name="usb_storage_button_mount">"Monter"</string> + <string name="usb_storage_button_unmount">"Ikke monter"</string> + <string name="usb_storage_error_message">"Det oppsto et problem med å bruke minnekortet ditt for USB-lagring."</string> + <string name="usb_storage_notification_title">"USB tilkoblet"</string> + <string name="usb_storage_notification_message">"Velg om du ønsker å kopiere filer til/fra en datamaskin."</string> + <!-- no translation found for usb_storage_stop_notification_title (2336058396663516017) --> + <skip /> + <!-- no translation found for usb_storage_stop_notification_message (2591813490269841539) --> + <skip /> + <!-- no translation found for usb_storage_stop_title (6014127947456185321) --> + <skip /> + <!-- no translation found for usb_storage_stop_message (2390958966725232848) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_mount (1181858854166273345) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_unmount (3774611918660582898) --> + <skip /> + <!-- no translation found for usb_storage_stop_error_message (3917317248440072084) --> + <skip /> + <!-- no translation found for extmedia_format_title (8663247929551095854) --> + <skip /> + <!-- no translation found for extmedia_format_message (3621369962433523619) --> + <skip /> + <!-- no translation found for extmedia_format_button_format (4131064560127478695) --> + <skip /> + <string name="select_input_method">"Velg inndatametode"</string> + <string name="fast_scroll_alphabet">"ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ"</string> + <string name="fast_scroll_numeric_alphabet">"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ"</string> + <!-- unknown quoting pattern: original -1, translation 1 --> + <string name="candidates_style">"TAG_FONT"<u>"kandidater"</u>"u&gt;CLOSE_FONT"</string> + <!-- no translation found for ext_media_checking_notification_title (5457603418970994050) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_message (4747432538578886744) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_title (780477838241212997) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_message (1312266820092958014) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_title (6410723906019100189) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_message (2679412884290061775) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_title (6872152882604407837) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_message (7260183293747448241) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_title (6729801130790616200) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_message (7613960686747592770) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_title (8902518030404381318) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_message (4205117227342822275) --> + <skip /> + <!-- no translation found for activity_list_empty (4168820609403385789) --> + <skip /> + <!-- no translation found for permlab_pkgUsageStats (8787352074326748892) --> + <skip /> + <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> + <skip /> + <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> + <skip /> +</resources> diff --git a/core/res/res/values-nl-rNL/strings.xml b/core/res/res/values-nl-rNL/strings.xml deleted file mode 100644 index 1dc48bb..0000000 --- a/core/res/res/values-nl-rNL/strings.xml +++ /dev/null @@ -1,905 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="byteShort">"b"</string> - <string name="kilobyteShort">"Kb"</string> - <string name="megabyteShort">"Mb"</string> - <string name="gigabyteShort">"Gb"</string> - <string name="terabyteShort">"Tb"</string> - <string name="petabyteShort">"Pb"</string> - <string name="untitled">"<Naamloos>"</string> - <string name="ellipsis">"…"</string> - <string name="emptyPhoneNumber">"(geen telefoonnummer)"</string> - <string name="unknownName">"(onbekend)"</string> - <string name="defaultVoiceMailAlphaTag">"Voicemail"</string> - <string name="defaultMsisdnAlphaTag">"Msisdn1"</string> - <string name="mmiError">"Netwerkfout of onjuiste MMI-code."</string> - <string name="serviceEnabled">"Service ingeschakeld"</string> - <string name="serviceEnabledFor">"Service ingeschakeld voor:"</string> - <string name="serviceDisabled">"Service uitgeschakeld"</string> - <string name="serviceRegistered">"Registratie gelukt"</string> - <string name="serviceErased">"Wissen gelukt"</string> - <string name="passwordIncorrect">"Ongeldig wachtwoord"</string> - <string name="mmiComplete">"MMI voltooid"</string> - <!-- no translation found for badPin (5103184589972647739) --> - <skip /> - <!-- no translation found for badPuk (2200634943393540609) --> - <skip /> - <!-- no translation found for mismatchPin (5055729703806180857) --> - <skip /> - <!-- no translation found for invalidPin (6201854814319326475) --> - <skip /> - <!-- no translation found for needPuk (4788728144863892764) --> - <skip /> - <!-- no translation found for needPuk2 (7056908944942451033) --> - <skip /> - <string name="ClipMmi">"Nummerweergave inkomend gesprek"</string> - <string name="ClirMmi">"Nummerweergave uitgaand gesprek"</string> - <string name="CfMmi">"Oproep doorschakelen"</string> - <string name="CwMmi">"Gesprek in wachtstand"</string> - <string name="BaMmi">"Oproep blokkeren"</string> - <string name="PwdMmi">"Wachtwoord wijzigen"</string> - <string name="PinMmi">"Pincode wijzigen"</string> - <string name="CLIRDefaultOnNextCallOn">"ID-beperkingsstandaarden op beperkt. Volgend gesprek: beperkt"</string> - <string name="CLIRDefaultOnNextCallOff">"ID-beperkingsstandaarden op beperkt. Volgend gesprek: niet beperkt"</string> - <string name="CLIRDefaultOffNextCallOn">"ID-beperkingsstandaarden op onbeperkt. Volgend gesprek: beperkt"</string> - <string name="CLIRDefaultOffNextCallOff">"ID-beperkingsstandaarden op onbeperkt. Volgend gesprek: niet beperkt"</string> - <string name="serviceNotProvisioned">"Service niet opgenomen."</string> - <string name="CLIRPermanent">"ID-beperking ingesteld op permanente modus."</string> - <string name="serviceClassVoice">"Spraak"</string> - <string name="serviceClassData">"Gegevens"</string> - <string name="serviceClassFAX">"FAX"</string> - <string name="serviceClassSMS">"SMS"</string> - <string name="serviceClassDataAsync">"Async"</string> - <string name="serviceClassDataSync">"Sync"</string> - <string name="serviceClassPacket">"Pakket"</string> - <string name="serviceClassPAD">"PAD"</string> - <string name="cfTemplateNotForwarded">"{0}: Niet doorgestuurd"</string> - <string name="cfTemplateForwarded">"{0}: {1}"</string> - <string name="cfTemplateForwardedTime">"{0}: {1} na {2} seconden"</string> - <string name="cfTemplateRegistered">"{0}: Niet doorgestuurd ({1})"</string> - <string name="cfTemplateRegisteredTime">"{0}: Niet doorgestuurd ({1} na {2} seconden)"</string> - <string name="httpErrorOk">"OK"</string> - <string name="httpError">"Onbekende fout"</string> - <string name="httpErrorLookup">"Onbekende host"</string> - <string name="httpErrorUnsupportedAuthScheme">"Niet-ondersteund verificatieschema. Verificeren mislukt."</string> - <string name="httpErrorAuth">"Verificatie mislukt"</string> - <string name="httpErrorProxyAuth">"Verificeren van proxyserver mislukt"</string> - <string name="httpErrorConnect">"Verbinding maken met server mislukt"</string> - <string name="httpErrorIO">"Lezen van of schrijven naar server mislukt"</string> - <string name="httpErrorTimeout">"Time-out bij serververbinding"</string> - <string name="httpErrorRedirectLoop">"Te veel omleidingen door server"</string> - <string name="httpErrorUnsupportedScheme">"Protocol wordt niet ondersteund"</string> - <string name="httpErrorFailedSslHandshake">"Uitvoeren van ssl-handshake mislukt"</string> - <string name="httpErrorBadUrl">"Ongeldige url"</string> - <string name="httpErrorFile">"Bestandfout"</string> - <string name="httpErrorFileNotFound">"Bestand niet gevonden"</string> - <!-- no translation found for httpErrorTooManyRequests (3764334538393544875) --> - <skip /> - <string name="contentServiceSync">"Sync"</string> - <string name="contentServiceSyncNotificationTitle">"Sync"</string> - <!-- no translation found for contentServiceTooManyDeletesNotificationDesc (8477597194404210723) --> - <skip /> - <!-- no translation found for low_memory (4191592786596642367) --> - <skip /> - <!-- no translation found for me (4616693653158602117) --> - <skip /> - <string name="power_dialog">"Energieopties"</string> - <string name="silent_mode">"Stille modus"</string> - <string name="turn_on_radio">"Radio uitschakelen"</string> - <string name="turn_off_radio">"Radio uitschakelen"</string> - <string name="screen_lock">"Vergrendeling"</string> - <string name="power_off">"Uitschakelen"</string> - <!-- no translation found for shutdown_progress (3735034517335251808) --> - <skip /> - <!-- no translation found for shutdown_confirm (699224922526414097) --> - <skip /> - <string name="no_recent_tasks">"Geen recente toepassingen"</string> - <string name="global_actions">"Globale handelingen"</string> - <string name="global_action_lock">"Vergrendeling"</string> - <string name="global_action_power_off">"Uitschakelen"</string> - <string name="global_action_toggle_silent_mode">"Stille modus"</string> - <string name="global_action_silent_mode_on_status">"Geluid staat UIT"</string> - <string name="global_action_silent_mode_off_status">"Geluid staat AAN"</string> - <string name="safeMode">"Veilige modus"</string> - <!-- no translation found for permgrouplab_costMoney (904087853776533085) --> - <skip /> - <!-- no translation found for permgroupdesc_costMoney (4662370555643969515) --> - <skip /> - <!-- no translation found for permgrouplab_messages (2984053976424233925) --> - <skip /> - <!-- no translation found for permgroupdesc_messages (2129093134354989379) --> - <skip /> - <!-- no translation found for permgrouplab_personalInfo (4548406335021507392) --> - <skip /> - <!-- no translation found for permgroupdesc_personalInfo (8499310823817958034) --> - <skip /> - <!-- no translation found for permgrouplab_location (8535677827151907069) --> - <skip /> - <!-- no translation found for permgroupdesc_location (2341662219604651887) --> - <skip /> - <!-- no translation found for permgrouplab_network (3597781730625751831) --> - <skip /> - <!-- no translation found for permgroupdesc_network (8332572695347918340) --> - <skip /> - <!-- no translation found for permgrouplab_accounts (8631201594657951893) --> - <skip /> - <!-- no translation found for permgroupdesc_accounts (443982868906396781) --> - <skip /> - <!-- no translation found for permgrouplab_hardwareControls (5074512938567152139) --> - <skip /> - <!-- no translation found for permgroupdesc_hardwareControls (8772503144945278440) --> - <skip /> - <!-- no translation found for permgrouplab_phoneCalls (7096448531266882376) --> - <skip /> - <!-- no translation found for permgroupdesc_phoneCalls (6703873478653366233) --> - <skip /> - <!-- no translation found for permgrouplab_systemTools (1840847965111633430) --> - <skip /> - <!-- no translation found for permgroupdesc_systemTools (2810337951496685271) --> - <skip /> - <!-- no translation found for permgrouplab_developmentTools (692844635256963358) --> - <skip /> - <!-- no translation found for permgroupdesc_developmentTools (5253915519857796400) --> - <skip /> - <string name="permlab_statusBar">"Statusbalk besturen"</string> - <string name="permdesc_statusBar">"Hiermee kan een toepassing de statusbalk en pictogrammen openen, sluiten of uitschakelen."</string> - <!-- no translation found for permlab_expandStatusBar (6382500803293284173) --> - <skip /> - <!-- no translation found for permdesc_expandStatusBar (90953162060681436) --> - <skip /> - <!-- no translation found for permlab_processOutgoingCalls (786316295241100144) --> - <skip /> - <!-- no translation found for permdesc_processOutgoingCalls (1655242138991854396) --> - <skip /> - <string name="permlab_receiveSms">"Sms-berichten ontvangen"</string> - <string name="permdesc_receiveSms">"Hiermee kan een toepassing tekstberichten ontvangen en verwerken. Slechte toepassingen kunnen uw berichten in de gaten houden of verwijderen zonder dat dit wordt aangegeven."</string> - <string name="permlab_receiveMms">"Mms-berichten ontvangen"</string> - <string name="permdesc_receiveMms">"Hiermee kan een toepassing multimediaberichten ontvangen en verwerken. Slechte toepassingen kunnen uw berichten in de gaten houden of verwijderen zonder dat dit wordt aangegeven."</string> - <string name="permlab_sendSms">"Sms-berichten verzenden"</string> - <string name="permdesc_sendSms">"Hiermee kan een toepassing tekstberichten verzenden. Slechte toepassingen kunnen u op kosten jagen door zonder toestemming berichten te verzenden."</string> - <string name="permlab_readSms">"Sms/mms-berichten lezen"</string> - <string name="permdesc_readSms">"Hiermee kan een toepassing sms-berichten op telefoon of SIM-kaart lezen. Slechte toepassingen kunnen vertrouwelijke berichten lezen."</string> - <string name="permlab_writeSms">"Sms/mms-berichten schrijven"</string> - <string name="permdesc_writeSms">"Hiermee kan een toepassing sms-berichten op telefoon of SIM-kaart schrijven. Slechte toepassingen kunnen berichten verwijderen."</string> - <string name="permlab_receiveWapPush">"Wap-berichten ontvangen"</string> - <string name="permdesc_receiveWapPush">"Hiermee kan een toepassing wap-berichten ontvangen en verwerken. Slechte toepassingen kunnen uw berichten in de gaten houden of verwijderen zonder dat dit wordt aangegeven."</string> - <string name="permlab_getTasks">"Taakinformatie verkrijgen"</string> - <string name="permdesc_getTasks">"Hiermee kan een toepassing informatie ophalen over geactiveerde en recent geactiveerde taken. Verkeerde toepassingen kunnen hierdoor toegang krijgen tot privé-informatie over andere toepassingen."</string> - <string name="permlab_reorderTasks">"Taken herschikken"</string> - <string name="permdesc_reorderTasks">"Hiermee kan een toepassing taken naar de voorgrond en achtergrond verplaatsen. Slechte toepassingen kunnen zichzelf zonder tussenkomst naar de voorgrond forceren."</string> - <string name="permlab_setDebugApp">"Foutopsporing instellen"</string> - <string name="permdesc_setDebugApp">"Hiermee kan een toepassing foutopsporing voor andere toepassingen inschakelen. Slechte toepassingen kunnen dit misbruiken om andere toepassingen te stoppen."</string> - <string name="permlab_changeConfiguration">"Configuratie wijzigen"</string> - <string name="permdesc_changeConfiguration">"Hiermee kan een toepassing de actuele configuratie veranderen, zoals de locatie of algemene tekengrootte."</string> - <!-- no translation found for permlab_restartPackages (5836367540766044606) --> - <skip /> - <!-- no translation found for permdesc_restartPackages (1764965996765573321) --> - <skip /> - <!-- no translation found for permlab_setProcessForeground (4860990420780868638) --> - <skip /> - <!-- no translation found for permdesc_setProcessForeground (3795477299954784360) --> - <skip /> - <string name="permlab_forceBack">"Terug forceren"</string> - <string name="permdesc_forceBack">"Hiermee kan een toepassing elke activiteit die op de voorgrond staat sluiten en terugkeren. Is bij normale toepassingen nooit nodig."</string> - <string name="permlab_dump">"Systeemstatus dumpen"</string> - <string name="permdesc_dump">"Hiermee kan een toepassingen de interne toestand van het systeem opvragen. Verkeerde toepassingen kunnen een diverse privé- en veiligheidsinformatie verkrijgen die ze normaal gesproken niet nodig hebben."</string> - <string name="permlab_addSystemService">"Systeemservice toevoegen"</string> - <string name="permdesc_addSystemService">"Toepassingen mogen hun eigen systeemdiensten van laag-niveau publiceren. Verkeerde toepassingen kunnen het systeem kapen en gegevens erop beschadigen of stelen."</string> - <string name="permlab_runSetActivityWatcher">"Activiteitenmonitor instellen"</string> - <string name="permdesc_runSetActivityWatcher">"Hiermee kan een toepassing de wijze waarop het systeem activiteiten start in de gaten houden en besturen. Slechte toepassingen kunnen zo het systeem in gevaar brengen. Deze toepassing is alleen nodig voor ontwikkelingsdoeleinden, nooit voor normaal gebruik van het toestel."</string> - <string name="permlab_broadcastPackageRemoved">"Uitzendpakket verwijderd"</string> - <string name="permdesc_broadcastPackageRemoved">"Hiermee kan een toepassing een melding uitzenden met de mededeling dat een toepassingspakket is verwijderd. Verkeerde toepassingen kunnen dit gebruiken om andere actieve toepassingen te stoppen."</string> - <!-- no translation found for permlab_broadcastSmsReceived (1994692154847312518) --> - <skip /> - <!-- no translation found for permdesc_broadcastSmsReceived (6072362543164841432) --> - <skip /> - <!-- no translation found for permlab_broadcastWapPush (3070023012636951639) --> - <skip /> - <!-- no translation found for permdesc_broadcastWapPush (726912255218924336) --> - <skip /> - <string name="permlab_setProcessLimit">"Proceslimiet instellen"</string> - <string name="permdesc_setProcessLimit">"Hiermee kan een toepassing het maximumaantal geactiveerde processen besturen. Nooit nodig voor normale toepassingen."</string> - <string name="permlab_setAlwaysFinish">"Altijd voltooien instellen"</string> - <string name="permdesc_setAlwaysFinish">"Hiermee kan een toepassing besturen of activiteiten altijd voltooid moeten zijn als ze naar de achtergrond gaan. Nooit nodig voor normale toepassingen."</string> - <string name="permlab_fotaUpdate">"Installatie van systeemupdate"</string> - <string name="permdesc_fotaUpdate">"Hiermee kan een toepassing meldingen ontvangen over systeemupdates die in behandeling zijn en hun installatie in gang zetten. Slechte toepassingen kunnen dit misbruiken om het te beschadigen met niet toegestane updates, of algemeen het updaten te verstoren."</string> - <!-- no translation found for permlab_batteryStats (1598947993704535568) --> - <skip /> - <!-- no translation found for permdesc_batteryStats (6247598531831307989) --> - <skip /> - <string name="permlab_internalSystemWindow">"Intern systeemvenster"</string> - <string name="permdesc_internalSystemWindow">"Hiermee kunnen vensters worden gemaakt die bedoeld zijn voor gebruik door het interne systeem van de gebruikersinterface. Niet bedoeld voor normale toepassingen."</string> - <string name="permlab_systemAlertWindow">"Systeemmeldingen"</string> - <string name="permdesc_systemAlertWindow">"Hiermee kan een toepassing systeemmeldingen weergeven. Slechte toepassingen kunnen het volledige toestelscherm overnemen."</string> - <string name="permlab_setAnimationScale">"Animatieschaal instellen"</string> - <string name="permdesc_setAnimationScale">"Hiermee kan een toepassing op elk moment de globale animatiesnelheid (snellere of tragere animaties) veranderen."</string> - <string name="permlab_manageAppTokens">"Toepassingstokens beheren"</string> - <string name="permdesc_manageAppTokens">"Hiermee kunnen toepassingen hun eigen tokens maken, en zo de normale Z-ordening buiten spel zetten. Is bij normale toepassingen nooit nodig."</string> - <string name="permlab_injectEvents">"Invoergebeurtenissen invoegen"</string> - <string name="permdesc_injectEvents">"Hiermee kan een toepassing zijn eigen invoergebeurtenissen (toetsindrukken enz.) naar andere toepassingen sturen. Slechte toepassingen kunnen dit misbruiken om controle over het toestel te krijgen."</string> - <string name="permlab_readInputState">"Invoerstatus lezen"</string> - <string name="permdesc_readInputState">"Hiermee kunnen toepassingen de toetsen zien die u indrukt, ook bij interactie met een andere toepassing (zoals het invoeren van een wachtwoord). Is bij normale toepassingen nooit nodig."</string> - <string name="permlab_setOrientation">"Ligging instellen"</string> - <string name="permdesc_setOrientation">"Hiermee kan een toepassing op elk moment de schermligging veranderen. Is bij normale toepassingen nooit nodig."</string> - <string name="permlab_signalPersistentProcesses">"Signaal naar aanhoudende processen"</string> - <string name="permdesc_signalPersistentProcesses">"Hiermee kan een toepassing verzoeken dat het meegeleverde signaal naar alle aanhoudende processen wordt gestuurd."</string> - <string name="permlab_persistentActivity">"Aanhoudende handelingen"</string> - <string name="permdesc_persistentActivity">"Hiermee kan een toepassing gedeeltes van zichzelf fixeren, zodat het systeem deze niet meer kan gebruiken voor andere toepassingen."</string> - <string name="permlab_deletePackages">"Pakketten verwijderen"</string> - <string name="permdesc_deletePackages">"Hiermee kan een toepassing Android-pakketten verwijderen. Slechte toepassingen kunnen dit misbruiken om belangrijke toepassingen te wissen."</string> - <!-- no translation found for permlab_clearAppUserData (3858185484601410171) --> - <skip /> - <!-- no translation found for permdesc_clearAppUserData (7233537744753081136) --> - <skip /> - <string name="permlab_deleteCacheFiles">"Cachebestanden wissen"</string> - <string name="permdesc_deleteCacheFiles">"Hiermee kan een toepassing cachebestanden verwijderen."</string> - <!-- no translation found for permlab_getPackageSize (6743556676630447973) --> - <skip /> - <!-- no translation found for permdesc_getPackageSize (2893996655828539776) --> - <skip /> - <string name="permlab_installPackages">"Pakketten installeren"</string> - <string name="permdesc_installPackages">"Hiermee kan een toepassing nieuwe Android- pakketten installeren of updaten. Slechte toepassingen kunnen dit misbruiken om nieuwe toepassingen met twijfelachtig verstrekkende rechten toe te voegen."</string> - <string name="permlab_clearAppCache">"Cachegegevens van toepassing wissen"</string> - <string name="permdesc_clearAppCache">"Hiermee kan een toepassing toestelgeheugen vrijmaken door bestanden uit de cachemap te verwijderen. Toegang wordt meestal beperkt tot systeemprocessen."</string> - <!-- no translation found for permlab_readLogs (6653488552442991707) --> - <skip /> - <!-- no translation found for permdesc_readLogs (356352685800884319) --> - <skip /> - <!-- no translation found for permlab_diagnostic (2955142476313469329) --> - <skip /> - <!-- no translation found for permdesc_diagnostic (1282409892215520166) --> - <skip /> - <string name="permlab_changeComponentState">"Toepassingscomponenten in- of uitschakelen"</string> - <string name="permdesc_changeComponentState">"Hiermee kan een toepassing veranderen of een component van een andere toepassing wordt ingeschakeld of niet. Slechte toepassingen kunnen dit gebruiken om belangrijke toestelfuncties uit te schakelen. Wees voorzichtig met het verlenen van toestemmingen, omdat het mogelijk is dat toepassingscomponenten in een onbruikbare, inconsistente of instabiele toestand geraken."</string> - <string name="permlab_setPreferredApplications">"Voorkeurstoepassingen instellen"</string> - <string name="permdesc_setPreferredApplications">"Hiermee kan een toepassing uw voorkeurstoepassingen veranderen. Slechte toepassingen kunnen zo stilletjes veranderen welke toepassingen gestart moeten worden, zodat bestaande toepassingen privégegevens over u verzamelen."</string> - <string name="permlab_writeSettings">"Systeeminstellingen schrijven"</string> - <string name="permdesc_writeSettings">"Hiermee kan een toepassing de instellingsgegevens van het systeem aanpassen. Slechte toepassingen kunnen de configuratie van het systeem beschadigen."</string> - <!-- no translation found for permlab_writeSecureSettings (4851801872124242319) --> - <skip /> - <!-- no translation found for permdesc_writeSecureSettings (2080620249472761366) --> - <skip /> - <!-- no translation found for permlab_writeGservices (296370685945777755) --> - <skip /> - <!-- no translation found for permdesc_writeGservices (2496928471286495053) --> - <skip /> - <string name="permlab_receiveBootCompleted">"Uitvoeren bij opstarten"</string> - <string name="permdesc_receiveBootCompleted">"Hiermee kan een toepassing zichzelf starten zodra het systeem is opgestart. Hierdoor kan het opstarten van het toestel langer duren en de toepassing het toestel afremmen omdat het altijd is geactiveerd."</string> - <string name="permlab_broadcastSticky">"Belangrijke uitzending"</string> - <string name="permdesc_broadcastSticky">"Hiermee kan een toepassing plakuitzendingen versturen, die blijven hangen als de uitzending stopt. Verkeerde toepassingen kunnen het toestel traag of instabiel maken door te veel geheugengebruik te veroorzaken."</string> - <string name="permlab_readContacts">"Contactgegevens lezen"</string> - <string name="permdesc_readContacts">"Hiermee kan een toepassing alle contactgegevens (adres) op het toestel lezen. Slechte toepassingen kunnen dit misbruiken om uw gegevens naar andere personen te sturen."</string> - <string name="permlab_writeContacts">"Contactgegevens schrijven"</string> - <string name="permdesc_writeContacts">"Hiermee kan een toepassing alle contactgegevens (adres) op het toestel aanpassen. Slechte toepassingen kunnen dit misbruiken om contactgegevens te wissen of wijzigen."</string> - <!-- no translation found for permlab_writeOwnerData (8036840529708535113) --> - <skip /> - <!-- no translation found for permdesc_writeOwnerData (5873447528845878348) --> - <skip /> - <!-- no translation found for permlab_readOwnerData (1847040178513733757) --> - <skip /> - <!-- no translation found for permdesc_readOwnerData (7563299529149214764) --> - <skip /> - <!-- no translation found for permlab_readCalendar (2111238731453410895) --> - <skip /> - <!-- no translation found for permdesc_readCalendar (4408253940601239114) --> - <skip /> - <!-- no translation found for permlab_writeCalendar (7518052789370653396) --> - <skip /> - <!-- no translation found for permdesc_writeCalendar (8057304232140147596) --> - <skip /> - <!-- no translation found for permlab_accessMockLocation (321094551062270213) --> - <skip /> - <!-- no translation found for permdesc_accessMockLocation (3651565866471419739) --> - <skip /> - <!-- no translation found for permlab_accessLocationExtraCommands (8291822077788811687) --> - <skip /> - <!-- no translation found for permdesc_accessLocationExtraCommands (5135782633548630731) --> - <skip /> - <string name="permlab_accessFineLocation">"Toegang tot gps-locatie"</string> - <string name="permdesc_accessFineLocation">"Open, indien beschikbaar, het Global Positioning System op het toestel. Verkeerde toepassingen kunnen dit gebruiken om te bepalen waar u zich bevindt en extra batterijstroom verbruiken."</string> - <string name="permlab_accessCoarseLocation">"Toegang tot netwerklocatie"</string> - <string name="permdesc_accessCoarseLocation">"Gebruik, indien beschikbaar, een netwerkdatabank om de locatie van het toestel te schatten. Verkeerde toepassingen kunnen dit gebruiken om te schatten waar u zich bevindt."</string> - <string name="permlab_accessSurfaceFlinger">"Toegang tot SurfaceFlinger"</string> - <string name="permdesc_accessSurfaceFlinger">"Toepassingen mogen de functies op laag niveau van SurfaceFlinger gebruiken."</string> - <string name="permlab_readFrameBuffer">"Framebuffer lezen"</string> - <string name="permdesc_readFrameBuffer">"Hiermee kan een toepassing de gegevens van de framebuffer lezen."</string> - <string name="permlab_modifyAudioSettings">"Audioinstellingen wijzigen"</string> - <string name="permdesc_modifyAudioSettings">"Hiermee kan een toepassing globale audioinstellingen aanpassen, zoals volume en route."</string> - <string name="permlab_recordAudio">"Audio opnemen"</string> - <string name="permdesc_recordAudio">"Hierdoor kan een toepassing het opnamepad voor audio openen."</string> - <string name="permlab_camera">"Camera"</string> - <!-- unknown placeholder BREAK in permdesc_camera --> - <skip /> - <string name="permlab_brick">"Toestel uitschakelen"</string> - <string name="permdesc_brick">"Hiermee kan de toepassing de gehele dienst permanent uitschakelen. Dit is erg gevaarlijk."</string> - <!-- no translation found for permlab_reboot (8844650672567077423) --> - <skip /> - <!-- no translation found for permdesc_reboot (4704919552870918328) --> - <skip /> - <string name="permlab_mount_unmount_filesystems">"Bestandssystemen koppelen en losmaken"</string> - <string name="permdesc_mount_unmount_filesystems">"Hiermee kan de toepassing bestandssystemen voor verwisselbaar geheugen koppelen en loskoppelen."</string> - <string name="permlab_vibrate">"Triller"</string> - <string name="permdesc_vibrate">"Hiermee kan de toepassing de triller besturen."</string> - <string name="permlab_flashlight">"Flitser"</string> - <string name="permdesc_flashlight">"Hiermee kan de toepassing de flitser besturen."</string> - <string name="permlab_hardware_test">"Hardwaretest"</string> - <string name="permdesc_hardware_test">"Hiermee kan de toepassing diverse randapparaten besturen met als doel het testen van de hardware."</string> - <string name="permlab_callPhone">"Telefoonnummers bellen"</string> - <string name="permdesc_callPhone">"Hiermee kunnen toepassingen telefoonnummers bellen zonder uw tussenkomst. Verkeerde toepassingen kunnen ongewilde gesprekken op uw telefoonrekening veroorzaken."</string> - <!-- no translation found for permlab_callPrivileged (2166923597287697159) --> - <skip /> - <!-- no translation found for permdesc_callPrivileged (5109789447971735501) --> - <skip /> - <!-- no translation found for permlab_locationUpdates (4216418293360456836) --> - <skip /> - <!-- no translation found for permdesc_locationUpdates (7635814693478743648) --> - <skip /> - <!-- no translation found for permlab_checkinProperties (2260796787386280708) --> - <skip /> - <!-- no translation found for permdesc_checkinProperties (3508022022841741945) --> - <skip /> - <!-- no translation found for permlab_modifyPhoneState (7791696535097912313) --> - <skip /> - <!-- no translation found for permdesc_modifyPhoneState (6352405226410454770) --> - <skip /> - <!-- no translation found for permlab_readPhoneState (7320082586621086653) --> - <skip /> - <!-- no translation found for permdesc_readPhoneState (8004450067066407969) --> - <skip /> - <!-- no translation found for permlab_wakeLock (1591164750935072136) --> - <skip /> - <!-- no translation found for permdesc_wakeLock (160471538196734936) --> - <skip /> - <string name="permlab_devicePower">"Toestel inschakelen"</string> - <string name="permdesc_devicePower">"Hiermee kan de toepassing het toestel in- of uitschakelen, of ingeschakeld laten."</string> - <string name="permlab_factoryTest">"Fabriekstest"</string> - <string name="permdesc_factoryTest">"Uitvoeren als een fabriekstest op laag niveau, waardoor volledige toegang tot de toestelhardware gegeven is. Alleen beschikbaar als een toestel in de fabriekstestmodus staat."</string> - <string name="permlab_setWallpaper">"Achtergrond instellen"</string> - <string name="permdesc_setWallpaper">"Hiermee kan de toepassing de systeemachtergrond instellen."</string> - <!-- no translation found for permlab_setWallpaperHints (4192438316932517807) --> - <skip /> - <!-- no translation found for permdesc_setWallpaperHints (738757439960921674) --> - <skip /> - <string name="permlab_masterClear">"Volledige systeemreset"</string> - <string name="permdesc_masterClear">"Hiermee kan een toepassing het systeem volledig opnieuw instellen op fabrieksinstellingen, waardoor alle gegevens, configuratie en geïnstalleerde toepassingen worden gewist."</string> - <!-- no translation found for permlab_setTimeZone (477196167239548690) --> - <skip /> - <!-- no translation found for permdesc_setTimeZone (8564892020460841198) --> - <skip /> - <!-- no translation found for permlab_getAccounts (2764070033402295170) --> - <skip /> - <!-- no translation found for permdesc_getAccounts (1203491378748649898) --> - <skip /> - <!-- no translation found for permlab_accessNetworkState (2032916924886010827) --> - <skip /> - <!-- no translation found for permdesc_accessNetworkState (7081329402551195933) --> - <skip /> - <!-- no translation found for permlab_createNetworkSockets (4706698319966917864) --> - <skip /> - <!-- no translation found for permdesc_createNetworkSockets (2580337178778551792) --> - <skip /> - <!-- no translation found for permlab_writeApnSettings (3190585220761979369) --> - <skip /> - <!-- no translation found for permdesc_writeApnSettings (4093875220468761052) --> - <skip /> - <!-- no translation found for permlab_changeNetworkState (2710779001260856872) --> - <skip /> - <!-- no translation found for permdesc_changeNetworkState (8076109230787022270) --> - <skip /> - <!-- no translation found for permlab_accessWifiState (3613679494230374297) --> - <skip /> - <!-- no translation found for permdesc_accessWifiState (8226508433563326925) --> - <skip /> - <!-- no translation found for permlab_changeWifiState (6043889338995432957) --> - <skip /> - <!-- no translation found for permdesc_changeWifiState (7829372845909567994) --> - <skip /> - <!-- no translation found for permlab_bluetoothAdmin (5513286736585647334) --> - <skip /> - <!-- no translation found for permdesc_bluetoothAdmin (1838208497914347365) --> - <skip /> - <!-- no translation found for permlab_bluetooth (6378797624765639115) --> - <skip /> - <!-- no translation found for permdesc_bluetooth (8592386018922265273) --> - <skip /> - <!-- no translation found for permlab_disableKeyguard (4574886811903233903) --> - <skip /> - <!-- no translation found for permdesc_disableKeyguard (815972646344251271) --> - <skip /> - <!-- no translation found for permlab_readSyncSettings (8818819977141505127) --> - <skip /> - <!-- no translation found for permdesc_readSyncSettings (8454705401908767847) --> - <skip /> - <!-- no translation found for permlab_writeSyncSettings (4514911143753152941) --> - <skip /> - <!-- no translation found for permdesc_writeSyncSettings (7630627689635091836) --> - <skip /> - <!-- no translation found for permlab_readSyncStats (5748337739678952863) --> - <skip /> - <!-- no translation found for permdesc_readSyncStats (582551457321957183) --> - <skip /> - <!-- no translation found for permlab_subscribedFeedsRead (2043206814904506589) --> - <skip /> - <!-- no translation found for permdesc_subscribedFeedsRead (6977343942680042449) --> - <skip /> - <!-- no translation found for permlab_subscribedFeedsWrite (2556727307229571556) --> - <skip /> - <!-- no translation found for permdesc_subscribedFeedsWrite (4134783294590266220) --> - <skip /> - <!-- no translation found for phoneTypes:0 (6070018634209800981) --> - <!-- no translation found for phoneTypes:1 (1514509689885965711) --> - <!-- no translation found for phoneTypes:2 (497473201754095234) --> - <!-- no translation found for phoneTypes:3 (5554432614281047787) --> - <!-- no translation found for phoneTypes:4 (2222084401110150993) --> - <!-- no translation found for phoneTypes:5 (2290007103906353121) --> - <!-- no translation found for phoneTypes:6 (6930783706213719251) --> - <!-- no translation found for phoneTypes:7 (1326005699931077792) --> - <!-- no translation found for emailAddressTypes:0 (1540640638077615417) --> - <!-- no translation found for emailAddressTypes:1 (4252853367575831977) --> - <!-- no translation found for emailAddressTypes:2 (7158046581744435718) --> - <!-- no translation found for emailAddressTypes:3 (3625034471181268169) --> - <!-- no translation found for postalAddressTypes:0 (5732960259696659380) --> - <!-- no translation found for postalAddressTypes:1 (7132240704786130285) --> - <!-- no translation found for postalAddressTypes:2 (1317604357745852817) --> - <!-- no translation found for postalAddressTypes:3 (1582953598462826702) --> - <!-- no translation found for imAddressTypes:0 (7806620012096518833) --> - <!-- no translation found for imAddressTypes:1 (5748846799950672787) --> - <!-- no translation found for imAddressTypes:2 (6196536810275073680) --> - <!-- no translation found for imAddressTypes:3 (8519128375350623648) --> - <!-- no translation found for organizationTypes:0 (1299224825223821142) --> - <!-- no translation found for organizationTypes:1 (2455717447227299354) --> - <!-- no translation found for organizationTypes:2 (7027570839313438290) --> - <!-- no translation found for imProtocols:0 (3318725788774688043) --> - <!-- no translation found for imProtocols:1 (1787713387022932886) --> - <!-- no translation found for imProtocols:2 (6751174158442316516) --> - <!-- no translation found for imProtocols:3 (1151283347465052653) --> - <!-- no translation found for imProtocols:4 (2157980008878817934) --> - <!-- no translation found for imProtocols:5 (7836237460308230767) --> - <!-- no translation found for imProtocols:6 (1180789904462172516) --> - <!-- no translation found for imProtocols:7 (21955111672779862) --> - <string name="keyguard_password_enter_pin_code">"Pincode invoeren"</string> - <string name="keyguard_password_wrong_pin_code">"Onjuiste pincode!"</string> - <string name="keyguard_label_text">"Om vrij te geven drukt u op Menu en vervolgens op 0."</string> - <string name="emergency_call_dialog_number_for_display">"Alarmnummers"</string> - <string name="lockscreen_carrier_default">"(Geen service)"</string> - <string name="lockscreen_screen_locked">"Schermblokkering"</string> - <string name="lockscreen_instructions_when_pattern_enabled">"Druk op Menu om vrij te geven of bel een alarmnummer"</string> - <string name="lockscreen_instructions_when_pattern_disabled">"Druk op Menu om vrij te geven"</string> - <string name="lockscreen_pattern_instructions">"Patroon tekenen om vrij te geven"</string> - <string name="lockscreen_emergency_call">"Noodoproep"</string> - <string name="lockscreen_pattern_correct">"Juist!"</string> - <string name="lockscreen_pattern_wrong">"Verkeerd patroon! Nogmaals proberen"</string> - <string name="lockscreen_plugged_in">"Bezig met opladen (<xliff:g id="NUMBER">%d%%</xliff:g>)"</string> - <string name="lockscreen_low_battery">"Oplader verbinden"</string> - <!-- no translation found for lockscreen_missing_sim_message_short (5051192587315492957) --> - <skip /> - <string name="lockscreen_missing_sim_message">"Geen SIM in toestel"</string> - <string name="lockscreen_missing_sim_instructions">"Voer een SIM-kaart in"</string> - <!-- no translation found for lockscreen_network_locked_message (323609607922245071) --> - <skip /> - <!-- no translation found for lockscreen_sim_puk_locked_message (1005803622871256359) --> - <skip /> - <!-- no translation found for lockscreen_sim_puk_locked_instructions (5033160098036646955) --> - <skip /> - <!-- no translation found for lockscreen_sim_locked_message (7398401200962556379) --> - <skip /> - <!-- no translation found for lockscreen_sim_unlock_progress_dialog_message (5939537246164692076) --> - <skip /> - <!-- unknown placeholder BREAK in lockscreen_too_many_failed_attempts_dialog_message --> - <skip /> - <!-- no translation found for lockscreen_failed_attempts_almost_glogin (1569017295989454551) --> - <skip /> - <string name="lockscreen_too_many_failed_attempts_countdown">"Probeer opnieuw over <xliff:g id="NUMBER">%d</xliff:g> seconden"</string> - <!-- no translation found for lockscreen_forgot_pattern_button_text (4219994639843985488) --> - <skip /> - <!-- no translation found for lockscreen_glogin_too_many_attempts (7504679498838839295) --> - <skip /> - <!-- no translation found for lockscreen_glogin_instructions (6542400673357252011) --> - <skip /> - <!-- no translation found for lockscreen_glogin_username_hint (6378418320242015111) --> - <skip /> - <!-- no translation found for lockscreen_glogin_password_hint (3224230234042131153) --> - <skip /> - <!-- no translation found for lockscreen_glogin_submit_button (5562051040043760034) --> - <skip /> - <!-- no translation found for lockscreen_glogin_invalid_input (4881057177478491580) --> - <skip /> - <!-- unknown placeholder FORMAT in status_bar_time_format --> - <skip /> - <!-- no translation found for hour_minute_ampm (1850330605794978742) --> - <skip /> - <!-- no translation found for hour_minute_cap_ampm (1122840227537374196) --> - <skip /> - <!-- no translation found for hour_ampm (7665432130905376251) --> - <skip /> - <!-- no translation found for hour_cap_ampm (3600295014648400268) --> - <skip /> - <!-- no translation found for status_bar_clear_all_button (2202004591253243750) --> - <skip /> - <string name="status_bar_no_notifications_title">"Meldingen"</string> - <string name="status_bar_ongoing_events_title">"Actueel"</string> - <string name="status_bar_latest_events_title">"Nieuwste gebeurtenissen"</string> - <string name="battery_status_text_percent_format">"<xliff:g id="NUMBER">%d</xliff:g>"</string> - <string name="battery_status_charging">"Bezig met opladen…"</string> - <string name="battery_low_title">"Oplader verbinden"</string> - <string name="battery_low_subtitle">"Lage batterijstroom"</string> - <string name="battery_low_percent_format">"Minder dan <xliff:g id="NUMBER">%d%%</xliff:g> resterend"</string> - <string name="factorytest_failed">"Fabriekstest mislukt"</string> - <string name="factorytest_not_system">"De handeling FACTORY_TEST wordt alleen ondersteund voor pakketten die geïnstalleerd zijn in /system/app."</string> - <string name="factorytest_no_action">"Geen pakket gevonden dat de handeling FACTORY_TEST levert."</string> - <string name="factorytest_reboot">"Opnieuw opstarten"</string> - <string name="save_password_label">"Bevestigen"</string> - <string name="save_password_message">"Wilt u dat de browser dit wachtwoord onthoudt?"</string> - <string name="save_password_notnow">"Niet nu"</string> - <string name="save_password_remember">"Onthouden"</string> - <string name="save_password_never">"Nooit"</string> - <string name="open_permission_deny">"U hebt geen toestemming om deze pagina te openen."</string> - <!-- no translation found for text_copied (6106873823411904723) --> - <skip /> - <string name="more_item_label">"Meer"</string> - <string name="prepend_shortcut_label">"Menu+"</string> - <!-- no translation found for menu_space_shortcut_label (194586306440382711) --> - <skip /> - <!-- no translation found for menu_enter_shortcut_label (7214761412193519345) --> - <skip /> - <!-- no translation found for menu_delete_shortcut_label (2854936426194985313) --> - <skip /> - <string name="search_go">"Ga naar"</string> - <string name="today">"Vandaag"</string> - <string name="yesterday">"Gisteren"</string> - <string name="tomorrow">"Morgen"</string> - <!-- no translation found for oneMonthDurationPast (3402179395240209557) --> - <skip /> - <!-- no translation found for beforeOneMonthDurationPast (7578100953282866827) --> - <skip /> - <!-- no translation found for num_seconds_ago:one (7416512229671810725) --> - <!-- no translation found for num_seconds_ago:other (8138756910300398447) --> - <!-- no translation found for num_minutes_ago:one (8620869479299420562) --> - <!-- no translation found for num_minutes_ago:other (5065488162050522741) --> - <!-- no translation found for num_hours_ago:one (853404611989669641) --> - <!-- no translation found for num_hours_ago:other (3558873784561756849) --> - <!-- no translation found for num_days_ago:one (4222479980812128212) --> - <!-- no translation found for num_days_ago:other (5445701370433601703) --> - <!-- no translation found for in_num_seconds:one (4253290037777327003) --> - <!-- no translation found for in_num_seconds:other (1280033870920841404) --> - <!-- no translation found for in_num_minutes:one (1487585791027953091) --> - <!-- no translation found for in_num_minutes:other (6274204576475209932) --> - <!-- no translation found for in_num_hours:one (6501470863235186391) --> - <!-- no translation found for in_num_hours:other (4415358752953289251) --> - <!-- no translation found for in_num_days:one (5608475533104443893) --> - <!-- no translation found for in_num_days:other (3827193006163842267) --> - <string name="preposition_for_date">"op %s"</string> - <string name="preposition_for_time">"bij %s"</string> - <string name="preposition_for_year">"in %s"</string> - <string name="day">"dag"</string> - <string name="days">"dagen"</string> - <string name="hour">"uur"</string> - <string name="hours">"uur"</string> - <string name="minute">"minuut"</string> - <string name="minutes">"minuten"</string> - <string name="second">"seconde"</string> - <string name="seconds">"seconden"</string> - <string name="week">"week"</string> - <string name="weeks">"weken"</string> - <!-- no translation found for year (8024790425994085153) --> - <skip /> - <!-- no translation found for years (8592090054773244417) --> - <skip /> - <string name="sunday">"Zondag"</string> - <string name="monday">"Maandag"</string> - <string name="tuesday">"Dinsdag"</string> - <string name="wednesday">"Woensdag"</string> - <string name="thursday">"Donderdag"</string> - <string name="friday">"Vrijdag"</string> - <string name="saturday">"Zaterdag"</string> - <string name="every_weekday">"Elke werkdag (Maa–vri)"</string> - <string name="daily">"Elke dag"</string> - <string name="weekly">"Wekelijks op <xliff:g id="DAY">%s</xliff:g>"</string> - <string name="monthly">"Elke mnd"</string> - <string name="yearly">"Elk jaar"</string> - <string name="VideoView_error_title">"Videoafspeelfout"</string> - <string name="VideoView_error_text_unknown">"Fout opgetreden bij afspelen van geselecteerde video."</string> - <string name="VideoView_error_button">"OK"</string> - <string name="am">"AM"</string> - <string name="pm">"PM"</string> - <!-- unknown placeholder FORMAT in numeric_date --> - <skip /> - <!-- unknown placeholder FORMAT in wday1_date1_time1_wday2_date2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in wday1_date1_wday2_date2 --> - <skip /> - <!-- unknown placeholder FORMAT in date1_time1_date2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in date1_date2 --> - <skip /> - <!-- unknown placeholder FORMAT in time1_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in time_wday_date --> - <skip /> - <!-- unknown placeholder FORMAT in wday_date --> - <skip /> - <!-- unknown placeholder FORMAT in time_date --> - <skip /> - <!-- unknown placeholder FORMAT in time_wday --> - <skip /> - <!-- no translation found for full_date_month_first (6011143962222283357) --> - <skip /> - <!-- no translation found for full_date_day_first (8621594762705478189) --> - <skip /> - <!-- no translation found for medium_date_month_first (48990963718825728) --> - <skip /> - <!-- no translation found for medium_date_day_first (2898992016440387123) --> - <skip /> - <!-- no translation found for twelve_hour_time_format (6015557937879492156) --> - <skip /> - <!-- no translation found for twenty_four_hour_time_format (5176807998669709535) --> - <skip /> - <string name="noon">"12 uur \'smiddags"</string> - <string name="Noon">"12 uur \'smiddags"</string> - <string name="midnight">"middernacht"</string> - <string name="Midnight">"Middernacht"</string> - <!-- unknown placeholder FORMAT in month_day --> - <skip /> - <!-- unknown placeholder FORMAT in month --> - <skip /> - <!-- unknown placeholder FORMAT in month_day_year --> - <skip /> - <!-- unknown placeholder FORMAT in month_year --> - <skip /> - <!-- no translation found for time_of_day (8375993139317154157) --> - <skip /> - <!-- no translation found for date_and_time (9197690194373107109) --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_md1_md2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_wday1_md1_wday2_md2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_mdy1_mdy2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_wday1_mdy1_wday2_mdy2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_md1_time1_md2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_wday1_md1_time1_wday2_md2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_mdy1_time1_mdy2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_wday1_mdy1_time1_wday2_mdy2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_md1_md2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_wday1_md1_wday2_md2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_mdy1_mdy2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_wday1_mdy1_wday2_mdy2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_md1_time1_md2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_wday1_md1_time1_wday2_md2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_mdy1_time1_mdy2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_wday1_mdy1_time1_wday2_mdy2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_md1_md2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_wday1_md1_wday2_md2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_mdy1_mdy2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_wday1_mdy1_wday2_mdy2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_md1_time1_md2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_wday1_md1_time1_wday2_md2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_mdy1_time1_mdy2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_wday1_mdy1_time1_wday2_mdy2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in abbrev_month_day_year --> - <skip /> - <!-- unknown placeholder FORMAT in abbrev_month_year --> - <skip /> - <!-- unknown placeholder FORMAT in abbrev_month_day --> - <skip /> - <!-- unknown placeholder FORMAT in abbrev_month --> - <skip /> - <string name="day_of_week_long_sunday">"Zondag"</string> - <string name="day_of_week_long_monday">"Maandag"</string> - <string name="day_of_week_long_tuesday">"Dinsdag"</string> - <string name="day_of_week_long_wednesday">"Woensdag"</string> - <string name="day_of_week_long_thursday">"Donderdag"</string> - <string name="day_of_week_long_friday">"Vrijdag"</string> - <string name="day_of_week_long_saturday">"Zaterdag"</string> - <string name="day_of_week_medium_sunday">"Zon"</string> - <string name="day_of_week_medium_monday">"Maa"</string> - <string name="day_of_week_medium_tuesday">"Din"</string> - <string name="day_of_week_medium_wednesday">"Woe"</string> - <string name="day_of_week_medium_thursday">"Don"</string> - <string name="day_of_week_medium_friday">"Vri"</string> - <string name="day_of_week_medium_saturday">"Zat"</string> - <string name="day_of_week_short_sunday">"Zo"</string> - <string name="day_of_week_short_monday">"Ma"</string> - <string name="day_of_week_short_tuesday">"Di"</string> - <string name="day_of_week_short_wednesday">"Wo"</string> - <string name="day_of_week_short_thursday">"Do"</string> - <string name="day_of_week_short_friday">"Vr"</string> - <string name="day_of_week_short_saturday">"Za"</string> - <string name="day_of_week_shorter_sunday">"Zo"</string> - <string name="day_of_week_shorter_monday">"M"</string> - <string name="day_of_week_shorter_tuesday">"Di"</string> - <string name="day_of_week_shorter_wednesday">"W"</string> - <string name="day_of_week_shorter_thursday">"Do"</string> - <string name="day_of_week_shorter_friday">"V"</string> - <string name="day_of_week_shorter_saturday">"Za"</string> - <string name="day_of_week_shortest_sunday">"Z"</string> - <string name="day_of_week_shortest_monday">"M"</string> - <string name="day_of_week_shortest_tuesday">"Di"</string> - <string name="day_of_week_shortest_wednesday">"W"</string> - <string name="day_of_week_shortest_thursday">"Do"</string> - <string name="day_of_week_shortest_friday">"V"</string> - <string name="day_of_week_shortest_saturday">"Z"</string> - <string name="month_long_january">"Januari"</string> - <string name="month_long_february">"Februari"</string> - <string name="month_long_march">"Maart"</string> - <string name="month_long_april">"April"</string> - <string name="month_long_may">"Mei"</string> - <string name="month_long_june">"Juni"</string> - <string name="month_long_july">"Juli"</string> - <string name="month_long_august">"Augustus"</string> - <string name="month_long_september">"September"</string> - <string name="month_long_october">"Oktober"</string> - <string name="month_long_november">"November"</string> - <string name="month_long_december">"December"</string> - <string name="month_medium_january">"jan"</string> - <string name="month_medium_february">"feb"</string> - <string name="month_medium_march">"mrt"</string> - <string name="month_medium_april">"apr"</string> - <string name="month_medium_may">"mei"</string> - <string name="month_medium_june">"jun"</string> - <string name="month_medium_july">"jul"</string> - <string name="month_medium_august">"aug"</string> - <string name="month_medium_september">"sep"</string> - <string name="month_medium_october">"okt"</string> - <string name="month_medium_november">"nov"</string> - <string name="month_medium_december">"dec"</string> - <string name="month_shortest_january">"J"</string> - <string name="month_shortest_february">"F"</string> - <string name="month_shortest_march">"M"</string> - <string name="month_shortest_april">"A"</string> - <string name="month_shortest_may">"M"</string> - <string name="month_shortest_june">"J"</string> - <string name="month_shortest_july">"J"</string> - <string name="month_shortest_august">"A"</string> - <string name="month_shortest_september">"S"</string> - <string name="month_shortest_october">"O"</string> - <string name="month_shortest_november">"N"</string> - <string name="month_shortest_december">"D"</string> - <!-- unknown placeholder FORMAT in elapsed_time_short_format_mm_ss --> - <skip /> - <!-- unknown placeholder FORMAT in elapsed_time_short_format_h_mm_ss --> - <skip /> - <string name="selectAll">"Alles selecteren"</string> - <string name="cut">"Knippen"</string> - <!-- no translation found for cutAll (4474519683293791451) --> - <skip /> - <string name="copy">"Kopiëren"</string> - <!-- no translation found for copyAll (4777548804630476932) --> - <skip /> - <string name="paste">"Plakken"</string> - <string name="copyUrl">"URL kopiëren"</string> - <!-- no translation found for inputMethod (7911866729148111492) --> - <skip /> - <!-- no translation found for editTextMenuTitle (3984253728638788023) --> - <skip /> - <string name="low_internal_storage_view_title">"Weinig intern geheugen"</string> - <string name="low_internal_storage_view_text">"Toestel heeft weinig intern geheugen"</string> - <string name="ok">"OK"</string> - <string name="cancel">"Annuleren"</string> - <string name="yes">"OK"</string> - <string name="no">"Annuleren"</string> - <string name="capital_on">"AAN"</string> - <string name="capital_off">"UIT"</string> - <string name="whichApplication">"Welke toepassing wilt u gebruiken?"</string> - <string name="alwaysUse">"Deze toepassing altijd gebruiken voor deze toepassing"</string> - <!-- no translation found for clearDefaultHintMsg (5742432113023174321) --> - <skip /> - <string name="chooseActivity">"Een actie selecteren"</string> - <string name="noApplications">"Geen toepassingen beschikbaar om de handeling uit te voeren"</string> - <!-- no translation found for aerr_title (2654390351574026098) --> - <skip /> - <!-- no translation found for aerr_application (4917288809565116720) --> - <skip /> - <!-- no translation found for aerr_process (1273819861108073461) --> - <skip /> - <!-- no translation found for anr_title (3305935690891435915) --> - <skip /> - <!-- no translation found for anr_activity_application (1653036325679156678) --> - <skip /> - <!-- no translation found for anr_activity_process (2674027618362070465) --> - <skip /> - <!-- no translation found for anr_application_process (2163656674970221928) --> - <skip /> - <!-- no translation found for anr_process (7747550780123472160) --> - <skip /> - <!-- no translation found for force_close (9020954128872810669) --> - <skip /> - <!-- no translation found for wait (7973775702304037058) --> - <skip /> - <!-- no translation found for debug (857932504764728770) --> - <skip /> - <string name="sendText">"Kiezen wat met de tekst gebeurt"</string> - <!-- no translation found for volume_ringtone (4121694816346562058) --> - <skip /> - <!-- no translation found for volume_music (4869950240104717493) --> - <skip /> - <!-- no translation found for volume_call (5723421277753250395) --> - <skip /> - <!-- no translation found for volume_alarm (2752102730973081294) --> - <skip /> - <!-- no translation found for volume_unknown (6908187627672375742) --> - <skip /> - <!-- no translation found for ringtone_default (2873893375149093475) --> - <skip /> - <!-- no translation found for ringtone_default_with_actual (5474076151665761913) --> - <skip /> - <!-- no translation found for ringtone_silent (7477159279081654685) --> - <skip /> - <!-- no translation found for ringtone_picker_title (7055241890764367884) --> - <skip /> - <!-- no translation found for ringtone_unknown (6888219771401173795) --> - <skip /> - <!-- no translation found for wifi_available:one (8168012881468888470) --> - <!-- no translation found for wifi_available:other (4666122955807117718) --> - <!-- no translation found for wifi_available_detailed:one (5107769161192143259) --> - <!-- no translation found for wifi_available_detailed:other (853347657960575809) --> - <!-- no translation found for select_character (3735110139249491726) --> - <skip /> - <!-- no translation found for sms_control_default_app_name (7522184737840550841) --> - <skip /> - <!-- no translation found for sms_control_title (2742400596989418394) --> - <skip /> - <!-- no translation found for sms_control_message (3447126217666595989) --> - <skip /> - <!-- no translation found for sms_control_yes (8839660939359273650) --> - <skip /> - <!-- no translation found for sms_control_no (909756849988183801) --> - <skip /> - <!-- no translation found for date_time_set (2495199891239480952) --> - <skip /> - <!-- no translation found for default_permission_group (7742780381379652409) --> - <skip /> - <!-- no translation found for no_permissions (85461124044682315) --> - <skip /> - <!-- no translation found for perms_hide (4145325555929151849) --> - <skip /> - <!-- no translation found for perms_show_all (6040194843455403173) --> - <skip /> - <!-- no translation found for googlewebcontenthelper_loading (2140804350507245589) --> - <skip /> - <!-- no translation found for usb_storage_title (8699631567051394409) --> - <skip /> - <!-- no translation found for usb_storage_message (5344039189213308733) --> - <skip /> - <!-- no translation found for usb_storage_button_mount (6700104384375121662) --> - <skip /> - <!-- no translation found for usb_storage_button_unmount (465869657252626688) --> - <skip /> - <!-- no translation found for usb_storage_error_message (3192564550748426087) --> - <skip /> - <!-- no translation found for usb_storage_notification_title (6237028017872246940) --> - <skip /> - <!-- no translation found for usb_storage_notification_message (7371717280517625905) --> - <skip /> - <!-- no translation found for select_input_method (2658280517827502015) --> - <skip /> - <!-- no translation found for fast_scroll_alphabet (1017432309285755759) --> - <skip /> - <!-- no translation found for fast_scroll_numeric_alphabet (3092587363718901074) --> - <skip /> - <!-- no translation found for candidates_style (7738463880139922176) --> - <skip /> -</resources> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 5c54226..7ce4c66 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -262,6 +262,10 @@ <string name="permdesc_reboot">"Hiermee kan de toepassing de telefoon gedwongen opnieuw opstarten."</string> <string name="permlab_mount_unmount_filesystems">"bestandssystemen koppelen en ontkoppelen"</string> <string name="permdesc_mount_unmount_filesystems">"Hiermee kan de toepassing bestandssystemen koppelen en ontkoppelen voor verwisselbare opslagruimte."</string> + <!-- no translation found for permlab_mount_format_filesystems (5523285143576718981) --> + <skip /> + <!-- no translation found for permdesc_mount_format_filesystems (574060044906047386) --> + <skip /> <string name="permlab_vibrate">"trilstand beheren"</string> <string name="permdesc_vibrate">"Hiermee kan de toepassing de trilstand beheren."</string> <string name="permlab_flashlight">"zaklamp bedienen"</string> @@ -276,6 +280,10 @@ <string name="permdesc_locationUpdates">"Hiermee kunnen updatemeldingen voor locaties van de radio worden ingeschakeld/uitgeschakeld. Niet voor gebruik door normale toepassingen."</string> <string name="permlab_checkinProperties">"toegang tot checkin-eigenschappen"</string> <string name="permdesc_checkinProperties">"Hiermee wordt lees-/schrijftoegang gegeven tot eigenschappen die door de checkin-service zijn geüpload. Niet voor gebruik door normale toepassingen."</string> + <!-- no translation found for permlab_bindGadget (2519859363977647275) --> + <skip /> + <!-- no translation found for permdesc_bindGadget (7865866514555126333) --> + <skip /> <string name="permlab_modifyPhoneState">"telefoonstatus wijzigen"</string> <string name="permdesc_modifyPhoneState">"Hiermee kan de toepassing de telefoonfuncties van het apparaat beheren. Een toepassing met deze machtiging kan schakelen tussen netwerken, de radio van de telefoon in- of uitschakelen en dergelijke zonder dat u hiervan op de hoogte wordt gesteld."</string> <string name="permlab_readPhoneState">"telefoonstatus lezen"</string> @@ -304,6 +312,10 @@ <string name="permdesc_writeApnSettings">"Hiermee kan een toepassing de APN-instellingen, zoals proxy en poort, van elke APN wijzigen."</string> <string name="permlab_changeNetworkState">"netwerkverbinding wijzigen"</string> <string name="permdesc_changeNetworkState">"Hiermee kan een toepassing de verbindingsstatus van het netwerk wijzigen."</string> + <!-- no translation found for permlab_changeBackgroundDataSetting (1400666012671648741) --> + <skip /> + <!-- no translation found for permdesc_changeBackgroundDataSetting (1001482853266638864) --> + <skip /> <string name="permlab_accessWifiState">"Wi-Fi-status bekijken"</string> <string name="permdesc_accessWifiState">"Hiermee kan een toepassing informatie over de Wi-Fi-status bekijken."</string> <string name="permlab_changeWifiState">"Wi-Fi-status wijzigen"</string> @@ -546,12 +558,12 @@ <!-- no translation found for relative_time (1818557177829411417) --> <skip /> <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first">"<xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="DAY">dd</xliff:g>, <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMMM</xliff:g>, <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first">"<xliff:g id="MONTH">MMM</xliff:g> <xliff:g id="DAY">dd</xliff:g>, <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMM</xliff:g>, <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g>"</string> + <string name="full_date_month_first">"<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="full_date_day_first">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_month_first">"<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_day_first">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> + <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"twaalf uur \'s middags"</string> <string name="Noon">"Twaalf uur \'s middags"</string> <string name="midnight">"middernacht"</string> @@ -679,6 +691,8 @@ <string name="paste">"Plakken"</string> <string name="copyUrl">"URL kopiëren"</string> <string name="inputMethod">"Invoermethode"</string> + <!-- no translation found for addToDictionary (726256909274177272) --> + <skip /> <string name="editTextMenuTitle">"Tekst bewerken"</string> <string name="low_internal_storage_view_title">"Weinig ruimte"</string> <string name="low_internal_storage_view_text">"Opslagruimte van telefoon raakt op."</string> @@ -686,6 +700,8 @@ <string name="cancel">"Annuleren"</string> <string name="yes">"OK"</string> <string name="no">"Annuleren"</string> + <!-- no translation found for dialog_alert_title (2049658708609043103) --> + <skip /> <string name="capital_on">"AAN"</string> <string name="capital_off">"UIT"</string> <string name="whichApplication">"Actie voltooien met"</string> @@ -709,7 +725,8 @@ <string name="volume_music">"Mediavolume"</string> <string name="volume_music_hint_playing_through_bluetooth">"Afspelen via Bluetooth"</string> <string name="volume_call">"Volume inkomende oproep"</string> - <string name="volume_call_hint_playing_through_bluetooth">"Afspelen via Bluetooth"</string> + <!-- no translation found for volume_bluetooth_call (2002891926351151534) --> + <skip /> <string name="volume_alarm">"Alarmvolume"</string> <string name="volume_notification">"Meldingsvolume"</string> <string name="volume_unknown">"Volume"</string> @@ -745,8 +762,61 @@ <string name="usb_storage_error_message">"Er is een probleem bij het gebruik van uw SD-kaart voor USB-opslag."</string> <string name="usb_storage_notification_title">"USB-verbinding"</string> <string name="usb_storage_notification_message">"Selecteer dit om bestanden naar/van uw computer te kopiëren."</string> + <!-- no translation found for usb_storage_stop_notification_title (2336058396663516017) --> + <skip /> + <!-- no translation found for usb_storage_stop_notification_message (2591813490269841539) --> + <skip /> + <!-- no translation found for usb_storage_stop_title (6014127947456185321) --> + <skip /> + <!-- no translation found for usb_storage_stop_message (2390958966725232848) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_mount (1181858854166273345) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_unmount (3774611918660582898) --> + <skip /> + <!-- no translation found for usb_storage_stop_error_message (3917317248440072084) --> + <skip /> + <!-- no translation found for extmedia_format_title (8663247929551095854) --> + <skip /> + <!-- no translation found for extmedia_format_message (3621369962433523619) --> + <skip /> + <!-- no translation found for extmedia_format_button_format (4131064560127478695) --> + <skip /> <string name="select_input_method">"Invoermethode selecteren"</string> <string name="fast_scroll_alphabet">"ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> <string name="fast_scroll_numeric_alphabet">"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> - <string name="candidates_style"><font fgcolor="#ff000000" bgcolor="#ff8080ff"><u>"kandidaten"</u>"u>"</font></string> + <!-- no translation found for candidates_style (4333913089637062257) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_title (5457603418970994050) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_message (4747432538578886744) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_title (780477838241212997) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_message (1312266820092958014) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_title (6410723906019100189) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_message (2679412884290061775) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_title (6872152882604407837) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_message (7260183293747448241) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_title (6729801130790616200) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_message (7613960686747592770) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_title (8902518030404381318) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_message (4205117227342822275) --> + <skip /> + <!-- no translation found for activity_list_empty (4168820609403385789) --> + <skip /> + <!-- no translation found for permlab_pkgUsageStats (8787352074326748892) --> + <skip /> + <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> + <skip /> + <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> + <skip /> </resources> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index c3e3a46..03c4f8b 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -262,6 +262,10 @@ <string name="permdesc_reboot">"Pozwala aplikacji na wymuszenie ponownego uruchomienia telefonu."</string> <string name="permlab_mount_unmount_filesystems">"montowanie i odmontowanie systemów plików"</string> <string name="permdesc_mount_unmount_filesystems">"Pozwala aplikacjom na podłączanie i odłączanie systemów plików w pamięciach przenośnych."</string> + <!-- no translation found for permlab_mount_format_filesystems (5523285143576718981) --> + <skip /> + <!-- no translation found for permdesc_mount_format_filesystems (574060044906047386) --> + <skip /> <string name="permlab_vibrate">"kontrolowanie wibracji"</string> <string name="permdesc_vibrate">"Pozwala aplikacjom na kontrolowanie wibracji."</string> <string name="permlab_flashlight">"kontrolowanie latarki"</string> @@ -276,6 +280,10 @@ <string name="permdesc_locationUpdates">"Pozwala włączyć/wyłączyć powiadomienia o aktualizacji położenia przez radio. Nie wykorzystywane przez normalne aplikacje."</string> <string name="permlab_checkinProperties">"dostęp do właściwości usługi rezerwacji"</string> <string name="permdesc_checkinProperties">"Pozwala na dostęp z uprawnieniami do odczytu/zapisu do właściwości przesłanych przez usługę rezerwacji. Nie wykorzystywane przez normalne aplikacje."</string> + <!-- no translation found for permlab_bindGadget (2519859363977647275) --> + <skip /> + <!-- no translation found for permdesc_bindGadget (7865866514555126333) --> + <skip /> <string name="permlab_modifyPhoneState">"zmiana stanu telefonu"</string> <string name="permdesc_modifyPhoneState">"Pozwala aplikacji na kontrolowanie funkcji telefonu w urządzeniu. Aplikacja z tymi uprawnieniami może przełączać sieci, włączać i wyłączać radio itp. bez informowania użytkownika."</string> <string name="permlab_readPhoneState">"czytanie stanu telefonu"</string> @@ -304,6 +312,10 @@ <string name="permdesc_writeApnSettings">"Pozwala aplikacji na zmianę ustawień APN, takich jak serwer proxy oraz port dowolnego APN."</string> <string name="permlab_changeNetworkState">"zmienianie połączeń sieci"</string> <string name="permdesc_changeNetworkState">"Pozwala aplikacji na zmianę stanu połączeń sieciowych."</string> + <!-- no translation found for permlab_changeBackgroundDataSetting (1400666012671648741) --> + <skip /> + <!-- no translation found for permdesc_changeBackgroundDataSetting (1001482853266638864) --> + <skip /> <string name="permlab_accessWifiState">"wyświetlanie stanu Wi-Fi"</string> <string name="permdesc_accessWifiState">"Pozwala aplikacji na wyświetlanie informacji o stanie Wi-Fi."</string> <string name="permlab_changeWifiState">"zmiana stanu Wi-Fi"</string> @@ -333,7 +345,7 @@ <!-- no translation found for permdesc_writeDictionary (2241256206524082880) --> <skip /> <string-array name="phoneTypes"> - <item>"Strona główna"</item> + <item>"Dom"</item> <item>"Komórka"</item> <item>"Praca"</item> <item>"Faks w pracy"</item> @@ -343,19 +355,19 @@ <item>"Niestandardowy"</item> </string-array> <string-array name="emailAddressTypes"> - <item>"Strona główna"</item> + <item>"Dom"</item> <item>"Praca"</item> <item>"Inne"</item> <item>"Niestandardowy"</item> </string-array> <string-array name="postalAddressTypes"> - <item>"Strona główna"</item> + <item>"Dom"</item> <item>"Praca"</item> <item>"Inny"</item> <item>"Niestandardowy"</item> </string-array> <string-array name="imAddressTypes"> - <item>"Strona główna"</item> + <item>"Dom"</item> <item>"Praca"</item> <item>"Inne"</item> <item>"Niestandardowy"</item> @@ -546,12 +558,12 @@ <!-- no translation found for relative_time (1818557177829411417) --> <skip /> <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first">"<xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="DAY">dd</xliff:g>, <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMMM</xliff:g>, <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first">"<xliff:g id="MONTH">MMM</xliff:g> <xliff:g id="DAY">dd</xliff:g>, <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMM</xliff:g>, <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g>"</string> + <string name="full_date_month_first">"<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="full_date_day_first">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_month_first">"<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_day_first">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> + <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"południe"</string> <string name="Noon">"Południe"</string> <string name="midnight">"północ"</string> @@ -679,6 +691,8 @@ <string name="paste">"Wklej"</string> <string name="copyUrl">"Kopiuj adres URL"</string> <string name="inputMethod">"Metoda wejściowa"</string> + <!-- no translation found for addToDictionary (726256909274177272) --> + <skip /> <string name="editTextMenuTitle">"Edytuj tekst"</string> <string name="low_internal_storage_view_title">"Mało miejsca"</string> <string name="low_internal_storage_view_text">"Maleje ilość dostępnej pamięci telefonu."</string> @@ -686,6 +700,8 @@ <string name="cancel">"Anuluj"</string> <string name="yes">"OK"</string> <string name="no">"Anuluj"</string> + <!-- no translation found for dialog_alert_title (2049658708609043103) --> + <skip /> <string name="capital_on">"Włącz"</string> <string name="capital_off">"Wyłącz"</string> <string name="whichApplication">"Zakończ działanie, korzystając z"</string> @@ -709,7 +725,8 @@ <string name="volume_music">"Głośność multimediów"</string> <string name="volume_music_hint_playing_through_bluetooth">"Odtwarzanie przez Bluetooth"</string> <string name="volume_call">"Głośność podczas połączenia"</string> - <string name="volume_call_hint_playing_through_bluetooth">"Odtwarzanie przez Bluetooth"</string> + <!-- no translation found for volume_bluetooth_call (2002891926351151534) --> + <skip /> <string name="volume_alarm">"Głośność alarmu"</string> <string name="volume_notification">"Głośność powiadomienia"</string> <string name="volume_unknown">"Głośność"</string> @@ -745,8 +762,61 @@ <string name="usb_storage_error_message">"Wystąpił problem z wykorzystaniem karty SD dla pamięci USB."</string> <string name="usb_storage_notification_title">"Połączenie przez USB"</string> <string name="usb_storage_notification_message">"Wybierz, aby skopiować pliki do/z komputera"</string> + <!-- no translation found for usb_storage_stop_notification_title (2336058396663516017) --> + <skip /> + <!-- no translation found for usb_storage_stop_notification_message (2591813490269841539) --> + <skip /> + <!-- no translation found for usb_storage_stop_title (6014127947456185321) --> + <skip /> + <!-- no translation found for usb_storage_stop_message (2390958966725232848) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_mount (1181858854166273345) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_unmount (3774611918660582898) --> + <skip /> + <!-- no translation found for usb_storage_stop_error_message (3917317248440072084) --> + <skip /> + <!-- no translation found for extmedia_format_title (8663247929551095854) --> + <skip /> + <!-- no translation found for extmedia_format_message (3621369962433523619) --> + <skip /> + <!-- no translation found for extmedia_format_button_format (4131064560127478695) --> + <skip /> <string name="select_input_method">"Wybierz metodę wejściową"</string> <string name="fast_scroll_alphabet">"AĄBCĆDEĘFGHIJKLŁMNŃOÓPQRSŚTUVWXYZŹŻ"</string> <string name="fast_scroll_numeric_alphabet">"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> - <string name="candidates_style"><font fgcolor="#ff000000" bgcolor="#ff8080ff"><u>"kandydaci"</u></font></string> + <!-- no translation found for candidates_style (4333913089637062257) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_title (5457603418970994050) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_message (4747432538578886744) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_title (780477838241212997) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_message (1312266820092958014) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_title (6410723906019100189) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_message (2679412884290061775) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_title (6872152882604407837) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_message (7260183293747448241) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_title (6729801130790616200) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_message (7613960686747592770) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_title (8902518030404381318) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_message (4205117227342822275) --> + <skip /> + <!-- no translation found for activity_list_empty (4168820609403385789) --> + <skip /> + <!-- no translation found for permlab_pkgUsageStats (8787352074326748892) --> + <skip /> + <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> + <skip /> + <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> + <skip /> </resources> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 33efbf6..d25ab8d 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -262,6 +262,10 @@ <string name="permdesc_reboot">"Разрешает приложению принудительно перезагружать телефон."</string> <string name="permlab_mount_unmount_filesystems">"подключаться и отключаться от файловых систем"</string> <string name="permdesc_mount_unmount_filesystems">"Разрешает приложению подключаться и отключаться от файловых систем съемных устройств хранения."</string> + <!-- no translation found for permlab_mount_format_filesystems (5523285143576718981) --> + <skip /> + <!-- no translation found for permdesc_mount_format_filesystems (574060044906047386) --> + <skip /> <string name="permlab_vibrate">"управлять вибрацией"</string> <string name="permdesc_vibrate">"Разрешает приложению управлять вибровызовом."</string> <string name="permlab_flashlight">"управлять фонарем"</string> @@ -276,6 +280,10 @@ <string name="permdesc_locationUpdates">"Разрешает включение/отключение уведомлений о местоположении по радиосвязи. Не используется обычными приложениями."</string> <string name="permlab_checkinProperties">"открывать свойства проверки"</string> <string name="permdesc_checkinProperties">"Разрешает доступ на чтение и запись к свойствам, загруженным службой проверки. Не используется обычными приложениями."</string> + <!-- no translation found for permlab_bindGadget (2519859363977647275) --> + <skip /> + <!-- no translation found for permdesc_bindGadget (7865866514555126333) --> + <skip /> <string name="permlab_modifyPhoneState">"изменять состояние телефона"</string> <string name="permdesc_modifyPhoneState">"Позволяет приложению управлять телефонными функциями устройства. Приложение с такими полномочиями может переключать сети, включать и выключать радиосвязь и т.д., не сообщая вам об этом."</string> <string name="permlab_readPhoneState">"считывать состояние телефона"</string> @@ -304,6 +312,10 @@ <string name="permdesc_writeApnSettings">"Разрешает приложению изменять настройки APN, например прокси и порт любого APN."</string> <string name="permlab_changeNetworkState">"изменять подключение к сети"</string> <string name="permdesc_changeNetworkState">"Позволяет приложению изменять подключение к сети."</string> + <!-- no translation found for permlab_changeBackgroundDataSetting (1400666012671648741) --> + <skip /> + <!-- no translation found for permdesc_changeBackgroundDataSetting (1001482853266638864) --> + <skip /> <string name="permlab_accessWifiState">"просматривать состояние Wi-Fi"</string> <string name="permdesc_accessWifiState">"Разрешает приложению просматривать сведения о состоянии Wi-Fi."</string> <string name="permlab_changeWifiState">"изменять состояние Wi-Fi"</string> @@ -546,12 +558,12 @@ <!-- no translation found for relative_time (1818557177829411417) --> <skip /> <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g>"</string> + <string name="full_date_month_first">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="full_date_day_first">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_month_first">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_day_first">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> + <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"полдень"</string> <string name="Noon">"Полдень"</string> <string name="midnight">"полночь"</string> @@ -679,6 +691,8 @@ <string name="paste">"Вставить"</string> <string name="copyUrl">"Копировать URL"</string> <string name="inputMethod">"Способ ввода"</string> + <!-- no translation found for addToDictionary (726256909274177272) --> + <skip /> <string name="editTextMenuTitle">"Правка текста"</string> <string name="low_internal_storage_view_title">"Недостаточно места"</string> <string name="low_internal_storage_view_text">"В памяти телефона осталось мало места."</string> @@ -686,6 +700,8 @@ <string name="cancel">"Отмена"</string> <string name="yes">"ОК"</string> <string name="no">"Отмена"</string> + <!-- no translation found for dialog_alert_title (2049658708609043103) --> + <skip /> <string name="capital_on">"ВКЛ"</string> <string name="capital_off">"ВЫКЛ"</string> <string name="whichApplication">"Выполнить действие с помощью"</string> @@ -709,7 +725,8 @@ <string name="volume_music">"Громкость звука мультимедиа"</string> <string name="volume_music_hint_playing_through_bluetooth">"Воспроизводится через Bluetooth"</string> <string name="volume_call">"Громкость звонка"</string> - <string name="volume_call_hint_playing_through_bluetooth">"Воспроизводится через Bluetooth"</string> + <!-- no translation found for volume_bluetooth_call (2002891926351151534) --> + <skip /> <string name="volume_alarm">"Громкость будильника"</string> <string name="volume_notification">"Громкость уведомления"</string> <string name="volume_unknown">"Громкость"</string> @@ -745,8 +762,61 @@ <string name="usb_storage_error_message">"Не удается использовать карту SD в качестве USB-хранилища."</string> <string name="usb_storage_notification_title">"Подключение через USB"</string> <string name="usb_storage_notification_message">"Выберите для копирования файлов на/с компьютера."</string> + <!-- no translation found for usb_storage_stop_notification_title (2336058396663516017) --> + <skip /> + <!-- no translation found for usb_storage_stop_notification_message (2591813490269841539) --> + <skip /> + <!-- no translation found for usb_storage_stop_title (6014127947456185321) --> + <skip /> + <!-- no translation found for usb_storage_stop_message (2390958966725232848) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_mount (1181858854166273345) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_unmount (3774611918660582898) --> + <skip /> + <!-- no translation found for usb_storage_stop_error_message (3917317248440072084) --> + <skip /> + <!-- no translation found for extmedia_format_title (8663247929551095854) --> + <skip /> + <!-- no translation found for extmedia_format_message (3621369962433523619) --> + <skip /> + <!-- no translation found for extmedia_format_button_format (4131064560127478695) --> + <skip /> <string name="select_input_method">"Выбор способа ввода"</string> <string name="fast_scroll_alphabet">"АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЫЭЮЯ"</string> <string name="fast_scroll_numeric_alphabet">"0123456789АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЫЭЮЯ"</string> - <string name="candidates_style"><font fgcolor="#ff000000" bgcolor="#ff8080ff"><u>"кандидаты"</u>"u>"</font></string> + <!-- no translation found for candidates_style (4333913089637062257) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_title (5457603418970994050) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_message (4747432538578886744) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_title (780477838241212997) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_message (1312266820092958014) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_title (6410723906019100189) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_message (2679412884290061775) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_title (6872152882604407837) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_message (7260183293747448241) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_title (6729801130790616200) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_message (7613960686747592770) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_title (8902518030404381318) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_message (4205117227342822275) --> + <skip /> + <!-- no translation found for activity_list_empty (4168820609403385789) --> + <skip /> + <!-- no translation found for permlab_pkgUsageStats (8787352074326748892) --> + <skip /> + <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> + <skip /> + <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> + <skip /> </resources> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index cb13390..447fccd 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -262,6 +262,10 @@ <string name="permdesc_reboot">"允许应用程序强制手机重新引导。"</string> <string name="permlab_mount_unmount_filesystems">"装载和卸载文件系统"</string> <string name="permdesc_mount_unmount_filesystems">"允许应用程序装载和卸载文件系统以进行可移动存储。"</string> + <!-- no translation found for permlab_mount_format_filesystems (5523285143576718981) --> + <skip /> + <!-- no translation found for permdesc_mount_format_filesystems (574060044906047386) --> + <skip /> <string name="permlab_vibrate">"控制振动器"</string> <string name="permdesc_vibrate">"允许应用程序控制振动器。"</string> <string name="permlab_flashlight">"控制闪光灯"</string> @@ -276,6 +280,10 @@ <string name="permdesc_locationUpdates">"允许启用/禁用来自收音机的位置更新通知。普通应用程序不能使用此权限。"</string> <string name="permlab_checkinProperties">"访问检入属性"</string> <string name="permdesc_checkinProperties">"允许对检入服务上传的属性进行读/写访问。普通应用程序不能使用此权限。"</string> + <!-- no translation found for permlab_bindGadget (2519859363977647275) --> + <skip /> + <!-- no translation found for permdesc_bindGadget (7865866514555126333) --> + <skip /> <string name="permlab_modifyPhoneState">"修改手机状态"</string> <string name="permdesc_modifyPhoneState">"允许应用程序控制设备的手机功能。具有此权限的应用程序可能会切换网络,打开和关闭手机收音机以及类似操作,而不会通知您。"</string> <string name="permlab_readPhoneState">"读取手机状态"</string> @@ -304,6 +312,10 @@ <string name="permdesc_writeApnSettings">"允许应用程序修改 APN 设置,例如任何 APN 的代理和端口。"</string> <string name="permlab_changeNetworkState">"更改网络连接性"</string> <string name="permdesc_changeNetworkState">"允许应用程序更改状态网络连接性。"</string> + <!-- no translation found for permlab_changeBackgroundDataSetting (1400666012671648741) --> + <skip /> + <!-- no translation found for permdesc_changeBackgroundDataSetting (1001482853266638864) --> + <skip /> <string name="permlab_accessWifiState">"查看 Wi-Fi 状态"</string> <string name="permdesc_accessWifiState">"允许应用程序查看有关 Wi-Fi 状态的信息。"</string> <string name="permlab_changeWifiState">"更改 Wi-Fi 状态"</string> @@ -546,12 +558,12 @@ <!-- no translation found for relative_time (1818557177829411417) --> <skip /> <string name="time_wday">"<xliff:g id="WEEKDAY">%2$s</xliff:g> <xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string> - <string name="full_date_month_first">"<xliff:g id="YEAR">yyyy</xliff:g> 年 <xliff:g id="MONTH">MMMM</xliff:g> 月 <xliff:g id="DAY">dd</xliff:g> 日"</string> - <string name="full_date_day_first">"<xliff:g id="YEAR">yyyy</xliff:g> 年 <xliff:g id="MONTH">MMMM</xliff:g> 月 <xliff:g id="DAY">dd</xliff:g> 日"</string> - <string name="medium_date_month_first">"<xliff:g id="YEAR">yyyy</xliff:g> 年 <xliff:g id="MONTH">MMM</xliff:g> 月 <xliff:g id="DAY">dd</xliff:g> 日"</string> - <string name="medium_date_day_first">"<xliff:g id="YEAR">yyyy</xliff:g> 年 <xliff:g id="DAY">dd</xliff:g> 月 <xliff:g id="MONTH">MMM</xliff:g> 日"</string> - <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g>"</string> + <string name="full_date_month_first">"<xliff:g id="YEAR">yyyy</xliff:g>' 年 '<xliff:g id="MONTH">MMMM</xliff:g>' 月 '<xliff:g id="DAY">d</xliff:g>' 日'"</string> + <string name="full_date_day_first">"<xliff:g id="YEAR">yyyy</xliff:g>' 年 '<xliff:g id="MONTH">MMMM</xliff:g>' 月 '<xliff:g id="DAY">d</xliff:g>' 日'"</string> + <string name="medium_date_month_first">"<xliff:g id="YEAR">yyyy</xliff:g>' 年 '<xliff:g id="MONTH">MMM</xliff:g>' 月 '<xliff:g id="DAY">d</xliff:g>' 日'"</string> + <string name="medium_date_day_first">"<xliff:g id="YEAR">yyyy</xliff:g>' 年 '<xliff:g id="DAY">d</xliff:g>' 月 '<xliff:g id="MONTH">MMM</xliff:g>' 日'"</string> + <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> + <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"中午"</string> <string name="Noon">"中午"</string> <string name="midnight">"午夜"</string> @@ -679,6 +691,8 @@ <string name="paste">"粘贴"</string> <string name="copyUrl">"复制网址"</string> <string name="inputMethod">"输入方法"</string> + <!-- no translation found for addToDictionary (726256909274177272) --> + <skip /> <string name="editTextMenuTitle">"编辑文本"</string> <string name="low_internal_storage_view_title">"存储空间不足"</string> <string name="low_internal_storage_view_text">"手机存储空间在减少。"</string> @@ -686,6 +700,8 @@ <string name="cancel">"取消"</string> <string name="yes">"正常"</string> <string name="no">"取消"</string> + <!-- no translation found for dialog_alert_title (2049658708609043103) --> + <skip /> <string name="capital_on">"开启"</string> <string name="capital_off">"关闭"</string> <string name="whichApplication">"使用以下内容完成操作"</string> @@ -709,7 +725,8 @@ <string name="volume_music">"媒体音量"</string> <string name="volume_music_hint_playing_through_bluetooth">"正通过蓝牙播放"</string> <string name="volume_call">"来电音量"</string> - <string name="volume_call_hint_playing_through_bluetooth">"正通过蓝牙播放"</string> + <!-- no translation found for volume_bluetooth_call (2002891926351151534) --> + <skip /> <string name="volume_alarm">"警告音量"</string> <string name="volume_notification">"通知音量"</string> <string name="volume_unknown">"音量"</string> @@ -745,8 +762,61 @@ <string name="usb_storage_error_message">"使用 SD 卡进行 USB 存储时出现问题。"</string> <string name="usb_storage_notification_title">"USB 已连接"</string> <string name="usb_storage_notification_message">"选择以将文件复制到计算机或从计算机复制文件。"</string> + <!-- no translation found for usb_storage_stop_notification_title (2336058396663516017) --> + <skip /> + <!-- no translation found for usb_storage_stop_notification_message (2591813490269841539) --> + <skip /> + <!-- no translation found for usb_storage_stop_title (6014127947456185321) --> + <skip /> + <!-- no translation found for usb_storage_stop_message (2390958966725232848) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_mount (1181858854166273345) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_unmount (3774611918660582898) --> + <skip /> + <!-- no translation found for usb_storage_stop_error_message (3917317248440072084) --> + <skip /> + <!-- no translation found for extmedia_format_title (8663247929551095854) --> + <skip /> + <!-- no translation found for extmedia_format_message (3621369962433523619) --> + <skip /> + <!-- no translation found for extmedia_format_button_format (4131064560127478695) --> + <skip /> <string name="select_input_method">"选择输入方法"</string> <string name="fast_scroll_alphabet">"ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> <string name="fast_scroll_numeric_alphabet">"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> - <string name="candidates_style"><font fgcolor="#ff000000" bgcolor="#ff8080ff"><u>"候选人"</u>"u>"</font></string> + <!-- no translation found for candidates_style (4333913089637062257) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_title (5457603418970994050) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_message (4747432538578886744) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_title (780477838241212997) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_message (1312266820092958014) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_title (6410723906019100189) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_message (2679412884290061775) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_title (6872152882604407837) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_message (7260183293747448241) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_title (6729801130790616200) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_message (7613960686747592770) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_title (8902518030404381318) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_message (4205117227342822275) --> + <skip /> + <!-- no translation found for activity_list_empty (4168820609403385789) --> + <skip /> + <!-- no translation found for permlab_pkgUsageStats (8787352074326748892) --> + <skip /> + <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> + <skip /> + <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> + <skip /> </resources> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 758bf82..e97c142 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -262,6 +262,10 @@ <string name="permdesc_reboot">"允許應用程式強制重開機。"</string> <string name="permlab_mount_unmount_filesystems">"掛載/卸載檔案系統"</string> <string name="permdesc_mount_unmount_filesystems">"允許應用程式掛載/卸載抽取式儲存設備的檔案系統。"</string> + <!-- no translation found for permlab_mount_format_filesystems (5523285143576718981) --> + <skip /> + <!-- no translation found for permdesc_mount_format_filesystems (574060044906047386) --> + <skip /> <string name="permlab_vibrate">"控制震動器"</string> <string name="permdesc_vibrate">"允許應用程式控制震動器。"</string> <string name="permlab_flashlight">"控制閃光燈"</string> @@ -276,6 +280,10 @@ <string name="permdesc_locationUpdates">"允許啟用/停用無線通訊位置更新通知。一般應用程式不會使用此功能。"</string> <string name="permlab_checkinProperties">"存取登機選項"</string> <string name="permdesc_checkinProperties">"允許讀寫登機服務上傳的資料。一般應用程式不會使用此功能。"</string> + <!-- no translation found for permlab_bindGadget (2519859363977647275) --> + <skip /> + <!-- no translation found for permdesc_bindGadget (7865866514555126333) --> + <skip /> <string name="permlab_modifyPhoneState">"修改手機狀態"</string> <string name="permdesc_modifyPhoneState">"允許應用程式控制電話功能。擁有此權限的程式可自行切換網路、開關無線通訊功能。"</string> <string name="permlab_readPhoneState">"讀取手機狀態"</string> @@ -304,6 +312,10 @@ <string name="permdesc_writeApnSettings">"允許應用程式修改 APN 設定,例如:Proxy 及 APN 的連接埠。"</string> <string name="permlab_changeNetworkState">"變更網路連線"</string> <string name="permdesc_changeNetworkState">"允許應用程式變更網路連線狀態。"</string> + <!-- no translation found for permlab_changeBackgroundDataSetting (1400666012671648741) --> + <skip /> + <!-- no translation found for permdesc_changeBackgroundDataSetting (1001482853266638864) --> + <skip /> <string name="permlab_accessWifiState">"檢視 Wi-Fi 狀態"</string> <string name="permdesc_accessWifiState">"允許應用程式檢視 Wi-Fi 狀態資訊。"</string> <string name="permlab_changeWifiState">"變更 Wi-Fi 狀態"</string> @@ -546,12 +558,12 @@ <!-- no translation found for relative_time (1818557177829411417) --> <skip /> <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>,<xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first">"<xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="DAY">dd</xliff:g>,<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMMM</xliff:g>,<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first">"<xliff:g id="MONTH">MMM</xliff:g> <xliff:g id="DAY">dd</xliff:g>,<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMM</xliff:g>,<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g>"</string> + <string name="full_date_month_first">"<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>','<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="full_date_day_first">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>','<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_month_first">"<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>','<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_day_first">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>','<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> + <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"中午"</string> <string name="Noon">"中午"</string> <string name="midnight">"午夜"</string> @@ -679,6 +691,8 @@ <string name="paste">"貼上"</string> <string name="copyUrl">"複製網址"</string> <string name="inputMethod">"輸入法"</string> + <!-- no translation found for addToDictionary (726256909274177272) --> + <skip /> <string name="editTextMenuTitle">"編輯文字"</string> <string name="low_internal_storage_view_title">"儲存空間太少"</string> <string name="low_internal_storage_view_text">"手機儲存空間即將不足。"</string> @@ -686,6 +700,8 @@ <string name="cancel">"取消"</string> <string name="yes">"確定"</string> <string name="no">"取消"</string> + <!-- no translation found for dialog_alert_title (2049658708609043103) --> + <skip /> <string name="capital_on">"開啟"</string> <string name="capital_off">"關閉"</string> <string name="whichApplication">"選取...完成動作"</string> @@ -709,7 +725,8 @@ <string name="volume_music">"媒體音量"</string> <string name="volume_music_hint_playing_through_bluetooth">"透過藍牙播放"</string> <string name="volume_call">"來電音量"</string> - <string name="volume_call_hint_playing_through_bluetooth">"透過藍牙播放"</string> + <!-- no translation found for volume_bluetooth_call (2002891926351151534) --> + <skip /> <string name="volume_alarm">"鬧鐘音量"</string> <string name="volume_notification">"通知音量"</string> <string name="volume_unknown">"音量"</string> @@ -745,8 +762,61 @@ <string name="usb_storage_error_message">"把 SD 卡當成 USB 儲存裝置時發生問題。"</string> <string name="usb_storage_notification_title">"USB 已連接"</string> <string name="usb_storage_notification_message">"選取此項將檔案複製到電腦,或從電腦複製。"</string> + <!-- no translation found for usb_storage_stop_notification_title (2336058396663516017) --> + <skip /> + <!-- no translation found for usb_storage_stop_notification_message (2591813490269841539) --> + <skip /> + <!-- no translation found for usb_storage_stop_title (6014127947456185321) --> + <skip /> + <!-- no translation found for usb_storage_stop_message (2390958966725232848) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_mount (1181858854166273345) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_unmount (3774611918660582898) --> + <skip /> + <!-- no translation found for usb_storage_stop_error_message (3917317248440072084) --> + <skip /> + <!-- no translation found for extmedia_format_title (8663247929551095854) --> + <skip /> + <!-- no translation found for extmedia_format_message (3621369962433523619) --> + <skip /> + <!-- no translation found for extmedia_format_button_format (4131064560127478695) --> + <skip /> <string name="select_input_method">"選取輸入法"</string> <string name="fast_scroll_alphabet">"ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> <string name="fast_scroll_numeric_alphabet">"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> - <string name="candidates_style"><font fgcolor="#ff000000" bgcolor="#ff8080ff"><u>"candidates"</u>"u>"</font></string> + <!-- no translation found for candidates_style (4333913089637062257) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_title (5457603418970994050) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_message (4747432538578886744) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_title (780477838241212997) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_message (1312266820092958014) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_title (6410723906019100189) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_message (2679412884290061775) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_title (6872152882604407837) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_message (7260183293747448241) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_title (6729801130790616200) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_message (7613960686747592770) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_title (8902518030404381318) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_message (4205117227342822275) --> + <skip /> + <!-- no translation found for activity_list_empty (4168820609403385789) --> + <skip /> + <!-- no translation found for permlab_pkgUsageStats (8787352074326748892) --> + <skip /> + <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> + <skip /> + <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> + <skip /> </resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 5477538..3f21303 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -261,6 +261,14 @@ actual instance is shown to the user. --> <attr name="windowDisablePreview" format="boolean" /> + <!-- Flag indicating that this window should not be displayed at all. + The default value is false; if set to true, and this window is + the main window of an Activity, then it will never actually + be added to the window manager. This means that your activity + must immediately quit without waiting for user interaction, + because there will be no such interaction coming. --> + <attr name="windowNoDisplay" format="boolean" /> + <!-- ============ --> <!-- Alert Dialog styles --> <!-- ============ --> @@ -336,6 +344,8 @@ <attr name="radioButtonStyle" format="reference" /> <!-- Default ScrollView style. --> <attr name="scrollViewStyle" format="reference" /> + <!-- Default HorizontalScrollView style. --> + <attr name="horizontalScrollViewStyle" format="reference" /> <!-- Default Spinner style. --> <attr name="spinnerStyle" format="reference" /> <!-- Default Star style. --> @@ -466,13 +476,19 @@ {@link android.text.InputType#TYPE_TEXT_FLAG_AUTO_COMPLETE}. --> <flag name="textAutoComplete" value="0x00010001" /> <!-- Can be combined with <var>text</var> and its variations to - allow multiple lines of text in the field. Corresponds to + allow multiple lines of text in the field. If this flag is not set, + the text field will be constrained to a single line. Corresponds to {@link android.text.InputType#TYPE_TEXT_FLAG_MULTI_LINE}. --> <flag name="textMultiLine" value="0x00020001" /> <!-- Can be combined with <var>text</var> and its variations to + indicate that though the regular text view should not be multiple + lines, and IME should provide multiple lines if it can. Corresponds to + {@link android.text.InputType#TYPE_TEXT_FLAG_IME_MULTI_LINE}. --> + <flag name="textImeMultiLine" value="0x00040001" /> + <!-- Can be combined with <var>text</var> and its variations to indicate that a search string is being entered. Corresponds to {@link android.text.InputType#TYPE_TEXT_FLAG_SEARCH}. --> - <flag name="textSearch" value="0x00040001" /> + <flag name="textSearch" value="0x00080001" /> <!-- Text that will be used as a URI. Corresponds to {@link android.text.InputType#TYPE_CLASS_TEXT} | {@link android.text.InputType#TYPE_TEXT_VARIATION_URI}. --> @@ -485,27 +501,26 @@ {@link android.text.InputType#TYPE_CLASS_TEXT} | {@link android.text.InputType#TYPE_TEXT_VARIATION_EMAIL_SUBJECT}. --> <flag name="textEmailSubject" value="0x00000031" /> - <!-- Text that is being supplied as the content of an e-mail. Corresponds to + <!-- Text that is the content of a short message. Corresponds to {@link android.text.InputType#TYPE_CLASS_TEXT} | - {@link android.text.InputType#TYPE_TEXT_VARIATION_EMAIL_CONTENT}. --> - <flag name="textEmailContent" value="0x00000041" /> + {@link android.text.InputType#TYPE_TEXT_VARIATION_SHORT_MESSAGE}. --> + <flag name="textShortMessage" value="0x00000041" /> + <!-- Text that is the content of a long message. Corresponds to + {@link android.text.InputType#TYPE_CLASS_TEXT} | + {@link android.text.InputType#TYPE_TEXT_VARIATION_LONG_MESSAGE}. --> + <flag name="textLongMessage" value="0x00000051" /> <!-- Text that is the name of a person. Corresponds to {@link android.text.InputType#TYPE_CLASS_TEXT} | {@link android.text.InputType#TYPE_TEXT_VARIATION_PERSON_NAME}. --> - <flag name="textPersonName" value="0x00000051" /> + <flag name="textPersonName" value="0x00000061" /> <!-- Text that is being supplied as a postal mailing address. Corresponds to {@link android.text.InputType#TYPE_CLASS_TEXT} | {@link android.text.InputType#TYPE_TEXT_VARIATION_POSTAL_ADDRESS}. --> - <flag name="textPostalAddress" value="0x00000061" /> + <flag name="textPostalAddress" value="0x00000071" /> <!-- Text that is a password. Corresponds to {@link android.text.InputType#TYPE_CLASS_TEXT} | {@link android.text.InputType#TYPE_TEXT_VARIATION_PASSWORD}. --> - <flag name="textPassword" value="0x00000071" /> - <!-- Text that will be used for free form search strings. Corresponds to - {@link android.text.InputType#TYPE_CLASS_TEXT} | - {@link android.text.InputType#TYPE_TEXT_VARIATION_SEARCH_STRING} | - {@link android.text.InputType#TYPE_TEXT_FLAG_SEARCH}. --> - <flag name="textSearchString" value="0x00040081" /> + <flag name="textPassword" value="0x00000081" /> <!-- Text that is being supplied as text in a web form. Corresponds to {@link android.text.InputType#TYPE_CLASS_TEXT} | {@link android.text.InputType#TYPE_TEXT_VARIATION_WEB_EDIT_TEXT}. --> @@ -767,6 +782,7 @@ <attr name="windowAnimationStyle" /> <attr name="windowSoftInputMode" /> <attr name="windowDisablePreview" /> + <attr name="windowNoDisplay" /> <attr name="textColor" /> <attr name="backgroundDimEnabled" /> <attr name="backgroundDimAmount" /> @@ -1673,9 +1689,15 @@ instead of letting it wrap onto multiple lines, and advances focus instead of inserting a newline when you press the enter key. Note: for editable text views, it is better - to set this using the textMultiLine flag in inputType; - if both this and inputType are supplied, the input - type overrides the value here. --> + to control this using the textMultiLine flag in the inputType + attribute. (If both singleLine and inputType are supplied, + the inputType flags will override the value of singleLine.) + {@deprecated This attribute is deprecated and is replaced by the textMultiLine flag + in the inputType attribute. Use caution when altering existing layouts, as the + default value of singeLine is false (multi-line mode), but if you specify any + value for inputType, the default is single-line mode. (If both singleLine and + inputType attributes are found, the inputType flags will override the value of + singleLine.) } --> <attr name="singleLine" format="boolean" /> <!-- {@deprecated Use state_enabled instead.} --> <attr name="enabled" format="boolean" /> @@ -1839,6 +1861,10 @@ <!-- Defines whether the scrollview should stretch its content to fill the viewport. --> <attr name="fillViewport" format="boolean" /> </declare-styleable> + <declare-styleable name="HorizontalScrollView"> + <!-- Defines whether the scrollview should stretch its content to fill the viewport. --> + <attr name="fillViewport" /> + </declare-styleable> <declare-styleable name="Spinner"> <!-- The prompt to display when the spinner's dialog is shown. --> <attr name="prompt" format="reference" /> @@ -1936,10 +1962,10 @@ Accommodates top margin. --> <attr name="layout_alignParentTop" format="boolean" /> <!-- If true, makes the right edge of this view match the right edge of the parent. - Accommodates top margin. --> + Accommodates right margin. --> <attr name="layout_alignParentRight" format="boolean" /> - <!-- f true, makes the bottom edge of this view match the bottom edge of the parent. - Accommodates top margin. --> + <!-- If true, makes the bottom edge of this view match the bottom edge of the parent. + Accommodates bottom margin. --> <attr name="layout_alignParentBottom" format="boolean" /> <!-- If true, centers this child horizontally and vertically within its parent. --> <attr name="layout_centerInParent" format="boolean" /> @@ -2513,6 +2539,43 @@ values are not suitable for user inspection and editing. --> <flag name="queryRewriteFromText" value="0x20" /> </attr> + + <!-- Voice search features are controlled by mode bits in this field. Omitting + this field, or setting to zero, provides default behavior. + If showVoiceSearchButton is set, then launchWebSearch or launchRecognizer must + also be set. <i>Optional attribute.</i> + --> + <attr name="voiceSearchMode"> + <!-- If set, display a voice search button. This only takes effect if voice search is + available on the device. --> + <flag name="showVoiceSearchButton" value="0x01" /> + <!-- If set, the voice search button will take the user directly to a built-in + voice web search activity. Most applications will not use this flag, as it + will take the user away from your searchable activity. --> + <flag name="launchWebSearch" value="0x02" /> + <!-- If set, the voice search button will take the user directly to a built-in + voice recording activity. This activity will prompt the user to speak, + transcribe the spoke text, and forward the resulting query text to your + searchable activity, just as if the user had typed it into the search UI. --> + <flag name="launchRecognizer" value="0x04" /> + </attr> + + <!-- If provided, this specifies the language model that should be used by the + voice recognition system. See + {@link android.speech.RecognizerIntent#EXTRA_LANGUAGE_MODEL } for more information. + If not provided, the default value + {@link android.speech.RecognizerIntent#LANGUAGE_MODEL_FREE_FORM } will be used. --> + <attr name="voiceLanguageModel" format="string" /> + <!-- If provided, this specifies a prompt that will be displayed during voice input. --> + <attr name="voicePromptText" format="string" /> + <!-- If provided, this specifies the spoken language to be expected, and that it will be + different than the one set in the {@link java.util.Locale#getDefault()}. --> + <attr name="voiceLanguage" format="string" /> + <!-- If provided, enforces the maximum number of results to return, including the "best" + result which will always be provided as the SEARCH intent's primary query. Must be one + or greater. If not provided, the recognizer will choose how many results to return. + --> + <attr name="voiceMaxResults" format="integer" /> <!-- If provided, this is the trigger indicating that the searchable activity provides suggestions as well. The value must be a fully-qualified content provider @@ -2948,6 +3011,7 @@ <attr name="minHeight"/> <attr name="updatePeriodMillis" format="integer" /> <attr name="initialLayout" format="reference" /> + <attr name="configure" format="string" /> </declare-styleable> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index e215141..f717196 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -87,19 +87,19 @@ <!-- Specify a permission that a client is required to have in order to use the associated object. If the client does not hold the named permission, its request will fail. See the - <a href="{@docRoot}devel/security.html">Security Model</a> + <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> document for more information on permissions. --> <attr name="permission" format="string" /> <!-- A specific {@link android.R.attr#permission} name for read-only access to a {@link android.content.ContentProvider}. See the - <a href="{@docRoot}devel/security.html">Security Model</a> + <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> document for more information on permissions. --> <attr name="readPermission" format="string" /> <!-- A specific {@link android.R.attr#permission} name for write access to a {@link android.content.ContentProvider}. See the - <a href="{@docRoot}devel/security.html">Security Model</a> + <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> document for more information on permissions. --> <attr name="writePermission" format="string" /> @@ -376,7 +376,7 @@ <attr name="priority" format="integer" /> <!-- Specify how an activity should be launched. See the - <a href="{@docRoot}intro/appmodel.html">Application Model</a> + <a href="{@docRoot}guide/topics/fundamentals.html#acttask">Application Fundamentals</a> documentation for important information on how these options impact the behavior of your application. @@ -413,7 +413,7 @@ of the activity being started at the top of the stack, it will receive the Intent as described there (without the FLAG_ACTIVITY_BROUGHT_TO_FRONT flag set). See the - <a href="{@docRoot}intro/appmodel.html">Application Model</a> + <a href="{@docRoot}guide/topics/fundamentals.html#acttask">Application Fundamentals</a> documentation for more details on tasks.--> <enum name="singleTask" value="2" /> <!-- Only allow one instance of this activity to ever be @@ -424,7 +424,7 @@ method called. If this activity tries to start a new activity, that new activity will be launched in a separate task. See the - <a href="{@docRoot}intro/appmodel.html">Application Model</a> + <a href="{@docRoot}guide/topics/fundamentals.html#acttask">Application Fundamentals</a> documentation for more details on tasks. --> <enum name="singleInstance" value="3" /> </attr> @@ -633,7 +633,7 @@ <!-- The <code>permission</code> tag declares a security permission that can be used to control access from other packages to specific components or features in your package (or other packages). See the - <a href="{@docRoot}devel/security.html">Security Model</a> + <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> document for more information on permissions. <p>This appears as a child tag of the root @@ -705,7 +705,7 @@ <!-- The <code>uses-permission</code> tag requests a {@link #AndroidManifestPermission <permission>} that the containing package must be granted in order for it to operate correctly. - See the <a href="{@docRoot}devel/security.html">Security Model</a> + See the <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> document for more information on permissions. Also available is a {@link android.Manifest.permission list of permissions} included with the base platform. diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 59f3a8f..3fb7e86 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -19,7 +19,7 @@ --> <resources> <drawable name="screen_background_light">#ffffffff</drawable> - <drawable name="screen_background_dark">#ff191919</drawable> + <drawable name="screen_background_dark">#ff262626</drawable> <drawable name="status_bar_closed_default_background">#ff000000</drawable> <drawable name="status_bar_opened_default_background">#ff000000</drawable> <drawable name="search_bar_default_color">#ff000000</drawable> @@ -28,7 +28,7 @@ <color name="white">#ffffffff</color> <color name="black">#ff000000</color> <color name="transparent">#00000000</color> - <color name="background_dark">#ff191919</color> + <color name="background_dark">#ff262626</color> <color name="bright_foreground_dark">#ffffffff</color> <color name="bright_foreground_dark_disabled">#80ffffff</color> <color name="bright_foreground_dark_inverse">#ff000000</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml new file mode 100644 index 0000000..f370151 --- /dev/null +++ b/core/res/res/values/config.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 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. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources> + <!-- Flag indicating whether the surface flinger has limited + alpha compositing functionality in hardware. If set, the window + manager will disable alpha trasformation in animations where not + strictly needed. --> + <bool name="config_sf_limitedAlpha">false</bool> +</resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 2abb26f..6461460 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -26,4 +26,6 @@ will be displayed in the app launcher and elsewhere. --> <dimen name="app_icon_size">48dip</dimen> <dimen name="toast_y_offset">64dip</dimen> + <!-- Height of the status bar --> + <dimen name="status_bar_height">25dip</dimen> </resources> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index c5dbfd8..7e9b7ea 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -59,9 +59,10 @@ <item type="id" name="copy" /> <item type="id" name="paste" /> <item type="id" name="copyUrl" /> - <item type="id" name="inputMethod" /> + <item type="id" name="switchInputMethod" /> <item type="id" name="keyboardView" /> <item type="id" name="button_close" /> - <item type="id" name="selectText" /> + <item type="id" name="startSelectingText" /> <item type="id" name="stopSelectingText" /> + <item type="id" name="addToDictionary" /> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index c757c56..1031585 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -937,6 +937,7 @@ =============================================================== --> <eat-comment /> + <public type="attr" name="windowNoDisplay" id="0x0101021e" /> <public type="attr" name="backgroundDimEnabled" id="0x0101021f" /> <public type="attr" name="inputType" id="0x01010220" /> <public type="attr" name="isDefault" id="0x01010221" /> @@ -964,30 +965,42 @@ <public type="attr" name="keyPreviewLayout" id="0x01010237" /> <public type="attr" name="keyPreviewOffset" id="0x01010238" /> <public type="attr" name="keyPreviewHeight" id="0x01010239" /> - <public type="attr" name="verticalCorrection" id="0x01010240" /> - <public type="attr" name="popupLayout" id="0x01010241" /> - <public type="attr" name="state_long_pressable" id="0x01010242" /> - <public type="attr" name="keyWidth" id="0x01010243" /> - <public type="attr" name="keyHeight" id="0x01010244" /> - <public type="attr" name="horizontalGap" id="0x01010245" /> - <public type="attr" name="verticalGap" id="0x01010246" /> - <public type="attr" name="rowEdgeFlags" id="0x01010247" /> - <public type="attr" name="codes" id="0x01010248" /> - <public type="attr" name="popupKeyboard" id="0x01010249" /> - <public type="attr" name="popupCharacters" id="0x0101024a" /> - <public type="attr" name="keyEdgeFlags" id="0x0101024b" /> - <public type="attr" name="isModifier" id="0x0101024c" /> - <public type="attr" name="isSticky" id="0x0101024d" /> - <public type="attr" name="isRepeatable" id="0x0101024e" /> - <public type="attr" name="iconPreview" id="0x0101024f" /> - <public type="attr" name="keyOutputText" id="0x01010250" /> - <public type="attr" name="keyLabel" id="0x01010251" /> - <public type="attr" name="keyIcon" id="0x01010252" /> - <public type="attr" name="keyboardMode" id="0x01010253" /> - <public type="attr" name="isScrollContainer" id="0x01010254" /> - <public type="attr" name="fillEnabled" id="0x01010255" /> - <public type="attr" name="updatePeriodMillis" id="0x01010256" /> - <public type="attr" name="initialLayout" id="0x01010257" /> + <public type="attr" name="verticalCorrection" id="0x0101023a" /> + <public type="attr" name="popupLayout" id="0x0101023b" /> + <public type="attr" name="state_long_pressable" id="0x0101023c" /> + <public type="attr" name="keyWidth" id="0x0101023d" /> + <public type="attr" name="keyHeight" id="0x0101023e" /> + <public type="attr" name="horizontalGap" id="0x0101023f" /> + <public type="attr" name="verticalGap" id="0x01010240" /> + <public type="attr" name="rowEdgeFlags" id="0x01010241" /> + <public type="attr" name="codes" id="0x01010242" /> + <public type="attr" name="popupKeyboard" id="0x01010243" /> + <public type="attr" name="popupCharacters" id="0x01010244" /> + <public type="attr" name="keyEdgeFlags" id="0x01010245" /> + <public type="attr" name="isModifier" id="0x01010246" /> + <public type="attr" name="isSticky" id="0x01010247" /> + <public type="attr" name="isRepeatable" id="0x01010248" /> + <public type="attr" name="iconPreview" id="0x01010249" /> + <public type="attr" name="keyOutputText" id="0x0101024a" /> + <public type="attr" name="keyLabel" id="0x0101024b" /> + <public type="attr" name="keyIcon" id="0x0101024c" /> + <public type="attr" name="keyboardMode" id="0x0101024d" /> + <public type="attr" name="isScrollContainer" id="0x0101024e" /> + <public type="attr" name="fillEnabled" id="0x0101024f" /> + <public type="attr" name="updatePeriodMillis" id="0x01010250" /> + <public type="attr" name="initialLayout" id="0x01010251" /> + <public type="attr" name="voiceSearchMode" id="0x01010252" /> + <public type="attr" name="voiceLanguageModel" id="0x01010253" /> + <public type="attr" name="voicePromptText" id="0x01010254" /> + <public type="attr" name="voiceLanguage" id="0x01010255" /> + <public type="attr" name="voiceMaxResults" id="0x01010256" /> + <public type="attr" name="bottomOffset" id="0x01010257" /> + <public type="attr" name="topOffset" id="0x01010258" /> + <public type="attr" name="allowSingleTap" id="0x01010259" /> + <public type="attr" name="handle" id="0x0101025a" /> + <public type="attr" name="content" id="0x0101025b" /> + <public type="attr" name="animateOnClick" id="0x0101025c" /> + <public type="attr" name="configure" id="0x0101025d" /> <!-- The part of the UI shown by an {@link android.inputmethodservice.InputMethodService} that contains the @@ -1022,7 +1035,7 @@ <!-- Context menu ID for the "Input Method" menu item to being up the input method picker dialog, allowing the user to switch to another input method. --> - <public type="id" name="inputMethod" id="0x01020024" /> + <public type="id" name="switchInputMethod" id="0x01020024" /> <!-- View ID of the text editor inside of an extracted text layout. --> <public type="id" name="inputExtractEditText" id="0x01020025" /> @@ -1032,10 +1045,20 @@ <!-- View ID of a {@link android.view.View} to close a popup keyboard --> <public type="id" name="button_close" id="0x01020027" /> + <!-- Menu ID to perform a "start selecting text" operation. --> + <public type="id" name="startSelectingText" id="0x01020028" /> + <!-- Menu ID to perform a "stop selecting text" operation. --> + <public type="id" name="stopSelectingText" id="0x01020029" /> + <!-- Menu ID to perform a "add to dictionary" operation. --> + <public type="id" name="addToDictionary" id="0x0102002a" /> + <public type="style" name="Theme.InputMethod" id="0x01030054" /> <public type="style" name="Theme.NoDisplay" id="0x01030055" /> <public type="style" name="Animation.InputMethod" id="0x01030056" /> <public type="style" name="Widget.KeyboardView" id="0x01030057" /> + <public type="style" name="ButtonBar" id="0x01030058" /> + + <public type="string" name="dialog_alert_title" id="0x01040014" /> <public type="drawable" name="emo_im_angel" id="0x010800a4" /> <public type="drawable" name="emo_im_cool" id="0x010800a5" /> @@ -1054,4 +1077,12 @@ <public type="drawable" name="emo_im_winking" id="0x010800b2" /> <public type="drawable" name="emo_im_wtf" id="0x010800b3" /> <public type="drawable" name="emo_im_yelling" id="0x010800b4" /> + + <public type="drawable" name="ic_btn_speak_now" id="0x010800b5" /> + + <!-- Drawable to use as a background for separators on a list with a dark background --> + <public type="drawable" name="dark_header" id="0x010800b6" /> + + <!-- Drawable to use as a background for a taller version of the titlebar --> + <public type="drawable" name="title_bar_tall" id="0x010800b7" /> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 978a024..4700b93 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -778,6 +778,11 @@ unmount filesystems for removable storage.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_mount_format_filesystems">format external storage</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_mount_format_filesystems">Allows the application to format removable storage.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_vibrate">control vibrator</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_vibrate">Allows the application to control @@ -824,6 +829,15 @@ properties uploaded by the checkin service. Not for use by normal applications.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_bindGadget">choose gadgets</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_bindGadget">Allows the application to tell the system + which gadgets can be used by which application. With this permission, + applications can give access to personal data to other applications. + Not for use by normal applications.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_modifyPhoneState">modify phone state</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> @@ -915,6 +929,12 @@ the state network connectivity.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_changeBackgroundDataSetting">change background data usage setting</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_changeBackgroundDataSetting">Allows an application to change + the background data usage setting.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_accessWifiState">view Wi-Fi state</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_accessWifiState">Allows an application to view @@ -1502,50 +1522,38 @@ <!-- Date format string used in contexts where the user has said they want the month first, as used in the USA, with the month fully spelled out. You can remove the comma or add a period, - or make other punctuation changes appropriate for your locale. - If you need to add letters, put apostrophes around them to keep - them from being interpreted as format characters. --> - <string name="full_date_month_first"><xliff:g id="month" example="December">MMMM</xliff:g> <xliff:g id="day" example="31">dd</xliff:g>, <xliff:g id="year" example="1972">yyyy</xliff:g></string> + or make other punctuation changes appropriate for your locale. --> + <string name="full_date_month_first" format="date"><xliff:g id="month" example="December">MMMM</xliff:g> <xliff:g id="day" example="31">d</xliff:g>, <xliff:g id="year" example="1972">yyyy</xliff:g></string> <!-- Date format string used in contexts where the user has said they want the day of the month first, as used in Europe, with the month fully spelled out. You can remove the comma or add a period, - or make other punctuation changes appropriate for your locale. - If you need to add letters, put apostrophes around them to keep - them from being interpreted as format characters. --> - <string name="full_date_day_first"><xliff:g id="day" example="31">dd</xliff:g> <xliff:g id="month" example="December">MMMM</xliff:g>, <xliff:g id="year" example="1972">yyyy</xliff:g></string> + or make other punctuation changes appropriate for your locale. --> + <string name="full_date_day_first" format="date"><xliff:g id="day" example="31">d</xliff:g> <xliff:g id="month" example="December">MMMM</xliff:g>, <xliff:g id="year" example="1972">yyyy</xliff:g></string> <!-- Date format string used in contexts where the user has said they want the month first, as used in the USA, with the month abbreviated. You can remove the comma or add a period, - or make other punctuation changes appropriate for your locale. - If you need to add letters, put apostrophes around them to keep - them from being interpreted as format characters. --> - <string name="medium_date_month_first"><xliff:g id="month" example="Dec.">MMM</xliff:g> <xliff:g id="day" example="31">dd</xliff:g>, <xliff:g id="year" example="1972">yyyy</xliff:g></string> + or make other punctuation changes appropriate for your locale. --> + <string name="medium_date_month_first" format="date"><xliff:g id="month" example="Dec.">MMM</xliff:g> <xliff:g id="day" example="31">d</xliff:g>, <xliff:g id="year" example="1972">yyyy</xliff:g></string> <!-- Date format string used in contexts where the user has said they want the day of the month first, as used in Europe, with the month abbreviated. You can remove the comma or add a period, - or make other punctuation changes appropriate for your locale. - If you need to add letters, put apostrophes around them to keep - them from being interpreted as format characters. --> - <string name="medium_date_day_first"><xliff:g id="day" example="31">dd</xliff:g> <xliff:g id="month" example="December">MMM</xliff:g>, <xliff:g id="year" example="1972">yyyy</xliff:g></string> + or make other punctuation changes appropriate for your locale. --> + <string name="medium_date_day_first" format="date"><xliff:g id="day" example="31">d</xliff:g> <xliff:g id="month" example="December">MMM</xliff:g>, <xliff:g id="year" example="1972">yyyy</xliff:g></string> <!-- Time format string used in the status bar when the user has said they want a 12-hour clock with AM and PM. You can remove the colon - or make other punctuation changes appropriate for your locale. - If you need to add letters, put apostrophes around them to keep - them from being interpreted as format characters. --> - <string name="twelve_hour_time_format"><xliff:g id="hour" example="11">h</xliff:g>:<xliff:g id="minute" example="59">mm</xliff:g> <xliff:g id="ampm" example="AM">a</xliff:g></string> + or make other punctuation changes appropriate for your locale. --> + <string name="twelve_hour_time_format" format="date"><xliff:g id="hour" example="11">h</xliff:g>:<xliff:g id="minute" example="59">mm</xliff:g> <xliff:g id="ampm" example="AM">a</xliff:g></string> <!-- Time format string used in the status bar when the user has said they want a 24-hour clock. You can remove the colon - or make other punctuation changes appropriate for your locale. - If you need to add letters, put apostrophes around them to keep - them from being interpreted as format characters. --> - <string name="twenty_four_hour_time_format"><xliff:g id="hour" example="23">H</xliff:g>:<xliff:g id="minute" example="59">mm</xliff:g></string> + or make other punctuation changes appropriate for your locale. --> + <string name="twenty_four_hour_time_format" format="date"><xliff:g id="hour" example="23">H</xliff:g>:<xliff:g id="minute" example="59">mm</xliff:g></string> <!-- Quoted name for 12pm, lowercase --> <string name="noon">"noon"</string> @@ -2020,6 +2028,10 @@ <!-- EditText context menu --> <string name="inputMethod">Input Method</string> + <!-- Item on EditText context menu, used to add a word to the + input method dictionary. --> + <string name="addToDictionary">"Add \"%s\" to dictionary</string> + <!-- Title for EditText context menu --> <string name="editTextMenuTitle">Edit text</string> @@ -2036,6 +2048,9 @@ <string name="yes">OK</string> <!-- Preference framework strings. --> <string name="no">Cancel</string> + <!-- This is the generic "attention" string to be used in attention dialogs. Typically + combined with setIcon(android.R.drawable.ic_dialog_alert) --> + <string name="dialog_alert_title">Attention</string> <!-- Default text for a button that can be toggled on and off. --> <string name="capital_on">ON</string> @@ -2090,8 +2105,8 @@ <string name="volume_music_hint_playing_through_bluetooth">Playing through Bluetooth</string> <!-- Title of the dialog where the user is adjusting the phone call volume --> <string name="volume_call">In-call volume</string> - <!-- Hint shown in the volume toast to inform the user that the in-call audio is playing through Bluetooth. --> - <string name="volume_call_hint_playing_through_bluetooth">Playing through Bluetooth</string> + <!-- Title of the dialog where the user is adjusting the phone call volume when connected on bluetooth--> + <string name="volume_bluetooth_call">Bluetooth in-call volume</string> <!-- Title of the dialog where the user is adjusting the audio volume for alarms --> <string name="volume_alarm">Alarm volume</string> <!-- Title of the dialog where the user is adjusting the audio volume for notifications --> @@ -2165,12 +2180,38 @@ <string name="usb_storage_button_mount">Mount</string> <!-- See USB_STORAGE. This is the button text to ignore the plugging in of the phone.. --> <string name="usb_storage_button_unmount">Don\'t mount</string> - <!-- See USB_STORAGE_DIALOG. IF there was an error mounting, this is hte text. --> + <!-- See USB_STORAGE_DIALOG. If there was an error mounting, this is the text. --> <string name="usb_storage_error_message">There is a problem using your SD card for USB storage.</string> - <!-- USB_STORAGE: When the user connects the phone to a computer via USB, we show a notification asking if he wnats to share files across. This is the title --> + <!-- USB_STORAGE: When the user connects the phone to a computer via USB, we show a notification asking if he wants to share files across. This is the title --> <string name="usb_storage_notification_title">USB connected</string> <!-- See USB_STORAGE. This is the message. --> <string name="usb_storage_notification_message">Select to copy files to/from your computer.</string> + <!-- USB_STORAGE_STOP: While USB storage is enabled, we show a notification dialog asking if he wants to stop. This is the title --> + <string name="usb_storage_stop_notification_title">Turn off USB storage</string> + <!-- See USB_STORAGE. This is the message. --> + <string name="usb_storage_stop_notification_message">Select to turn off USB storage.</string> + + <!-- USB storage stop dialog strings --> + <!-- This is the label for the activity, and should never be visible to the user. --> + <!-- See USB_STORAGE_STOP. USB_STORAGE_STOP_DIALOG: After the user selects the notification, a dialog is shown asking if he wants to stop usb storage. This is the title. --> + <string name="usb_storage_stop_title">Turn off USB storage</string> + <!-- See USB_STORAGE_STOP. This is the message. --> + <string name="usb_storage_stop_message">Before turning off USB storage, make sure you have unmounted on the USB host. Select \"Turn Off\" to turn off USB storage.</string> + <!-- See USB_STORAGE_STOP. This is the button text to stop usb storage. --> + <string name="usb_storage_stop_button_mount">Turn Off</string> + <!-- See USB_STORAGE_STOP. This is the button text to cancel stoping usb storage. --> + <string name="usb_storage_stop_button_unmount">Cancel</string> + <!-- See USB_STORAGE_STOP_DIALOG. If there was an error stopping, this is the text. --> + <string name="usb_storage_stop_error_message">We've encountered a problem turning off USB storage. Check to make sure you have unmounted the USB host, then try again.</string> + + <!-- External media format dialog strings --> + <!-- This is the label for the activity, and should never be visible to the user. --> + <!-- See EXTMEDIA_FORMAT. EXTMEDIA_FORMAT_DIALOG: After the user selects the notification, a dialog is shown asking if he wants to format the SD card. This is the title. --> + <string name="extmedia_format_title">Format SD card</string> + <!-- See EXTMEDIA_FORMAT. This is the message. --> + <string name="extmedia_format_message">Are you sure you want to format the SD card? All data on your card will be lost.</string> + <!-- See EXTMEDIA_FORMAT. This is the button text to format the sd card. --> + <string name="extmedia_format_button_format">Format</string> <!-- Used to replace %s in urls retreived from the signin server with locales. For Some --> <!-- devices we don't support all the locales we ship to and need to replace the '%s' with a --> @@ -2185,8 +2226,45 @@ <string name="fast_scroll_alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string> <string name="fast_scroll_numeric_alphabet">\u00200123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ</string> - <string name="candidates_style"><font fgcolor="#ff000000" - bgcolor="#ff8080ff"><u>candidates</u>u></font></string> + <string name="candidates_style"><u>candidates</u></string> + + <!-- External media notification strings --> + <!-- Shown when external media is being checked --> + <string name="ext_media_checking_notification_title">Preparing SD card</string> + <string name="ext_media_checking_notification_message">Checking for errors</string> + + <!-- Shown when external media is blank (or unsupported filesystem) --> + <string name="ext_media_nofs_notification_title">Blank SD card</string> + <string name="ext_media_nofs_notification_message">The SD card is blank or using an unsupported filesystem.</string> + + <!-- Shown when external media is unmountable (corrupt)) --> + <string name="ext_media_unmountable_notification_title">Damaged SD card</string> + <string name="ext_media_unmountable_notification_message">The SD card is damaged. You may have to reformat your card.</string> + + <!-- Shown when external media is unsafely removed --> + <string name="ext_media_badremoval_notification_title">SD card unexpectedly removed</string> + <string name="ext_media_badremoval_notification_message">Unmount SD card before removing to avoid data loss.</string> + + <!-- Shown when external media has been safely removed --> + <string name="ext_media_safe_unmount_notification_title">SD card safe to remove</string> + <string name="ext_media_safe_unmount_notification_message">The SD card can now be safely removed.</string> + + <!-- Shown when external media is missing --> + <string name="ext_media_nomedia_notification_title">Removed SD card</string> + <string name="ext_media_nomedia_notification_message">The SD has been removed. Insert a new SD card to increase your device storage.</string> + + <!-- Shown in LauncherActivity when the requested target Intent didn't return any matching Activities, leaving the list empty. --> + <string name="activity_list_empty">No matching activities found</string> + + <!-- permission attributes related to package usage statistics --> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_pkgUsageStats">update component usage statistics</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_pkgUsageStats">Allows the modification of collected component usage statistics. Not for use by normal applications.</string> + + <!-- Shown in the tutorial for double tap to zoom. --> + <string name="tutorial_double_tap_to_zoom_message">Congratulations on downloading the Android software update. This update includes a number of great new features for you to enjoy. One major improvement we've made is to how you zoom. Now, when you want to zoom, just double tap on the screen. This will bring up a zoom widget. Drag the widget's handle clockwise to zoom in, and counter-clockwise to zoom out.</string> + </resources> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 41d3a81..6ed2d8d 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -84,6 +84,11 @@ <style name="Animation.Translucent"> </style> + <!-- Standard animations for a non-full-screen window or activity. --> + <style name="Animation.LockScreen"> + <item name="windowExitAnimation">@anim/lock_screen_exit</item> + </style> + <style name="Animation.OptionsPanel"> <item name="windowEnterAnimation">@anim/options_panel_enter</item> <item name="windowExitAnimation">@anim/options_panel_exit</item> @@ -126,6 +131,12 @@ <item name="windowExitAnimation">@anim/input_method_exit</item> </style> + <!-- Special optional fancy IM animations. @hide --> + <style name="Animation.InputMethodFancy"> + <item name="windowEnterAnimation">@anim/input_method_fancy_enter</item> + <item name="windowExitAnimation">@anim/input_method_fancy_exit</item> + </style> + <!-- Window animations that are applied to the search bar overlay window. {@hide Pending API council approval} --> <style name="Animation.SearchBar"> @@ -289,13 +300,14 @@ </style> <style name="Widget.TextView.ListSeparator"> - <item name="android:background">@android:drawable/settings_header</item> + <item name="android:background">@android:drawable/dark_header</item> <item name="android:layout_width">fill_parent</item> - <item name="android:layout_height">27dip</item> - <item name="android:textSize">18sp</item> - <item name="android:textColor">#FF000000</item> + <item name="android:layout_height">25dip</item> + <item name="android:textStyle">bold</item> + <item name="android:textColor">?textColorSecondary</item> + <item name="android:textSize">14sp</item> <item name="android:gravity">center_vertical</item> - <item name="android:paddingLeft">5sp</item> + <item name="android:paddingLeft">5sp</item> </style> <style name="Widget.EditText"> @@ -338,7 +350,7 @@ <item name="android:dropDownSelector">@android:drawable/list_selector_background</item> <item name="android:popupBackground">@android:drawable/spinner_dropdown_background</item> <item name="android:dropDownVerticalOffset">-8px</item> - <item name="android:dropDownHorizontalOffset">2px</item> + <item name="android:dropDownHorizontalOffset">0px</item> </style> <style name="Widget.Spinner"> @@ -371,6 +383,11 @@ <item name="android:fadingEdge">vertical</item> </style> + <style name="Widget.HorizontalScrollView"> + <item name="android:scrollbars">horizontal</item> + <item name="android:fadingEdge">horizontal</item> + </style> + <style name="Widget.ListView" parent="Widget.AbsListView"> <item name="android:listSelector">@android:drawable/list_selector_background</item> <item name="android:cacheColorHint">?android:attr/colorBackground</item> @@ -423,12 +440,27 @@ <item name="android:popupBackground">@android:drawable/editbox_dropdown_background_dark</item> </style> + <style name="Widget.KeyboardView" parent="android:Widget"> + <item name="android:background">@android:drawable/keyboard_background</item> + <item name="android:keyBackground">@android:drawable/btn_keyboard_key</item> + <item name="android:keyTextSize">22sp</item> + <item name="android:keyTextColor">#FFFFFFFF</item> + <item name="android:keyPreviewLayout">@android:layout/keyboard_key_preview</item> + <item name="android:keyPreviewOffset">-12dp</item> + <item name="android:keyPreviewHeight">80dp</item> + <item name="android:labelTextSize">14sp</item> + <item name="android:popupLayout">@android:layout/keyboard_popup_keyboard</item> + <item name="android:verticalCorrection">-10dip</item> + <item name="android:shadowColor">#BB000000</item> + <item name="android:shadowRadius">2.75</item> + </style> + <!-- Text Appearances --> <eat-comment /> <style name="TextAppearance"> <item name="android:textColor">?textColorPrimary</item> - <item name="android:textColorHighlight">#FF1B82EB</item> + <item name="android:textColorHighlight">#FFFF9200</item> <item name="android:textColorHint">?textColorHint</item> <item name="android:textColorLink">#5C5CFF</item> <item name="android:textSize">16sp</item> @@ -542,36 +574,6 @@ <item name="android:textStyle">bold</item> </style> - <style name="MediaButton"> - <item name="android:background">@android:drawable/media_button_background</item> - <item name="android:layout_width">71px</item> - <item name="android:layout_height">52px</item> - </style> - - <style name="MediaButton.Previous"> - <item name="android:src">@android:drawable/ic_media_previous</item> - </style> - - <style name="MediaButton.Next"> - <item name="android:src">@android:drawable/ic_media_next</item> - </style> - - <style name="MediaButton.Play"> - <item name="android:src">@android:drawable/ic_media_play</item> - </style> - - <style name="MediaButton.Ffwd"> - <item name="android:src">@android:drawable/ic_media_ff</item> - </style> - - <style name="MediaButton.Rew"> - <item name="android:src">@android:drawable/ic_media_rew</item> - </style> - - <style name="MediaButton.Pause"> - <item name="android:src">@android:drawable/ic_media_pause</item> - </style> - <!-- Preference Styles --> <style name="Preference"> @@ -619,19 +621,37 @@ <item name="android:showDefault">true</item> </style> - <style name="Widget.KeyboardView" parent="android:Widget"> - <item name="android:background">@android:drawable/keyboard_background</item> - <item name="android:keyBackground">@android:drawable/btn_keyboard_key</item> - <item name="android:keyTextSize">22sp</item> - <item name="android:keyTextColor">#FFFFFFFF</item> - <item name="android:keyPreviewLayout">@android:layout/keyboard_key_preview</item> - <item name="android:keyPreviewOffset">-12dp</item> - <item name="android:keyPreviewHeight">80dp</item> - <item name="android:labelTextSize">14sp</item> - <item name="android:popupLayout">@android:layout/keyboard_popup_keyboard</item> - <item name="android:verticalCorrection">-10dip</item> - <item name="android:shadowColor">#BB000000</item> - <item name="android:shadowRadius">2.75</item> + <!-- Other Misc Styles --> + <eat-comment /> + + <style name="MediaButton"> + <item name="android:background">@android:drawable/media_button_background</item> + <item name="android:layout_width">71px</item> + <item name="android:layout_height">52px</item> + </style> + + <style name="MediaButton.Previous"> + <item name="android:src">@android:drawable/ic_media_previous</item> + </style> + + <style name="MediaButton.Next"> + <item name="android:src">@android:drawable/ic_media_next</item> + </style> + + <style name="MediaButton.Play"> + <item name="android:src">@android:drawable/ic_media_play</item> + </style> + + <style name="MediaButton.Ffwd"> + <item name="android:src">@android:drawable/ic_media_ff</item> + </style> + + <style name="MediaButton.Rew"> + <item name="android:src">@android:drawable/ic_media_rew</item> + </style> + + <style name="MediaButton.Pause"> + <item name="android:src">@android:drawable/ic_media_pause</item> </style> <style name="ZoomControls"> @@ -640,4 +660,15 @@ <item name="android:paddingLeft">15dip</item> <item name="android:paddingRight">15dip</item> </style> + + <!-- Style you can use with a container (typically a horizontal + LinearLayout) to get the standard "button bar" background and + spacing. @hide --> + <style name="ButtonBar"> + <item name="android:paddingTop">5dip</item> + <item name="android:paddingLeft">4dip</item> + <item name="android:paddingRight">4dip</item> + <item name="android:paddingBottom">1dip</item> + <item name="android:background">@android:drawable/bottom_bar</item> + </style> </resources> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 3f07e70..01c46de 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -74,7 +74,7 @@ <item name="buttonStyleToggle">@android:style/Widget.Button.Toggle</item> <!-- List attributes --> - <item name="listPreferredItemHeight">64px</item> + <item name="listPreferredItemHeight">64dip</item> <item name="listDivider">@drawable/divider_horizontal_dark</item> <item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator</item> @@ -122,8 +122,8 @@ <item name="scrollbarSize">10dip</item> <item name="scrollbarThumbHorizontal">@android:drawable/scrollbar_handle_horizontal</item> <item name="scrollbarThumbVertical">@android:drawable/scrollbar_handle_vertical</item> - <item name="scrollbarTrackHorizontal">@android:drawable/scrollbar_horizontal</item> - <item name="scrollbarTrackVertical">@android:drawable/scrollbar_vertical</item> + <item name="scrollbarTrackHorizontal">@null</item> + <item name="scrollbarTrackVertical">@null</item> <!-- Widget styles --> <item name="absListViewStyle">@android:style/Widget.AbsListView</item> @@ -150,6 +150,7 @@ <item name="ratingBarStyleSmall">@android:style/Widget.RatingBar.Small</item> <item name="radioButtonStyle">@android:style/Widget.CompoundButton.RadioButton</item> <item name="scrollViewStyle">@android:style/Widget.ScrollView</item> + <item name="horizontalScrollViewStyle">@android:style/Widget.HorizontalScrollView</item> <item name="spinnerStyle">@android:style/Widget.Spinner</item> <item name="starStyle">@android:style/Widget.CompoundButton.Star</item> <item name="starStyleButtonless">@android:style/Widget.CompoundButton.StarButtonless</item> @@ -276,7 +277,8 @@ <item name="android:windowBackground">@null</item> <item name="android:windowIsTranslucent">false</item> <item name="android:windowAnimationStyle">@null</item> - <item name="android:windowDisablePreview">@null</item> + <item name="android:windowDisablePreview">true</item> + <item name="android:windowNoDisplay">true</item> </style> <!-- Default theme for dialog windows and activities, which is used by the |