diff options
125 files changed, 7127 insertions, 1236 deletions
diff --git a/Android.mk b/Android.mk index b11877e2cde2..9c5773fd8ef5 100644 --- a/Android.mk +++ b/Android.mk @@ -362,6 +362,34 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS += \ -hdf sdk.rel.id $(framework_docs_SDK_REL_ID) \ -hdf sdk.current $(framework_docs_SDK_CURRENT_DIR) +# ==== the api stubs and current.xml =========================== +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES) +LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES) +LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES) +LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS) +LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH) +LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR) +LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR) + +LOCAL_MODULE := api-stubs + +LOCAL_DROIDDOC_OPTIONS:=\ + $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \ + -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_stubs_current_intermediates/src \ + -apixml $(INTERNAL_PLATFORM_API_FILE) \ + -nodocs + +LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk +LOCAL_DROIDDOC_CUSTOM_ASSET_DIR:=assets-sdk + +include $(BUILD_DROIDDOC) + +$(full_target): $(framework_built) +$(INTERNAL_PLATFORM_API_FILE): $(full_target) +$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE)) + # ==== static html in the sdk ================================== include $(CLEAR_VARS) @@ -380,10 +408,7 @@ LOCAL_DROIDDOC_OPTIONS:=\ -title "Android SDK" \ -proofread $(OUT_DOCS)/$(LOCAL_MODULE)-proofread.txt \ -todo $(OUT_DOCS)/$(LOCAL_MODULE)-docs-todo.html \ - -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_stubs_current_intermediates/src \ - -apixml $(INTERNAL_PLATFORM_API_FILE) \ -sdkvalues $(OUT_DOCS) \ - -warning 3 \ -hdf android.whichdoc offline LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk @@ -399,8 +424,6 @@ $(static_doc_index_redirect): \ $(full_target): $(static_doc_index_redirect) $(full_target): $(framework_built) -$(INTERNAL_PLATFORM_API_FILE): $(full_target) -$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE)) # ==== docs for the web (on the google app engine server) ======================= diff --git a/api/current.xml b/api/current.xml index 1c1e1ba06a78..3e3a8553a203 100644 --- a/api/current.xml +++ b/api/current.xml @@ -52689,6 +52689,17 @@ visibility="public" > </method> +<method name="getDensityScale" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getHeight" return="int" abstract="false" @@ -52762,6 +52773,58 @@ visibility="public" > </method> +<method name="getScaledHeight" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="canvas" type="android.graphics.Canvas"> +</parameter> +</method> +<method name="getScaledHeight" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="metrics" type="android.util.DisplayMetrics"> +</parameter> +</method> +<method name="getScaledWidth" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="canvas" type="android.graphics.Canvas"> +</parameter> +</method> +<method name="getScaledWidth" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="metrics" type="android.util.DisplayMetrics"> +</parameter> +</method> <method name="getWidth" return="int" abstract="false" @@ -52784,6 +52847,17 @@ visibility="public" > </method> +<method name="isAutoScalingEnabled" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="isMutable" return="boolean" abstract="false" @@ -52828,6 +52902,32 @@ visibility="public" > </method> +<method name="setAutoScalingEnabled" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="autoScalingEnabled" type="boolean"> +</parameter> +</method> +<method name="setDensityScale" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="densityScale" type="float"> +</parameter> +</method> <method name="setPixel" return="void" abstract="false" @@ -52895,6 +52995,17 @@ visibility="public" > </field> +<field name="DENSITY_SCALE_UNKNOWN" + type="float" + transient="false" + volatile="false" + value="-1.0f" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> </class> <class name="Bitmap.CompressFormat" extends="java.lang.Enum" @@ -53114,6 +53225,27 @@ deprecated="not deprecated" visibility="public" > +<parameter name="res" type="android.content.res.Resources"> +</parameter> +<parameter name="value" type="android.util.TypedValue"> +</parameter> +<parameter name="is" type="java.io.InputStream"> +</parameter> +<parameter name="pad" type="android.graphics.Rect"> +</parameter> +<parameter name="opts" type="android.graphics.BitmapFactory.Options"> +</parameter> +</method> +<method name="decodeStream" + return="android.graphics.Bitmap" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> <parameter name="is" type="java.io.InputStream"> </parameter> <parameter name="outPadding" type="android.graphics.Rect"> @@ -53162,6 +53294,16 @@ visibility="public" > </method> +<field name="inDensity" + type="int" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="inDither" type="boolean" transient="false" @@ -53222,6 +53364,16 @@ visibility="public" > </field> +<field name="inScaled" + type="boolean" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="inTempStorage" type="byte[]" transient="false" @@ -54476,6 +54628,17 @@ visibility="public" > </method> +<method name="getDensityScale" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getDrawFilter" return="android.graphics.DrawFilter" abstract="false" @@ -54822,6 +54985,19 @@ <parameter name="bitmap" type="android.graphics.Bitmap"> </parameter> </method> +<method name="setDensityScale" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="densityScale" type="float"> +</parameter> +</method> <method name="setDrawFilter" return="void" abstract="false" @@ -65986,6 +66162,19 @@ <exception name="IOException" type="java.io.IOException"> </exception> </method> +<method name="setZoomCallback" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="cb" type="android.hardware.Camera.ZoomCallback"> +</parameter> +</method> <method name="startPreview" return="void" abstract="false" @@ -66025,6 +66214,25 @@ <parameter name="jpeg" type="android.hardware.Camera.PictureCallback"> </parameter> </method> +<method name="takePicture" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="shutter" type="android.hardware.Camera.ShutterCallback"> +</parameter> +<parameter name="raw" type="android.hardware.Camera.PictureCallback"> +</parameter> +<parameter name="postview" type="android.hardware.Camera.PictureCallback"> +</parameter> +<parameter name="jpeg" type="android.hardware.Camera.PictureCallback"> +</parameter> +</method> <field name="CAMERA_ERROR_SERVER_DIED" type="int" transient="false" @@ -66426,6 +66634,29 @@ > </field> </class> +<interface name="Camera.ZoomCallback" + abstract="true" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="onZoomUpdate" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="zoomLevel" type="int"> +</parameter> +<parameter name="camera" type="android.hardware.Camera"> +</parameter> +</method> +</interface> <class name="GeomagneticField" extends="java.lang.Object" abstract="false" @@ -136994,6 +137225,50 @@ visibility="public" > </method> +<field name="DENSITY_DEFAULT" + type="int" + transient="false" + volatile="false" + value="160" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="DENSITY_HIGH" + type="int" + transient="false" + volatile="false" + value="240" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="DENSITY_LOW" + type="int" + transient="false" + volatile="false" + value="120" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="DENSITY_MEDIUM" + type="int" + transient="false" + volatile="false" + value="160" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="density" type="float" transient="false" @@ -139043,6 +139318,28 @@ visibility="public" > </field> +<field name="DENSITY_DEFAULT" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="DENSITY_NONE" + type="int" + transient="false" + volatile="false" + value="65535" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="TYPE_ATTRIBUTE" type="int" transient="false" @@ -139271,6 +139568,16 @@ visibility="public" > </field> +<field name="density" + type="int" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="resourceId" type="int" transient="false" diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp index 96389dd5d22c..97b43a4fe259 100644 --- a/camera/libcameraservice/CameraService.cpp +++ b/camera/libcameraservice/CameraService.cpp @@ -227,6 +227,8 @@ CameraService::Client::Client(const sp<CameraService>& cameraService, mMediaPlayerClick = newMediaPlayer("/system/media/audio/ui/camera_click.ogg"); mMediaPlayerBeep = newMediaPlayer("/system/media/audio/ui/VideoRecord.ogg"); + mOverlayW = 0; + mOverlayH = 0; // Callback is disabled by default mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP; @@ -399,6 +401,11 @@ void CameraService::Client::disconnect() mHardware->cancelPicture(true, true, true); // Release the hardware resources. mHardware->release(); + // Release the held overlay resources. + if (mUseOverlay) + { + mOverlayRef = 0; + } mHardware.clear(); mCameraService->removeClient(mCameraClient); @@ -420,11 +427,21 @@ status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface) result = NO_ERROR; // asBinder() is safe on NULL (returns NULL) if (surface->asBinder() != mSurface->asBinder()) { - if (mSurface != 0 && !mUseOverlay) { + if (mSurface != 0) { LOGD("clearing old preview surface %p", mSurface.get()); - mSurface->unregisterBuffers(); + if ( !mUseOverlay) + { + mSurface->unregisterBuffers(); + } + else + { + // Force the destruction of any previous overlay + sp<Overlay> dummy; + mHardware->setOverlay( dummy ); + } } mSurface = surface; + mOverlayRef = 0; // If preview has been already started, set overlay or register preview // buffers now. if (mHardware->previewEnabled()) { @@ -520,8 +537,8 @@ status_t CameraService::Client::setOverlay() const char *format = params.getPreviewFormat(); int fmt; - if (!strcmp(format, "yuv422i")) - fmt = OVERLAY_FORMAT_YCbCr_422_I; + if (!strcmp(format, "yuv422i-yuyv")) + fmt = OVERLAY_FORMAT_YCbYCr_422_I; else if (!strcmp(format, "rgb565")) fmt = OVERLAY_FORMAT_RGB_565; else { @@ -529,16 +546,35 @@ status_t CameraService::Client::setOverlay() return -EINVAL; } + if ( w != mOverlayW || h != mOverlayH ) + { + // Force the destruction of any previous overlay + sp<Overlay> dummy; + mHardware->setOverlay( dummy ); + mOverlayRef = 0; + } + status_t ret = NO_ERROR; if (mSurface != 0) { - sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt); - ret = mHardware->setOverlay(new Overlay(ref)); + if (mOverlayRef.get() == NULL) { + mOverlayRef = mSurface->createOverlay(w, h, fmt); + if ( mOverlayRef.get() == NULL ) + { + LOGE("Overlay Creation Failed!"); + return -EINVAL; + } + ret = mHardware->setOverlay(new Overlay(mOverlayRef)); + } } else { ret = mHardware->setOverlay(NULL); } if (ret != NO_ERROR) { LOGE("mHardware->setOverlay() failed with status %d\n", ret); } + + mOverlayW = w; + mOverlayH = h; + return ret; } @@ -1092,6 +1128,7 @@ void CameraService::Client::postPreviewFrame(const sp<IMemory>& mem) ssize_t offset; size_t size; sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); + if ( !mUseOverlay ) { Mutex::Autolock surfaceLock(mSurfaceLock); if (mSurface != NULL) { diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h index ea93789d14a0..8a49fa6a7d99 100644 --- a/camera/libcameraservice/CameraService.h +++ b/camera/libcameraservice/CameraService.h @@ -189,6 +189,10 @@ private: sp<CameraHardwareInterface> mHardware; pid_t mClientPid; bool mUseOverlay; + + sp<OverlayRef> mOverlayRef; + int mOverlayW; + int mOverlayH; }; // ---------------------------------------------------------------------------- diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index f2814f2acee6..76b47f1140c3 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -32,7 +32,6 @@ import android.content.pm.InstrumentationInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; -import android.content.pm.PackageParser.Component; import android.content.res.AssetManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl index 84a6085c4b75..bd725443e13a 100644 --- a/core/java/android/app/ISearchManager.aidl +++ b/core/java/android/app/ISearchManager.aidl @@ -37,4 +37,5 @@ interface ISearchManager { ISearchManagerCallback searchManagerCallback, int ident); void stopSearch(); + boolean isVisible(); } diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index bfd9923712bb..54b65271b051 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -139,8 +139,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // A weak map of drawables we've gotten from other packages, so we don't load them // more than once. - private final WeakHashMap<String, Drawable> mOutsideDrawablesCache = - new WeakHashMap<String, Drawable>(); + private final WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache = + new WeakHashMap<String, Drawable.ConstantState>(); // Last known IME options value for the search edit text. private int mSearchAutoCompleteImeOptions; @@ -354,7 +354,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } show(); } - updateUI(); return true; @@ -490,6 +489,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS */ private void updateUI() { if (mSearchable != null) { + mDecor.setVisibility(View.VISIBLE); updateSearchAutoComplete(); updateSearchButton(); updateSearchAppIcon(); @@ -994,7 +994,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS }; @Override - public void dismiss() { + public void hide() { if (!isShowing()) return; // We made sure the IME was displayed, so also make sure it is closed @@ -1005,10 +1005,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS imm.hideSoftInputFromWindow( getWindow().getDecorView().getWindowToken(), 0); } - - super.dismiss(); + + super.hide(); } - + /** * React to the user typing while in the suggestions list. First, check for action * keys. If not handled, try refocusing regular characters into the EditText. @@ -1042,6 +1042,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mSearchAutoComplete.setSelection(selPoint); mSearchAutoComplete.setListSelection(0); mSearchAutoComplete.clearListSelection(); + mSearchAutoComplete.ensureImeVisible(); + return true; } @@ -1232,8 +1234,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** - * Launches an intent and dismisses the search dialog (unless the intent - * is one of the special intents that modifies the state of the search dialog). + * Launches an intent, including any special intent handling. Doesn't dismiss the dialog + * since that will be handled in {@link SearchDialogWrapper#performActivityResuming} */ private void launchIntent(Intent intent) { if (intent == null) { @@ -1242,7 +1244,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (handleSpecialIntent(intent)){ return; } - dismiss(); + Log.d(LOG_TAG, "launching " + intent); getContext().startActivity(intent); } diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index b795a5431ede..325c207222f4 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -1534,7 +1534,6 @@ public class SearchManager private int mIdent; // package private since they are used by the inner class SearchManagerCallback - /* package */ boolean mIsShowing = false; /* package */ final Handler mHandler; /* package */ OnDismissListener mDismissListener = null; /* package */ OnCancelListener mCancelListener = null; @@ -1600,12 +1599,9 @@ public class SearchManager ComponentName launchActivity, Bundle appSearchData, boolean globalSearch) { - if (DBG) debug("startSearch(), mIsShowing=" + mIsShowing); - if (mIsShowing) return; if (mIdent == 0) throw new IllegalArgumentException( "Called from outside of an Activity context"); try { - mIsShowing = true; // activate the search manager and start it up! mService.startSearch(initialQuery, selectInitialQuery, launchActivity, appSearchData, globalSearch, mSearchManagerCallback, mIdent); @@ -1626,15 +1622,10 @@ public class SearchManager * @see #startSearch */ public void stopSearch() { - if (DBG) debug("stopSearch(), mIsShowing=" + mIsShowing); - if (!mIsShowing) return; + if (DBG) debug("stopSearch()"); try { mService.stopSearch(); - // onDismiss will also clear this, but we do it here too since onDismiss() is - // called asynchronously. - mIsShowing = false; } catch (RemoteException ex) { - Log.e(TAG, "stopSearch() failed: " + ex); } } @@ -1648,8 +1639,13 @@ public class SearchManager * @hide */ public boolean isVisible() { - if (DBG) debug("isVisible(), mIsShowing=" + mIsShowing); - return mIsShowing; + if (DBG) debug("isVisible()"); + try { + return mService.isVisible(); + } catch (RemoteException e) { + Log.e(TAG, "isVisible() failed: " + e); + return false; + } } /** @@ -1701,7 +1697,6 @@ public class SearchManager private final Runnable mFireOnDismiss = new Runnable() { public void run() { if (DBG) debug("mFireOnDismiss"); - mIsShowing = false; if (mDismissListener != null) { mDismissListener.onDismiss(); } @@ -1711,7 +1706,6 @@ public class SearchManager private final Runnable mFireOnCancel = new Runnable() { public void run() { if (DBG) debug("mFireOnCancel"); - // doesn't need to clear mIsShowing since onDismiss() always gets called too if (mCancelListener != null) { mCancelListener.onCancel(); } diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java index f2325d020772..593b7b736cfa 100644 --- a/core/java/android/app/SuggestionsAdapter.java +++ b/core/java/android/app/SuggestionsAdapter.java @@ -61,8 +61,8 @@ class SuggestionsAdapter extends ResourceCursorAdapter { private SearchDialog mSearchDialog; private SearchableInfo mSearchable; private Context mProviderContext; - private WeakHashMap<String, Drawable> mOutsideDrawablesCache; - private SparseArray<Drawable> mBackgroundsCache; + private WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache; + private SparseArray<Drawable.ConstantState> mBackgroundsCache; private boolean mGlobalSearchMode; // Cached column indexes, updated when the cursor changes. @@ -97,8 +97,10 @@ class SuggestionsAdapter extends ResourceCursorAdapter { */ private static final long DELETE_KEY_POST_DELAY = 500L; - public SuggestionsAdapter(Context context, SearchDialog searchDialog, SearchableInfo searchable, - WeakHashMap<String, Drawable> outsideDrawablesCache, boolean globalSearchMode) { + public SuggestionsAdapter(Context context, SearchDialog searchDialog, + SearchableInfo searchable, + WeakHashMap<String, Drawable.ConstantState> outsideDrawablesCache, + boolean globalSearchMode) { super(context, com.android.internal.R.layout.search_dropdown_item_icons_2line, null, // no initial cursor @@ -112,7 +114,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter { mProviderContext = mSearchable.getProviderContext(mContext, activityContext); mOutsideDrawablesCache = outsideDrawablesCache; - mBackgroundsCache = new SparseArray<Drawable>(); + mBackgroundsCache = new SparseArray<Drawable.ConstantState>(); mGlobalSearchMode = globalSearchMode; mStartSpinnerRunnable = new Runnable() { @@ -345,11 +347,10 @@ class SuggestionsAdapter extends ResourceCursorAdapter { if (backgroundColor == 0) { return null; } else { - Drawable cachedBg = mBackgroundsCache.get(backgroundColor); + Drawable.ConstantState cachedBg = mBackgroundsCache.get(backgroundColor); if (cachedBg != null) { if (DBG) Log.d(LOG_TAG, "Background cache hit for color " + backgroundColor); - // copy the drawable so that they don't share states - return cachedBg.getConstantState().newDrawable(); + return cachedBg.newDrawable(); } if (DBG) Log.d(LOG_TAG, "Creating new background for color " + backgroundColor); ColorDrawable transparent = new ColorDrawable(0); @@ -358,7 +359,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter { newBg.addState(new int[]{android.R.attr.state_selected}, transparent); newBg.addState(new int[]{android.R.attr.state_pressed}, transparent); newBg.addState(new int[]{}, background); - mBackgroundsCache.put(backgroundColor, newBg); + mBackgroundsCache.put(backgroundColor, newBg.getConstantState()); return newBg; } } @@ -523,12 +524,13 @@ class SuggestionsAdapter extends ResourceCursorAdapter { } // First, check the cache. - Drawable drawable = mOutsideDrawablesCache.get(drawableId); - if (drawable != null) { + Drawable.ConstantState cached = mOutsideDrawablesCache.get(drawableId); + if (cached != null) { if (DBG) Log.d(LOG_TAG, "Found icon in cache: " + drawableId); - return drawable; + return cached.newDrawable(); } + Drawable drawable = null; try { // Not cached, try using it as a plain resource ID in the provider's context. int resourceId = Integer.parseInt(drawableId); @@ -560,7 +562,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter { // If we got a drawable for this resource id, then stick it in the // map so we don't do this lookup again. if (drawable != null) { - mOutsideDrawablesCache.put(drawableId, drawable); + mOutsideDrawablesCache.put(drawableId, drawable.getConstantState()); } } catch (Resources.NotFoundException nfe) { if (DBG) Log.d(LOG_TAG, "Icon resource not found: " + drawableId); @@ -615,12 +617,14 @@ class SuggestionsAdapter extends ResourceCursorAdapter { String componentIconKey = component.flattenToShortString(); // Using containsKey() since we also store null values. if (mOutsideDrawablesCache.containsKey(componentIconKey)) { - return mOutsideDrawablesCache.get(componentIconKey); + Drawable.ConstantState cached = mOutsideDrawablesCache.get(componentIconKey); + return cached == null ? null : cached.newDrawable(); } // Then try the activity or application icon Drawable drawable = getActivityIcon(component); // Stick it in the cache so we don't do this lookup again. - mOutsideDrawablesCache.put(componentIconKey, drawable); + Drawable.ConstantState toCache = drawable == null ? null : drawable.getConstantState(); + mOutsideDrawablesCache.put(componentIconKey, toCache); return drawable; } diff --git a/core/java/android/appwidget/AppWidgetProvider.java b/core/java/android/appwidget/AppWidgetProvider.java index 26712a10f0ce..f1bbedef9024 100755 --- a/core/java/android/appwidget/AppWidgetProvider.java +++ b/core/java/android/appwidget/AppWidgetProvider.java @@ -64,11 +64,9 @@ public class AppWidgetProvider extends BroadcastReceiver { } else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) { Bundle extras = intent.getExtras(); - if (extras != null) { - int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS); - if (appWidgetIds != null && appWidgetIds.length > 0) { - this.onDeleted(context, appWidgetIds); - } + if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) { + final int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID); + this.onDeleted(context, new int[] { appWidgetId }); } } else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) { diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index c942a27e8cd1..a64c6d72d43c 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -74,6 +74,14 @@ public class BluetoothDevice { /** An existing bond was explicitly revoked */ public static final int UNBOND_REASON_REMOVED = 6; + /* The user will be prompted to enter a pin */ + public static final int PAIRING_VARIANT_PIN = 0; + /* The user will be prompted to enter a passkey */ + public static final int PAIRING_VARIANT_PASSKEY = 1; + /* The user will be prompted to confirm the passkey displayed on the screen */ + public static final int PAIRING_VARIANT_CONFIRMATION = 2; + + private static final String TAG = "BluetoothDevice"; private final IBluetoothDevice mService; @@ -358,9 +366,24 @@ public class BluetoothDevice { } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } - public boolean cancelPin(String address) { + + public boolean setPasskey(String address, int passkey) { + try { + return mService.setPasskey(address, passkey); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public boolean setPairingConfirmation(String address, boolean confirm) { + try { + return mService.setPairingConfirmation(address, confirm); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public boolean cancelPairingUserInput(String address) { try { - return mService.cancelPin(address); + return mService.cancelPairingUserInput(address); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } diff --git a/core/java/android/bluetooth/BluetoothIntent.java b/core/java/android/bluetooth/BluetoothIntent.java index 344601b0b89a..d6c79b404909 100644 --- a/core/java/android/bluetooth/BluetoothIntent.java +++ b/core/java/android/bluetooth/BluetoothIntent.java @@ -57,6 +57,10 @@ public interface BluetoothIntent { "android.bluetooth.intent.BOND_PREVIOUS_STATE"; public static final String REASON = "android.bluetooth.intent.REASON"; + public static final String PAIRING_VARIANT = + "android.bluetooth.intent.PAIRING_VARIANT"; + public static final String PASSKEY = + "android.bluetooth.intent.PASSKEY"; /** Broadcast when the local Bluetooth device state changes, for example * when Bluetooth is enabled. Will contain int extra's BLUETOOTH_STATE and diff --git a/core/java/android/bluetooth/IBluetoothDevice.aidl b/core/java/android/bluetooth/IBluetoothDevice.aidl index c249c817a464..a78752bff5e5 100644 --- a/core/java/android/bluetooth/IBluetoothDevice.aidl +++ b/core/java/android/bluetooth/IBluetoothDevice.aidl @@ -54,5 +54,8 @@ interface IBluetoothDevice int getRemoteServiceChannel(in String address, String uuid); boolean setPin(in String address, in byte[] pin); - boolean cancelPin(in String address); + boolean setPasskey(in String address, int passkey); + boolean setPairingConfirmation(in String address, boolean confirm); + boolean cancelPairingUserInput(in String address); + } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 903f4820b101..cebb696867b0 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -979,12 +979,12 @@ public class PackageParser { /** * TODO: enable this before code freeze. b/1967935 * * - */ if ((densities == null || densities.length == 0) && (pkg.applicationInfo.targetSdkVersion >= android.os.Build.VERSION_CODES.CUR_DEVELOPMENT)) { pkg.supportsDensities = ApplicationInfo.ANY_DENSITIES_ARRAY; } + */ return pkg; } diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java index ebe556e3cd65..6e34cc8a24dc 100644 --- a/core/java/android/content/res/CompatibilityInfo.java +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -38,7 +38,12 @@ public class CompatibilityInfo { private static final String TAG = "CompatibilityInfo"; /** default compatibility info object for compatible applications */ - public static final CompatibilityInfo DEFAULT_COMPATIBILITY_INFO = new CompatibilityInfo(); + public static final CompatibilityInfo DEFAULT_COMPATIBILITY_INFO = new CompatibilityInfo() { + @Override + public void setExpandable(boolean expandable) { + throw new UnsupportedOperationException("trying to change default compatibility info"); + } + }; /** * The default width of the screen in portrait mode. @@ -80,6 +85,11 @@ public class CompatibilityInfo { private static final int SCALING_EXPANDABLE_MASK = SCALING_REQUIRED | EXPANDABLE; /** + * The effective screen density we have selected for this application. + */ + public final int applicationDensity; + + /** * Application's scale. */ public final float applicationScale; @@ -102,30 +112,36 @@ public class CompatibilityInfo { } float packageDensityScale = -1.0f; + int packageDensity = 0; if (appInfo.supportsDensities != null) { int minDiff = Integer.MAX_VALUE; for (int density : appInfo.supportsDensities) { - if (density == ApplicationInfo.ANY_DENSITY) { + if (density == ApplicationInfo.ANY_DENSITY) { + packageDensity = DisplayMetrics.DENSITY_DEVICE; packageDensityScale = 1.0f; break; } - int tmpDiff = Math.abs(DisplayMetrics.DEVICE_DENSITY - density); + int tmpDiff = Math.abs(DisplayMetrics.DENSITY_DEVICE - density); if (tmpDiff == 0) { + packageDensity = DisplayMetrics.DENSITY_DEVICE; packageDensityScale = 1.0f; break; } // prefer higher density (appScale>1.0), unless that's only option. if (tmpDiff < minDiff && packageDensityScale < 1.0f) { - packageDensityScale = DisplayMetrics.DEVICE_DENSITY / (float) density; + packageDensity = density; + packageDensityScale = DisplayMetrics.DENSITY_DEVICE / (float) density; minDiff = tmpDiff; } } } if (packageDensityScale > 0.0f) { + applicationDensity = packageDensity; applicationScale = packageDensityScale; } else { + applicationDensity = DisplayMetrics.DENSITY_DEFAULT; applicationScale = - DisplayMetrics.DEVICE_DENSITY / (float) DisplayMetrics.DEFAULT_DENSITY; + DisplayMetrics.DENSITY_DEVICE / (float) DisplayMetrics.DENSITY_DEFAULT; } applicationInvertedScale = 1.0f / applicationScale; @@ -134,9 +150,11 @@ public class CompatibilityInfo { } } - private CompatibilityInfo(int appFlags, int compFlags, float scale, float invertedScale) { + private CompatibilityInfo(int appFlags, int compFlags, + int dens, float scale, float invertedScale) { this.appFlags = appFlags; mCompatibilityFlags = compFlags; + applicationDensity = dens; applicationScale = scale; applicationInvertedScale = invertedScale; } @@ -146,6 +164,7 @@ public class CompatibilityInfo { | ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS | ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS, EXPANDABLE | CONFIGURED_EXPANDABLE, + DisplayMetrics.DENSITY_DEVICE, 1.0f, 1.0f); } @@ -155,7 +174,7 @@ public class CompatibilityInfo { */ public CompatibilityInfo copy() { CompatibilityInfo info = new CompatibilityInfo(appFlags, mCompatibilityFlags, - applicationScale, applicationInvertedScale); + applicationDensity, applicationScale, applicationInvertedScale); return info; } @@ -191,7 +210,7 @@ public class CompatibilityInfo { @Override public String toString() { return "CompatibilityInfo{scale=" + applicationScale + - ", compatibility flag=" + mCompatibilityFlags + "}"; + ", supports screen=" + supportsScreen() + "}"; } /** @@ -205,8 +224,7 @@ public class CompatibilityInfo { if (DBG) Log.d(TAG, "no translation required"); return null; } - if (!isScalingRequired() || - (params.flags & WindowManager.LayoutParams.FLAG_NO_COMPATIBILITY_SCALING) != 0) { + if (!isScalingRequired()) { return null; } return new Translator(); diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index a9aa1ee6b835..2354519d6ad6 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -87,7 +87,7 @@ public class Resources { /*package*/ final DisplayMetrics mMetrics = new DisplayMetrics(); PluralRules mPluralRule; - private final CompatibilityInfo mCompatibilityInfo; + private CompatibilityInfo mCompatibilityInfo; private Display mDefaultDisplay; private static final LongSparseArray<Object> EMPTY_ARRAY = new LongSparseArray<Object>() { @@ -1386,6 +1386,15 @@ public class Resources { } /** + * This is just for testing. + * @hide + */ + public void setCompatibilityInfo(CompatibilityInfo ci) { + mCompatibilityInfo = ci; + updateConfiguration(mConfiguration, mMetrics); + } + + /** * Return a resource identifier for the given resource name. A fully * qualified resource name is of the form "package:type/entry". The first * two components (package and type) are optional if defType and diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 091bc1700988..40d2c869d30a 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -56,7 +56,9 @@ public class Camera { private PictureCallback mRawImageCallback; private PictureCallback mJpegCallback; private PreviewCallback mPreviewCallback; + private PictureCallback mPostviewCallback; private AutoFocusCallback mAutoFocusCallback; + private ZoomCallback mZoomCallback; private ErrorCallback mErrorCallback; private boolean mOneShot; @@ -72,6 +74,8 @@ public class Camera { mRawImageCallback = null; mJpegCallback = null; mPreviewCallback = null; + mPostviewCallback = null; + mZoomCallback = null; Looper looper; if ((looper = Looper.myLooper()) != null) { @@ -245,13 +249,15 @@ public class Camera { return; case CAMERA_MSG_RAW_IMAGE: - if (mRawImageCallback != null) + if (mRawImageCallback != null) { mRawImageCallback.onPictureTaken((byte[])msg.obj, mCamera); + } return; case CAMERA_MSG_COMPRESSED_IMAGE: - if (mJpegCallback != null) + if (mJpegCallback != null) { mJpegCallback.onPictureTaken((byte[])msg.obj, mCamera); + } return; case CAMERA_MSG_PREVIEW_FRAME: @@ -263,15 +269,29 @@ public class Camera { } return; + case CAMERA_MSG_POSTVIEW_FRAME: + if (mPostviewCallback != null) { + mPostviewCallback.onPictureTaken((byte[])msg.obj, mCamera); + } + return; + case CAMERA_MSG_FOCUS: - if (mAutoFocusCallback != null) + if (mAutoFocusCallback != null) { mAutoFocusCallback.onAutoFocus(msg.arg1 == 0 ? false : true, mCamera); + } + return; + + case CAMERA_MSG_ZOOM: + if (mZoomCallback != null) { + mZoomCallback.onZoomUpdate(msg.arg1, mCamera); + } return; case CAMERA_MSG_ERROR : Log.e(TAG, "Error " + msg.arg1); - if (mErrorCallback != null) + if (mErrorCallback != null) { mErrorCallback.onError(msg.arg1, mCamera); + } return; default: @@ -364,13 +384,63 @@ public class Camera { */ public final void takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback jpeg) { + takePicture(shutter, raw, null, jpeg); + } + private native final void native_takePicture(); + + /** + * Triggers an asynchronous image capture. The camera service + * will initiate a series of callbacks to the application as the + * image capture progresses. The shutter callback occurs after + * the image is captured. This can be used to trigger a sound + * to let the user know that image has been captured. The raw + * callback occurs when the raw image data is available. The + * postview callback occurs when a scaled, fully processed + * postview image is available (NOTE: not all hardware supports + * this). The jpeg callback occurs when the compressed image is + * available. If the application does not need a particular + * callback, a null can be passed instead of a callback method. + * + * @param shutter callback after the image is captured, may be null + * @param raw callback with raw image data, may be null + * @param postview callback with postview image data, may be null + * @param jpeg callback with jpeg image data, may be null + */ + public final void takePicture(ShutterCallback shutter, PictureCallback raw, + PictureCallback postview, PictureCallback jpeg) { mShutterCallback = shutter; mRawImageCallback = raw; + mPostviewCallback = postview; mJpegCallback = jpeg; native_takePicture(); } - private native final void native_takePicture(); + /** + * Handles the zoom callback. + */ + public interface ZoomCallback + { + /** + * Callback for zoom updates + * @param zoomLevel new zoom level in 1/1000 increments, + * e.g. a zoom of 3.2x is stored as 3200. Accuracy of the + * value is dependent on the hardware implementation. Not + * all devices will generate this callback. + * @param camera the Camera service object + */ + void onZoomUpdate(int zoomLevel, Camera camera); + }; + + /** + * Registers a callback to be invoked when the zoom + * level is updated by the camera driver. + * @param cb the callback to run + */ + public final void setZoomCallback(ZoomCallback cb) + { + mZoomCallback = cb; + } + // These match the enum in include/ui/Camera.h /** Unspecified camerar error. @see #ErrorCallback */ public static final int CAMERA_ERROR_UNKNOWN = 1; diff --git a/core/java/android/net/http/ConnectionThread.java b/core/java/android/net/http/ConnectionThread.java index 1d0db2bc70d6..0b30e589c4a7 100644 --- a/core/java/android/net/http/ConnectionThread.java +++ b/core/java/android/net/http/ConnectionThread.java @@ -69,6 +69,7 @@ class ConnectionThread extends Thread { */ public void run() { android.os.Process.setThreadPriority( + android.os.Process.THREAD_PRIORITY_DEFAULT + android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE); // these are used to get performance data. When it is not in the timing, diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 480519386e48..980cff39801f 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -739,7 +739,7 @@ public class Process { public static final native void sendSignal(int pid, int signal); /** @hide */ - public static final native int getFreeMemory(); + public static final native long getFreeMemory(); /** @hide */ public static final native void readProcLines(String path, diff --git a/core/java/android/pim/vcard/ContactStruct.java b/core/java/android/pim/vcard/ContactStruct.java new file mode 100644 index 000000000000..46725d31e04d --- /dev/null +++ b/core/java/android/pim/vcard/ContactStruct.java @@ -0,0 +1,1244 @@ +/* + * 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.pim.vcard; + +import android.content.AbstractSyncableContentProvider; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.net.Uri; +import android.provider.Contacts; +import android.provider.Contacts.ContactMethods; +import android.provider.Contacts.Extensions; +import android.provider.Contacts.GroupMembership; +import android.provider.Contacts.Organizations; +import android.provider.Contacts.People; +import android.provider.Contacts.Phones; +import android.provider.Contacts.Photos; +import android.telephony.PhoneNumberUtils; +import android.text.TextUtils; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; + +/** + * This class bridges between data structure of Contact app and VCard data. + */ +public class ContactStruct { + private static final String LOG_TAG = "ContactStruct"; + + /** + * @hide only for testing + */ + static public class PhoneData { + public final int type; + public final String data; + public final String label; + // isPrimary is changable only when there's no appropriate one existing in + // the original VCard. + public boolean isPrimary; + public PhoneData(int type, String data, String label, boolean isPrimary) { + this.type = type; + this.data = data; + this.label = label; + this.isPrimary = isPrimary; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof PhoneData) { + return false; + } + PhoneData phoneData = (PhoneData)obj; + return (type == phoneData.type && data.equals(phoneData.data) && + label.equals(phoneData.label) && isPrimary == phoneData.isPrimary); + } + + @Override + public String toString() { + return String.format("type: %d, data: %s, label: %s, isPrimary: %s", + type, data, label, isPrimary); + } + } + + /** + * @hide only for testing + */ + static public class ContactMethod { + // Contacts.KIND_EMAIL, Contacts.KIND_POSTAL + public final int kind; + // e.g. Contacts.ContactMethods.TYPE_HOME, Contacts.PhoneColumns.TYPE_HOME + // If type == Contacts.PhoneColumns.TYPE_CUSTOM, label is used. + public final int type; + public final String data; + // Used only when TYPE is TYPE_CUSTOM. + public final String label; + // isPrimary is changable only when there's no appropriate one existing in + // the original VCard. + public boolean isPrimary; + public ContactMethod(int kind, int type, String data, String label, + boolean isPrimary) { + this.kind = kind; + this.type = type; + this.data = data; + this.label = data; + this.isPrimary = isPrimary; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ContactMethod) { + return false; + } + ContactMethod contactMethod = (ContactMethod)obj; + return (kind == contactMethod.kind && type == contactMethod.type && + data.equals(contactMethod.data) && label.equals(contactMethod.label) && + isPrimary == contactMethod.isPrimary); + } + + @Override + public String toString() { + return String.format("kind: %d, type: %d, data: %s, label: %s, isPrimary: %s", + kind, type, data, label, isPrimary); + } + } + + /** + * @hide only for testing + */ + static public class OrganizationData { + public final int type; + public final String companyName; + // can be changed in some VCard format. + public String positionName; + // isPrimary is changable only when there's no appropriate one existing in + // the original VCard. + public boolean isPrimary; + public OrganizationData(int type, String companyName, String positionName, + boolean isPrimary) { + this.type = type; + this.companyName = companyName; + this.positionName = positionName; + this.isPrimary = isPrimary; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof OrganizationData) { + return false; + } + OrganizationData organization = (OrganizationData)obj; + return (type == organization.type && companyName.equals(organization.companyName) && + positionName.equals(organization.positionName) && + isPrimary == organization.isPrimary); + } + + @Override + public String toString() { + return String.format("type: %d, company: %s, position: %s, isPrimary: %s", + type, companyName, positionName, isPrimary); + } + } + + static class Property { + private String mPropertyName; + private Map<String, Collection<String>> mParameterMap = + new HashMap<String, Collection<String>>(); + private List<String> mPropertyValueList = new ArrayList<String>(); + private byte[] mPropertyBytes; + + public Property() { + clear(); + } + + public void setPropertyName(final String propertyName) { + mPropertyName = propertyName; + } + + public void addParameter(final String paramName, final String paramValue) { + Collection<String> values; + if (mParameterMap.containsKey(paramName)) { + if (paramName.equals("TYPE")) { + values = new HashSet<String>(); + } else { + values = new ArrayList<String>(); + } + mParameterMap.put(paramName, values); + } else { + values = mParameterMap.get(paramName); + } + } + + public void addToPropertyValueList(final String propertyValue) { + mPropertyValueList.add(propertyValue); + } + + public void setPropertyBytes(final byte[] propertyBytes) { + mPropertyBytes = propertyBytes; + } + + public final Collection<String> getParameters(String type) { + return mParameterMap.get(type); + } + + public final List<String> getPropertyValueList() { + return mPropertyValueList; + } + + public void clear() { + mPropertyName = null; + mParameterMap.clear(); + mPropertyValueList.clear(); + } + } + + private String mName; + private String mPhoneticName; + // private String mPhotoType; + private byte[] mPhotoBytes; + private List<String> mNotes; + private List<PhoneData> mPhoneList; + private List<ContactMethod> mContactMethodList; + private List<OrganizationData> mOrganizationList; + private Map<String, List<String>> mExtensionMap; + + private int mNameOrderType; + + /* private variables bellow is for temporary use. */ + + // For name, there are three fields in vCard: FN, N, NAME. + // We prefer FN, which is a required field in vCard 3.0 , but not in vCard 2.1. + // Next, we prefer NAME, which is defined only in vCard 3.0. + // Finally, we use N, which is a little difficult to parse. + private String mTmpFullName; + private String mTmpNameFromNProperty; + + // Some vCard has "X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", and + // "X-PHONETIC-LAST-NAME" + private String mTmpXPhoneticFirstName; + private String mTmpXPhoneticMiddleName; + private String mTmpXPhoneticLastName; + + // Each Column of four properties has ISPRIMARY field + // (See android.provider.Contacts) + // If false even after the following loop, we choose the first + // entry as a "primary" entry. + private boolean mPrefIsSet_Address; + private boolean mPrefIsSet_Phone; + private boolean mPrefIsSet_Email; + private boolean mPrefIsSet_Organization; + + public ContactStruct() { + mNameOrderType = VCardConfig.NAME_ORDER_TYPE_DEFAULT; + } + + public ContactStruct(int nameOrderType) { + mNameOrderType = nameOrderType; + } + + /** + * @hide only for test + */ + public ContactStruct(String name, + String phoneticName, + byte[] photoBytes, + List<String> notes, + List<PhoneData> phoneList, + List<ContactMethod> contactMethodList, + List<OrganizationData> organizationList, + Map<String, List<String>> extensionMap) { + mName = name; + mPhoneticName = phoneticName; + mPhotoBytes = photoBytes; + mContactMethodList = contactMethodList; + mOrganizationList = organizationList; + mExtensionMap = extensionMap; + } + + /** + * @hide only for test + */ + public String getName() { + return mName; + } + + /** + * @hide only for test + */ + public String getPhoneticName() { + return mPhoneticName; + } + + /** + * @hide only for test + */ + public final byte[] getPhotoBytes() { + return mPhotoBytes; + } + + /** + * @hide only for test + */ + public final List<String> getNotes() { + return mNotes; + } + + /** + * @hide only for test + */ + public final List<PhoneData> getPhoneList() { + return mPhoneList; + } + + /** + * @hide only for test + */ + public final List<ContactMethod> getContactMethodList() { + return mContactMethodList; + } + + /** + * @hide only for test + */ + public final List<OrganizationData> getOrganizationList() { + return mOrganizationList; + } + + /** + * @hide only for test + */ + public final Map<String, List<String>> getExtensionMap() { + return mExtensionMap; + } + + /** + * Add a phone info to phoneList. + * @param data phone number + * @param type type col of content://contacts/phones + * @param label lable col of content://contacts/phones + */ + private void addPhone(int type, String data, String label, boolean isPrimary){ + if (mPhoneList == null) { + mPhoneList = new ArrayList<PhoneData>(); + } + StringBuilder builder = new StringBuilder(); + String trimed = data.trim(); + int length = trimed.length(); + for (int i = 0; i < length; i++) { + char ch = trimed.charAt(i); + if (('0' <= ch && ch <= '9') || (i == 0 && ch == '+')) { + builder.append(ch); + } + } + + PhoneData phoneData = new PhoneData(type, + PhoneNumberUtils.formatNumber(builder.toString()), + label, isPrimary); + + mPhoneList.add(phoneData); + } + + /** + * Add a contactmethod info to contactmethodList. + * @param kind integer value defined in Contacts.java + * (e.g. Contacts.KIND_EMAIL) + * @param type type col of content://contacts/contact_methods + * @param data contact data + * @param label extra string used only when kind is Contacts.KIND_CUSTOM. + */ + private void addContactmethod(int kind, int type, String data, + String label, boolean isPrimary){ + if (mContactMethodList == null) { + mContactMethodList = new ArrayList<ContactMethod>(); + } + mContactMethodList.add(new ContactMethod(kind, type, data, label, isPrimary)); + } + + /** + * Add a Organization info to organizationList. + */ + private void addOrganization(int type, String companyName, String positionName, + boolean isPrimary) { + if (mOrganizationList == null) { + mOrganizationList = new ArrayList<OrganizationData>(); + } + mOrganizationList.add(new OrganizationData(type, companyName, positionName, isPrimary)); + } + + /** + * Set "position" value to the appropriate data. If there's more than one + * OrganizationData objects, the value is set to the last one. If there's no + * OrganizationData object, a new OrganizationData is created, whose company name is + * empty. + * + * TODO: incomplete logic. fix this: + * + * e.g. This assumes ORG comes earlier, but TITLE may come earlier like this, though we do not + * know how to handle it in general cases... + * ---- + * TITLE:Software Engineer + * ORG:Google + * ---- + */ + private void setPosition(String positionValue) { + if (mOrganizationList == null) { + mOrganizationList = new ArrayList<OrganizationData>(); + } + int size = mOrganizationList.size(); + if (size == 0) { + addOrganization(Contacts.OrganizationColumns.TYPE_OTHER, "", null, false); + size = 1; + } + OrganizationData lastData = mOrganizationList.get(size - 1); + lastData.positionName = positionValue; + } + + private void addExtension(String propName, Map<String, Collection<String>> paramMap, + List<String> propValueList) { + if (propValueList.size() == 0) { + return; + } + // Now store the string into extensionMap. + List<String> list; + if (mExtensionMap == null) { + mExtensionMap = new HashMap<String, List<String>>(); + } + if (!mExtensionMap.containsKey(propName)){ + list = new ArrayList<String>(); + mExtensionMap.put(propName, list); + } else { + list = mExtensionMap.get(propName); + } + + list.add(encodeProperty(propName, paramMap, propValueList)); + } + + private String encodeProperty(String propName, Map<String, Collection<String>> paramMap, + List<String> propValueList) { + // PropertyNode#toString() is for reading, not for parsing in the future. + // We construct appropriate String here. + StringBuilder builder = new StringBuilder(); + if (propName.length() > 0) { + builder.append("propName:["); + builder.append(propName); + builder.append("],"); + } + + if (paramMap.size() > 0) { + builder.append("paramMap:["); + int size = paramMap.size(); + int i = 0; + for (Map.Entry<String, Collection<String>> entry : paramMap.entrySet()) { + String key = entry.getKey(); + for (String value : entry.getValue()) { + // Assuming param-key does not contain NON-ASCII nor symbols. + // TODO: check it. + // + // According to vCard 3.0: + // param-name = iana-token / x-name + builder.append(key); + + // param-value may contain any value including NON-ASCIIs. + // We use the following replacing rule. + // \ -> \\ + // , -> \, + // In String#replaceAll(), "\\\\" means a single backslash. + builder.append("="); + + // TODO: fix this. + builder.append(value.replaceAll("\\\\", "\\\\\\\\").replaceAll(",", "\\\\,")); + if (i < size -1) { + builder.append(","); + } + i++; + } + } + + builder.append("],"); + } + + int size = propValueList.size(); + if (size > 0) { + builder.append("propValue:["); + List<String> list = propValueList; + for (int i = 0; i < size; i++) { + // TODO: fix this. + builder.append(list.get(i).replaceAll("\\\\", "\\\\\\\\").replaceAll(",", "\\\\,")); + if (i < size -1) { + builder.append(","); + } + } + builder.append("],"); + } + + return builder.toString(); + } + + private static String getNameFromNProperty(List<String> elems, int nameOrderType) { + // Family, Given, Middle, Prefix, Suffix. (1 - 5) + int size = elems.size(); + if (size > 1) { + StringBuilder builder = new StringBuilder(); + boolean builderIsEmpty = true; + // Prefix + if (size > 3 && elems.get(3).length() > 0) { + builder.append(elems.get(3)); + builderIsEmpty = false; + } + String first, second; + if (nameOrderType == VCardConfig.NAME_ORDER_TYPE_JAPANESE) { + first = elems.get(0); + second = elems.get(1); + } else { + first = elems.get(1); + second = elems.get(0); + } + if (first.length() > 0) { + if (!builderIsEmpty) { + builder.append(' '); + } + builder.append(first); + builderIsEmpty = false; + } + // Middle name + if (size > 2 && elems.get(2).length() > 0) { + if (!builderIsEmpty) { + builder.append(' '); + } + builder.append(elems.get(2)); + builderIsEmpty = false; + } + if (second.length() > 0) { + if (!builderIsEmpty) { + builder.append(' '); + } + builder.append(second); + builderIsEmpty = false; + } + // Suffix + if (size > 4 && elems.get(4).length() > 0) { + if (!builderIsEmpty) { + builder.append(' '); + } + builder.append(elems.get(4)); + builderIsEmpty = false; + } + return builder.toString(); + } else if (size == 1) { + return elems.get(0); + } else { + return ""; + } + } + + public void addProperty(Property property) { + String propName = property.mPropertyName; + final Map<String, Collection<String>> paramMap = property.mParameterMap; + final List<String> propValueList = property.mPropertyValueList; + byte[] propBytes = property.mPropertyBytes; + + if (propValueList.size() == 0) { + return; + } + + String propValue = listToString(propValueList); + + if (propName.equals("VERSION")) { + // vCard version. Ignore this. + } else if (propName.equals("FN")) { + mTmpFullName = propValue; + } else if (propName.equals("NAME") && mTmpFullName == null) { + // Only in vCard 3.0. Use this if FN does not exist. + // Though, note that vCard 3.0 requires FN. + mTmpFullName = propValue; + } else if (propName.equals("N")) { + mTmpNameFromNProperty = getNameFromNProperty(propValueList, mNameOrderType); + } else if (propName.equals("SORT-STRING")) { + mPhoneticName = propValue; + } else if (propName.equals("SOUND")) { + if ("X-IRMC-N".equals(paramMap.get("TYPE")) && mPhoneticName == null) { + // Some Japanese mobile phones use this field for phonetic name, + // since vCard 2.1 does not have "SORT-STRING" type. + // Also, in some cases, the field has some ';'s in it. + // We remove them. + StringBuilder builder = new StringBuilder(); + String value = propValue; + int length = value.length(); + for (int i = 0; i < length; i++) { + char ch = value.charAt(i); + if (ch != ';') { + builder.append(ch); + } + } + if (builder.length() > 0) { + mPhoneticName = builder.toString(); + } + } else { + addExtension(propName, paramMap, propValueList); + } + } else if (propName.equals("ADR")) { + boolean valuesAreAllEmpty = true; + for (String value : propValueList) { + if (value.length() > 0) { + valuesAreAllEmpty = false; + break; + } + } + if (valuesAreAllEmpty) { + return; + } + + int kind = Contacts.KIND_POSTAL; + int type = -1; + String label = ""; + boolean isPrimary = false; + Collection<String> typeCollection = paramMap.get("TYPE"); + if (typeCollection != null) { + for (String typeString : typeCollection) { + if (typeString.equals("PREF") && !mPrefIsSet_Address) { + // Only first "PREF" is considered. + mPrefIsSet_Address = true; + isPrimary = true; + } else if (typeString.equalsIgnoreCase("HOME")) { + type = Contacts.ContactMethodsColumns.TYPE_HOME; + label = ""; + } else if (typeString.equalsIgnoreCase("WORK") || + typeString.equalsIgnoreCase("COMPANY")) { + // "COMPANY" seems emitted by Windows Mobile, which is not + // specifically supported by vCard 2.1. We assume this is same + // as "WORK". + type = Contacts.ContactMethodsColumns.TYPE_WORK; + label = ""; + } else if (typeString.equalsIgnoreCase("POSTAL")) { + kind = Contacts.KIND_POSTAL; + } else if (typeString.equalsIgnoreCase("PARCEL") || + typeString.equalsIgnoreCase("DOM") || + typeString.equalsIgnoreCase("INTL")) { + // We do not have a kind or type matching these. + // TODO: fix this. We may need to split entries into two. + // (e.g. entries for KIND_POSTAL and KIND_PERCEL) + } else if (typeString.toUpperCase().startsWith("X-") && + type < 0) { + type = Contacts.ContactMethodsColumns.TYPE_CUSTOM; + label = typeString.substring(2); + } else if (type < 0) { + // vCard 3.0 allows iana-token. Also some vCard 2.1 exporters + // emit non-standard types. We do not handle their values now. + type = Contacts.ContactMethodsColumns.TYPE_CUSTOM; + label = typeString; + } + } + } + // We use "HOME" as default + if (type < 0) { + type = Contacts.ContactMethodsColumns.TYPE_HOME; + } + + // adr-value = 0*6(text-value ";") text-value + // ; PO Box, Extended Address, Street, Locality, Region, Postal + // ; Code, Country Name + String address; + int size = propValueList.size(); + if (size > 1) { + StringBuilder builder = new StringBuilder(); + boolean builderIsEmpty = true; + if (Locale.getDefault().getCountry().equals(Locale.JAPAN.getCountry())) { + // In Japan, the order is reversed. + for (int i = size - 1; i >= 0; i--) { + String addressPart = propValueList.get(i); + if (addressPart.length() > 0) { + if (!builderIsEmpty) { + builder.append(' '); + } + builder.append(addressPart); + builderIsEmpty = false; + } + } + } else { + for (int i = 0; i < size; i++) { + String addressPart = propValueList.get(i); + if (addressPart.length() > 0) { + if (!builderIsEmpty) { + builder.append(' '); + } + builder.append(addressPart); + builderIsEmpty = false; + } + } + } + address = builder.toString().trim(); + } else { + address = propValue; + } + addContactmethod(kind, type, address, label, isPrimary); + } else if (propName.equals("ORG")) { + // vCard specification does not specify other types. + int type = Contacts.OrganizationColumns.TYPE_WORK; + boolean isPrimary = false; + + Collection<String> typeCollection = paramMap.get("TYPE"); + if (typeCollection != null) { + for (String typeString : typeCollection) { + if (typeString.equals("PREF") && !mPrefIsSet_Organization) { + // vCard specification officially does not have PREF in ORG. + // This is just for safety. + mPrefIsSet_Organization = true; + isPrimary = true; + } + // XXX: Should we cope with X- words? + } + } + + int size = propValueList.size(); + StringBuilder builder = new StringBuilder(); + for (Iterator<String> iter = propValueList.iterator(); iter.hasNext();) { + builder.append(iter.next()); + if (iter.hasNext()) { + builder.append(' '); + } + } + + addOrganization(type, builder.toString(), "", isPrimary); + } else if (propName.equals("TITLE")) { + setPosition(propValue); + } else if (propName.equals("ROLE")) { + setPosition(propValue); + } else if ((propName.equals("PHOTO") || (propName.equals("LOGO")) && mPhotoBytes == null)) { + // We prefer PHOTO to LOGO. + Collection<String> paramMapValue = paramMap.get("VALUE"); + if (paramMapValue != null && paramMapValue.contains("URL")) { + // TODO: do something. + } else { + // Assume PHOTO is stored in BASE64. In that case, + // data is already stored in propValue_bytes in binary form. + // It should be automatically done by VBuilder (VDataBuilder/VCardDatabuilder) + mPhotoBytes = propBytes; + /* + Collection<String> typeCollection = paramMap.get("TYPE"); + if (typeCollection != null) { + if (typeCollection.size() > 1) { + StringBuilder builder = new StringBuilder(); + int size = typeCollection.size(); + int i = 0; + for (String type : typeCollection) { + builder.append(type); + if (i < size - 1) { + builder.append(','); + } + i++; + } + Log.w(LOG_TAG, "There is more than TYPE: " + builder.toString()); + } + mPhotoType = typeCollection.iterator().next(); + }*/ + } + } else if (propName.equals("EMAIL")) { + int type = -1; + String label = null; + boolean isPrimary = false; + Collection<String> typeCollection = paramMap.get("TYPE"); + if (typeCollection != null) { + for (String typeString : typeCollection) { + if (typeString.equals("PREF") && !mPrefIsSet_Email) { + // Only first "PREF" is considered. + mPrefIsSet_Email = true; + isPrimary = true; + } else if (typeString.equalsIgnoreCase("HOME")) { + type = Contacts.ContactMethodsColumns.TYPE_HOME; + } else if (typeString.equalsIgnoreCase("WORK")) { + type = Contacts.ContactMethodsColumns.TYPE_WORK; + } else if (typeString.equalsIgnoreCase("CELL")) { + // We do not have Contacts.ContactMethodsColumns.TYPE_MOBILE yet. + type = Contacts.ContactMethodsColumns.TYPE_CUSTOM; + label = Contacts.ContactMethodsColumns.MOBILE_EMAIL_TYPE_NAME; + } else if (typeString.toUpperCase().startsWith("X-") && + type < 0) { + type = Contacts.ContactMethodsColumns.TYPE_CUSTOM; + label = typeString.substring(2); + } else if (type < 0) { + // vCard 3.0 allows iana-token. + // We may have INTERNET (specified in vCard spec), + // SCHOOL, etc. + type = Contacts.ContactMethodsColumns.TYPE_CUSTOM; + label = typeString; + } + } + } + if (type < 0) { + type = Contacts.ContactMethodsColumns.TYPE_OTHER; + } + addContactmethod(Contacts.KIND_EMAIL, type, propValue,label, isPrimary); + } else if (propName.equals("TEL")) { + int type = -1; + String label = null; + boolean isPrimary = false; + boolean isFax = false; + Collection<String> typeCollection = paramMap.get("TYPE"); + if (typeCollection != null) { + for (String typeString : typeCollection) { + if (typeString.equals("PREF") && !mPrefIsSet_Phone) { + // Only first "PREF" is considered. + mPrefIsSet_Phone = true; + isPrimary = true; + } else if (typeString.equalsIgnoreCase("HOME")) { + type = Contacts.PhonesColumns.TYPE_HOME; + } else if (typeString.equalsIgnoreCase("WORK")) { + type = Contacts.PhonesColumns.TYPE_WORK; + } else if (typeString.equalsIgnoreCase("CELL")) { + type = Contacts.PhonesColumns.TYPE_MOBILE; + } else if (typeString.equalsIgnoreCase("PAGER")) { + type = Contacts.PhonesColumns.TYPE_PAGER; + } else if (typeString.equalsIgnoreCase("FAX")) { + isFax = true; + } else if (typeString.equalsIgnoreCase("VOICE") || + typeString.equalsIgnoreCase("MSG")) { + // Defined in vCard 3.0. Ignore these because they + // conflict with "HOME", "WORK", etc. + // XXX: do something? + } else if (typeString.toUpperCase().startsWith("X-") && + type < 0) { + type = Contacts.PhonesColumns.TYPE_CUSTOM; + label = typeString.substring(2); + } else if (type < 0){ + // We may have MODEM, CAR, ISDN, etc... + type = Contacts.PhonesColumns.TYPE_CUSTOM; + label = typeString; + } + } + } + if (type < 0) { + type = Contacts.PhonesColumns.TYPE_HOME; + } + if (isFax) { + if (type == Contacts.PhonesColumns.TYPE_HOME) { + type = Contacts.PhonesColumns.TYPE_FAX_HOME; + } else if (type == Contacts.PhonesColumns.TYPE_WORK) { + type = Contacts.PhonesColumns.TYPE_FAX_WORK; + } + } + + addPhone(type, propValue, label, isPrimary); + } else if (propName.equals("NOTE")) { + if (mNotes == null) { + mNotes = new ArrayList<String>(1); + } + mNotes.add(propValue); + } else if (propName.equals("BDAY")) { + addExtension(propName, paramMap, propValueList); + } else if (propName.equals("URL")) { + addExtension(propName, paramMap, propValueList); + } else if (propName.equals("REV")) { + // Revision of this VCard entry. I think we can ignore this. + addExtension(propName, paramMap, propValueList); + } else if (propName.equals("UID")) { + addExtension(propName, paramMap, propValueList); + } else if (propName.equals("KEY")) { + // Type is X509 or PGP? I don't know how to handle this... + addExtension(propName, paramMap, propValueList); + } else if (propName.equals("MAILER")) { + addExtension(propName, paramMap, propValueList); + } else if (propName.equals("TZ")) { + addExtension(propName, paramMap, propValueList); + } else if (propName.equals("GEO")) { + addExtension(propName, paramMap, propValueList); + } else if (propName.equals("NICKNAME")) { + // vCard 3.0 only. + addExtension(propName, paramMap, propValueList); + } else if (propName.equals("CLASS")) { + // vCard 3.0 only. + // e.g. CLASS:CONFIDENTIAL + addExtension(propName, paramMap, propValueList); + } else if (propName.equals("PROFILE")) { + // VCard 3.0 only. Must be "VCARD". I think we can ignore this. + addExtension(propName, paramMap, propValueList); + } else if (propName.equals("CATEGORIES")) { + // VCard 3.0 only. + // e.g. CATEGORIES:INTERNET,IETF,INDUSTRY,INFORMATION TECHNOLOGY + addExtension(propName, paramMap, propValueList); + } else if (propName.equals("SOURCE")) { + // VCard 3.0 only. + addExtension(propName, paramMap, propValueList); + } else if (propName.equals("PRODID")) { + // VCard 3.0 only. + // To specify the identifier for the product that created + // the vCard object. + addExtension(propName, paramMap, propValueList); + } else if (propName.equals("X-PHONETIC-FIRST-NAME")) { + mTmpXPhoneticFirstName = propValue; + } else if (propName.equals("X-PHONETIC-MIDDLE-NAME")) { + mTmpXPhoneticMiddleName = propValue; + } else if (propName.equals("X-PHONETIC-LAST-NAME")) { + mTmpXPhoneticLastName = propValue; + } else { + // Unknown X- words and IANA token. + addExtension(propName, paramMap, propValueList); + } + } + + public String displayString() { + if (mName.length() > 0) { + return mName; + } + if (mContactMethodList != null && mContactMethodList.size() > 0) { + for (ContactMethod contactMethod : mContactMethodList) { + if (contactMethod.kind == Contacts.KIND_EMAIL && contactMethod.isPrimary) { + return contactMethod.data; + } + } + } + if (mPhoneList != null && mPhoneList.size() > 0) { + for (PhoneData phoneData : mPhoneList) { + if (phoneData.isPrimary) { + return phoneData.data; + } + } + } + return ""; + } + + /** + * Consolidate several fielsds (like mName) using name candidates, + */ + public void consolidateFields() { + if (mTmpFullName != null) { + mName = mTmpFullName; + } else if(mTmpNameFromNProperty != null) { + mName = mTmpNameFromNProperty; + } else { + mName = ""; + } + + if (mPhoneticName == null && + (mTmpXPhoneticFirstName != null || mTmpXPhoneticMiddleName != null || + mTmpXPhoneticLastName != null)) { + // Note: In Europe, this order should be "LAST FIRST MIDDLE". See the comment around + // NAME_ORDER_TYPE_* for more detail. + String first; + String second; + if (mNameOrderType == VCardConfig.NAME_ORDER_TYPE_JAPANESE) { + first = mTmpXPhoneticLastName; + second = mTmpXPhoneticFirstName; + } else { + first = mTmpXPhoneticFirstName; + second = mTmpXPhoneticLastName; + } + StringBuilder builder = new StringBuilder(); + if (first != null) { + builder.append(first); + } + if (mTmpXPhoneticMiddleName != null) { + builder.append(mTmpXPhoneticMiddleName); + } + if (second != null) { + builder.append(second); + } + mPhoneticName = builder.toString(); + } + + // Remove unnecessary white spaces. + // It is found that some mobile phone emits phonetic name with just one white space + // when a user does not specify one. + // This logic is effective toward such kind of weird data. + if (mPhoneticName != null) { + mPhoneticName = mPhoneticName.trim(); + } + + // If there is no "PREF", we choose the first entries as primary. + if (!mPrefIsSet_Phone && mPhoneList != null && mPhoneList.size() > 0) { + mPhoneList.get(0).isPrimary = true; + } + + if (!mPrefIsSet_Address && mContactMethodList != null) { + for (ContactMethod contactMethod : mContactMethodList) { + if (contactMethod.kind == Contacts.KIND_POSTAL) { + contactMethod.isPrimary = true; + break; + } + } + } + if (!mPrefIsSet_Email && mContactMethodList != null) { + for (ContactMethod contactMethod : mContactMethodList) { + if (contactMethod.kind == Contacts.KIND_EMAIL) { + contactMethod.isPrimary = true; + break; + } + } + } + if (!mPrefIsSet_Organization && mOrganizationList != null && mOrganizationList.size() > 0) { + mOrganizationList.get(0).isPrimary = true; + } + + } + + private void pushIntoContentProviderOrResolver(Object contentSomething, + long myContactsGroupId) { + ContentResolver resolver = null; + AbstractSyncableContentProvider provider = null; + if (contentSomething instanceof ContentResolver) { + resolver = (ContentResolver)contentSomething; + } else if (contentSomething instanceof AbstractSyncableContentProvider) { + provider = (AbstractSyncableContentProvider)contentSomething; + } else { + Log.e(LOG_TAG, "Unsupported object came."); + return; + } + + ContentValues contentValues = new ContentValues(); + contentValues.put(People.NAME, mName); + contentValues.put(People.PHONETIC_NAME, mPhoneticName); + + if (mNotes != null && mNotes.size() > 0) { + if (mNotes.size() > 1) { + StringBuilder builder = new StringBuilder(); + for (String note : mNotes) { + builder.append(note); + builder.append("\n"); + } + contentValues.put(People.NOTES, builder.toString()); + } else { + contentValues.put(People.NOTES, mNotes.get(0)); + } + } + + Uri personUri; + long personId = 0; + if (resolver != null) { + personUri = Contacts.People.createPersonInMyContactsGroup(resolver, contentValues); + if (personUri != null) { + personId = ContentUris.parseId(personUri); + } + } else { + personUri = provider.insert(People.CONTENT_URI, contentValues); + if (personUri != null) { + personId = ContentUris.parseId(personUri); + ContentValues values = new ContentValues(); + values.put(GroupMembership.PERSON_ID, personId); + values.put(GroupMembership.GROUP_ID, myContactsGroupId); + Uri resultUri = provider.insert(GroupMembership.CONTENT_URI, values); + if (resultUri == null) { + Log.e(LOG_TAG, "Faild to insert the person to MyContact."); + provider.delete(personUri, null, null); + personUri = null; + } + } + } + + if (personUri == null) { + Log.e(LOG_TAG, "Failed to create the contact."); + return; + } + + if (mPhotoBytes != null) { + if (resolver != null) { + People.setPhotoData(resolver, personUri, mPhotoBytes); + } else { + Uri photoUri = Uri.withAppendedPath(personUri, Contacts.Photos.CONTENT_DIRECTORY); + ContentValues values = new ContentValues(); + values.put(Photos.DATA, mPhotoBytes); + provider.update(photoUri, values, null, null); + } + } + + long primaryPhoneId = -1; + if (mPhoneList != null && mPhoneList.size() > 0) { + for (PhoneData phoneData : mPhoneList) { + ContentValues values = new ContentValues(); + values.put(Contacts.PhonesColumns.TYPE, phoneData.type); + if (phoneData.type == Contacts.PhonesColumns.TYPE_CUSTOM) { + values.put(Contacts.PhonesColumns.LABEL, phoneData.label); + } + // Already formatted. + values.put(Contacts.PhonesColumns.NUMBER, phoneData.data); + + // Not sure about Contacts.PhonesColumns.NUMBER_KEY ... + values.put(Contacts.PhonesColumns.ISPRIMARY, 1); + values.put(Contacts.Phones.PERSON_ID, personId); + Uri phoneUri; + if (resolver != null) { + phoneUri = resolver.insert(Phones.CONTENT_URI, values); + } else { + phoneUri = provider.insert(Phones.CONTENT_URI, values); + } + if (phoneData.isPrimary) { + primaryPhoneId = Long.parseLong(phoneUri.getLastPathSegment()); + } + } + } + + long primaryOrganizationId = -1; + if (mOrganizationList != null && mOrganizationList.size() > 0) { + for (OrganizationData organizationData : mOrganizationList) { + ContentValues values = new ContentValues(); + // Currently, we do not use TYPE_CUSTOM. + values.put(Contacts.OrganizationColumns.TYPE, + organizationData.type); + values.put(Contacts.OrganizationColumns.COMPANY, + organizationData.companyName); + values.put(Contacts.OrganizationColumns.TITLE, + organizationData.positionName); + values.put(Contacts.OrganizationColumns.ISPRIMARY, 1); + values.put(Contacts.OrganizationColumns.PERSON_ID, personId); + + Uri organizationUri; + if (resolver != null) { + organizationUri = resolver.insert(Organizations.CONTENT_URI, values); + } else { + organizationUri = provider.insert(Organizations.CONTENT_URI, values); + } + if (organizationData.isPrimary) { + primaryOrganizationId = Long.parseLong(organizationUri.getLastPathSegment()); + } + } + } + + long primaryEmailId = -1; + if (mContactMethodList != null && mContactMethodList.size() > 0) { + for (ContactMethod contactMethod : mContactMethodList) { + ContentValues values = new ContentValues(); + values.put(Contacts.ContactMethodsColumns.KIND, contactMethod.kind); + values.put(Contacts.ContactMethodsColumns.TYPE, contactMethod.type); + if (contactMethod.type == Contacts.ContactMethodsColumns.TYPE_CUSTOM) { + values.put(Contacts.ContactMethodsColumns.LABEL, contactMethod.label); + } + values.put(Contacts.ContactMethodsColumns.DATA, contactMethod.data); + values.put(Contacts.ContactMethodsColumns.ISPRIMARY, 1); + values.put(Contacts.ContactMethods.PERSON_ID, personId); + + if (contactMethod.kind == Contacts.KIND_EMAIL) { + Uri emailUri; + if (resolver != null) { + emailUri = resolver.insert(ContactMethods.CONTENT_URI, values); + } else { + emailUri = provider.insert(ContactMethods.CONTENT_URI, values); + } + if (contactMethod.isPrimary) { + primaryEmailId = Long.parseLong(emailUri.getLastPathSegment()); + } + } else { // probably KIND_POSTAL + if (resolver != null) { + resolver.insert(ContactMethods.CONTENT_URI, values); + } else { + provider.insert(ContactMethods.CONTENT_URI, values); + } + } + } + } + + if (mExtensionMap != null && mExtensionMap.size() > 0) { + ArrayList<ContentValues> contentValuesArray; + if (resolver != null) { + contentValuesArray = new ArrayList<ContentValues>(); + } else { + contentValuesArray = null; + } + for (Entry<String, List<String>> entry : mExtensionMap.entrySet()) { + String key = entry.getKey(); + List<String> list = entry.getValue(); + for (String value : list) { + ContentValues values = new ContentValues(); + values.put(Extensions.NAME, key); + values.put(Extensions.VALUE, value); + values.put(Extensions.PERSON_ID, personId); + if (resolver != null) { + contentValuesArray.add(values); + } else { + provider.insert(Extensions.CONTENT_URI, values); + } + } + } + if (resolver != null) { + resolver.bulkInsert(Extensions.CONTENT_URI, + contentValuesArray.toArray(new ContentValues[0])); + } + } + + if (primaryPhoneId >= 0 || primaryOrganizationId >= 0 || primaryEmailId >= 0) { + ContentValues values = new ContentValues(); + if (primaryPhoneId >= 0) { + values.put(People.PRIMARY_PHONE_ID, primaryPhoneId); + } + if (primaryOrganizationId >= 0) { + values.put(People.PRIMARY_ORGANIZATION_ID, primaryOrganizationId); + } + if (primaryEmailId >= 0) { + values.put(People.PRIMARY_EMAIL_ID, primaryEmailId); + } + if (resolver != null) { + resolver.update(personUri, values, null, null); + } else { + provider.update(personUri, values, null, null); + } + } + } + + /** + * Push this object into database in the resolver. + */ + public void pushIntoContentResolver(ContentResolver resolver) { + pushIntoContentProviderOrResolver(resolver, 0); + } + + /** + * Push this object into AbstractSyncableContentProvider object. + * {@link #consolidateFields() must be called before this method is called} + * @hide + */ + public void pushIntoAbstractSyncableContentProvider( + AbstractSyncableContentProvider provider, long myContactsGroupId) { + boolean successful = false; + provider.beginBatch(); + try { + pushIntoContentProviderOrResolver(provider, myContactsGroupId); + successful = true; + } finally { + provider.endBatch(successful); + } + } + + public boolean isIgnorable() { + return TextUtils.isEmpty(mName) && + TextUtils.isEmpty(mPhoneticName) && + (mPhoneList == null || mPhoneList.size() == 0) && + (mContactMethodList == null || mContactMethodList.size() == 0); + } + + private String listToString(List<String> list){ + final int size = list.size(); + if (size > 1) { + StringBuilder builder = new StringBuilder(); + int i = 0; + for (String type : list) { + builder.append(type); + if (i < size - 1) { + builder.append(";"); + } + } + return builder.toString(); + } else if (size == 1) { + return list.get(0); + } else { + return ""; + } + } +} diff --git a/core/java/android/pim/vcard/EntryCommitter.java b/core/java/android/pim/vcard/EntryCommitter.java new file mode 100644 index 000000000000..e26fac5dbd70 --- /dev/null +++ b/core/java/android/pim/vcard/EntryCommitter.java @@ -0,0 +1,88 @@ +/* + * 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.pim.vcard; + +import android.content.AbstractSyncableContentProvider; +import android.content.ContentProvider; +import android.content.ContentResolver; +import android.content.IContentProvider; +import android.provider.Contacts; +import android.util.Log; + +/** + * EntryHandler implementation which commits the entry to Contacts Provider + */ +public class EntryCommitter implements EntryHandler { + public static String LOG_TAG = "vcard.EntryComitter"; + + private ContentResolver mContentResolver; + + // Ideally, this should be ContactsProvider but it seems Class loader cannot find it, + // even when it is subclass of ContactsProvider... + private AbstractSyncableContentProvider mProvider; + private long mMyContactsGroupId; + + private long mTimeToCommit; + + public EntryCommitter(ContentResolver resolver) { + mContentResolver = resolver; + + tryGetOriginalProvider(); + } + + public void onFinal() { + if (VCardConfig.showPerformanceLog()) { + Log.d(LOG_TAG, + String.format("time to commit entries: %ld ms", mTimeToCommit)); + } + } + + private void tryGetOriginalProvider() { + final ContentResolver resolver = mContentResolver; + + if ((mMyContactsGroupId = Contacts.People.tryGetMyContactsGroupId(resolver)) == 0) { + Log.e(LOG_TAG, "Could not get group id of MyContact"); + return; + } + + IContentProvider iProviderForName = resolver.acquireProvider(Contacts.CONTENT_URI); + ContentProvider contentProvider = + ContentProvider.coerceToLocalContentProvider(iProviderForName); + if (contentProvider == null) { + Log.e(LOG_TAG, "Fail to get ContentProvider object."); + return; + } + + if (!(contentProvider instanceof AbstractSyncableContentProvider)) { + Log.e(LOG_TAG, + "Acquired ContentProvider object is not AbstractSyncableContentProvider."); + return; + } + + mProvider = (AbstractSyncableContentProvider)contentProvider; + } + + public void onEntryCreated(final ContactStruct contactStruct) { + long start = System.currentTimeMillis(); + if (mProvider != null) { + contactStruct.pushIntoAbstractSyncableContentProvider( + mProvider, mMyContactsGroupId); + } else { + contactStruct.pushIntoContentResolver(mContentResolver); + } + mTimeToCommit += System.currentTimeMillis() - start; + } +}
\ No newline at end of file diff --git a/core/java/android/pim/vcard/EntryHandler.java b/core/java/android/pim/vcard/EntryHandler.java new file mode 100644 index 000000000000..4015cb5b98b0 --- /dev/null +++ b/core/java/android/pim/vcard/EntryHandler.java @@ -0,0 +1,33 @@ +/* + * 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.pim.vcard; + +/** + * Unlike VCardBuilderBase, this (and VCardDataBuilder) assumes + * "each VCard entry should be correctly parsed and passed to each EntryHandler object", + */ +public interface EntryHandler { + /** + * Able to be use this method for showing performance log, etc. + * TODO: better name? + */ + public void onFinal(); + + /** + * The method called when one VCard entry is successfully created + */ + public void onEntryCreated(final ContactStruct entry); +} diff --git a/core/java/android/pim/vcard/VCardBuilder.java b/core/java/android/pim/vcard/VCardBuilder.java new file mode 100644 index 000000000000..e1c4b338e501 --- /dev/null +++ b/core/java/android/pim/vcard/VCardBuilder.java @@ -0,0 +1,64 @@ +/* + * 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.pim.vcard; + +import java.util.List; + +public interface VCardBuilder { + void start(); + + void end(); + + /** + * BEGIN:VCARD + */ + void startRecord(String type); + + /** END:VXX */ + void endRecord(); + + void startProperty(); + + void endProperty(); + + /** + * @param group + */ + void propertyGroup(String group); + + /** + * @param name + * N <br> + * N + */ + void propertyName(String name); + + /** + * @param type + * LANGUAGE \ ENCODING <br> + * ;LANGUage= \ ;ENCODING= + */ + void propertyParamType(String type); + + /** + * @param value + * FR-EN \ GBK <br> + * FR-EN \ GBK + */ + void propertyParamValue(String value); + + void propertyValues(List<String> values); +} diff --git a/core/java/android/pim/vcard/VCardBuilderCollection.java b/core/java/android/pim/vcard/VCardBuilderCollection.java new file mode 100644 index 000000000000..e3985b600e69 --- /dev/null +++ b/core/java/android/pim/vcard/VCardBuilderCollection.java @@ -0,0 +1,99 @@ +/* + * 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.pim.vcard; + +import java.util.Collection; +import java.util.List; + +public class VCardBuilderCollection implements VCardBuilder { + + private final Collection<VCardBuilder> mVCardBuilderCollection; + + public VCardBuilderCollection(Collection<VCardBuilder> vBuilderCollection) { + mVCardBuilderCollection = vBuilderCollection; + } + + public Collection<VCardBuilder> getVCardBuilderBaseCollection() { + return mVCardBuilderCollection; + } + + public void start() { + for (VCardBuilder builder : mVCardBuilderCollection) { + builder.start(); + } + } + + public void end() { + for (VCardBuilder builder : mVCardBuilderCollection) { + builder.end(); + } + } + + public void startRecord(String type) { + for (VCardBuilder builder : mVCardBuilderCollection) { + builder.startRecord(type); + } + } + + public void endRecord() { + for (VCardBuilder builder : mVCardBuilderCollection) { + builder.endRecord(); + } + } + + public void startProperty() { + for (VCardBuilder builder : mVCardBuilderCollection) { + builder.startProperty(); + } + } + + + public void endProperty() { + for (VCardBuilder builder : mVCardBuilderCollection) { + builder.endProperty(); + } + } + + public void propertyGroup(String group) { + for (VCardBuilder builder : mVCardBuilderCollection) { + builder.propertyGroup(group); + } + } + + public void propertyName(String name) { + for (VCardBuilder builder : mVCardBuilderCollection) { + builder.propertyName(name); + } + } + + public void propertyParamType(String type) { + for (VCardBuilder builder : mVCardBuilderCollection) { + builder.propertyParamType(type); + } + } + + public void propertyParamValue(String value) { + for (VCardBuilder builder : mVCardBuilderCollection) { + builder.propertyParamValue(value); + } + } + + public void propertyValues(List<String> values) { + for (VCardBuilder builder : mVCardBuilderCollection) { + builder.propertyValues(values); + } + } +} diff --git a/core/java/android/pim/vcard/VCardConfig.java b/core/java/android/pim/vcard/VCardConfig.java new file mode 100644 index 000000000000..fef9dba4cc32 --- /dev/null +++ b/core/java/android/pim/vcard/VCardConfig.java @@ -0,0 +1,59 @@ +/* + * 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.pim.vcard; + +/** + * The class representing VCard related configurations + */ +public class VCardConfig { + static final int LOG_LEVEL_NONE = 0; + static final int LOG_LEVEL_PERFORMANCE_MEASUREMENT = 0x1; + static final int LOG_LEVEL_SHOW_WARNING = 0x2; + static final int LOG_LEVEL_VERBOSE = + LOG_LEVEL_PERFORMANCE_MEASUREMENT | LOG_LEVEL_SHOW_WARNING; + + // Assumes that "iso-8859-1" is able to map "all" 8bit characters to some unicode and + // decode the unicode to the original charset. If not, this setting will cause some bug. + public static final String DEFAULT_CHARSET = "iso-8859-1"; + + // TODO: use this flag + public static boolean IGNORE_CASE_EXCEPT_VALUE = true; + + protected static final int LOG_LEVEL = LOG_LEVEL_PERFORMANCE_MEASUREMENT; + + // Note: phonetic name probably should be "LAST FIRST MIDDLE" for European languages, and + // space should be added between each element while it should not be in Japanese. + // But unfortunately, we currently do not have the data and are not sure whether we should + // support European version of name ordering. + // + // TODO: Implement the logic described above if we really need European version of + // phonetic name handling. Also, adding the appropriate test case of vCard would be + // highly appreciated. + public static final int NAME_ORDER_TYPE_ENGLISH = 0; + public static final int NAME_ORDER_TYPE_JAPANESE = 1; + + public static final int NAME_ORDER_TYPE_DEFAULT = NAME_ORDER_TYPE_ENGLISH; + + /** + * @hide temporal. may be deleted + */ + public static boolean showPerformanceLog() { + return (LOG_LEVEL & LOG_LEVEL_PERFORMANCE_MEASUREMENT) != 0; + } + + private VCardConfig() { + } +}
\ No newline at end of file diff --git a/core/java/android/pim/vcard/VCardDataBuilder.java b/core/java/android/pim/vcard/VCardDataBuilder.java new file mode 100644 index 000000000000..4025f6c164ca --- /dev/null +++ b/core/java/android/pim/vcard/VCardDataBuilder.java @@ -0,0 +1,319 @@ +/* + * 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.pim.vcard; + +import android.util.CharsetUtils; +import android.util.Log; + +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.net.QuotedPrintableCodec; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * VBuilder for VCard. VCard may contain big photo images encoded by BASE64, + * If we store all VNode entries in memory like VDataBuilder.java, + * OutOfMemoryError may be thrown. Thus, this class push each VCard entry into + * ContentResolver immediately. + */ +public class VCardDataBuilder implements VCardBuilder { + static private String LOG_TAG = "VCardDataBuilder"; + + /** + * If there's no other information available, this class uses this charset for encoding + * byte arrays. + */ + static public String TARGET_CHARSET = "UTF-8"; + + private ContactStruct.Property mCurrentProperty = new ContactStruct.Property(); + private ContactStruct mCurrentContactStruct; + private String mParamType; + + /** + * The charset using which VParser parses the text. + */ + private String mSourceCharset; + + /** + * The charset with which byte array is encoded to String. + */ + private String mTargetCharset; + private boolean mStrictLineBreakParsing; + + private int mNameOrderType; + + // Just for testing. + private long mTimePushIntoContentResolver; + + private List<EntryHandler> mEntryHandlers = new ArrayList<EntryHandler>(); + + public VCardDataBuilder() { + this(null, null, false, VCardConfig.NAME_ORDER_TYPE_DEFAULT); + } + + /** + * @hide + */ + public VCardDataBuilder(int nameOrderType) { + this(null, null, false, nameOrderType); + } + + /** + * @hide + */ + public VCardDataBuilder(String charset, + boolean strictLineBreakParsing, + int nameOrderType) { + this(null, charset, strictLineBreakParsing, nameOrderType); + } + + /** + * @hide + */ + public VCardDataBuilder(String sourceCharset, + String targetCharset, + boolean strictLineBreakParsing, + int nameOrderType) { + if (sourceCharset != null) { + mSourceCharset = sourceCharset; + } else { + mSourceCharset = VCardConfig.DEFAULT_CHARSET; + } + if (targetCharset != null) { + mTargetCharset = targetCharset; + } else { + mTargetCharset = TARGET_CHARSET; + } + mStrictLineBreakParsing = strictLineBreakParsing; + mNameOrderType = nameOrderType; + } + + public void addEntryHandler(EntryHandler entryHandler) { + mEntryHandlers.add(entryHandler); + } + + public void start() { + } + + public void end() { + for (EntryHandler entryHandler : mEntryHandlers) { + entryHandler.onFinal(); + } + } + + /** + * Assume that VCard is not nested. In other words, this code does not accept + */ + public void startRecord(String type) { + // TODO: add the method clear() instead of using null for reducing GC? + if (mCurrentContactStruct != null) { + // This means startRecord() is called inside startRecord() - endRecord() block. + // TODO: should throw some Exception + Log.e(LOG_TAG, "Nested VCard code is not supported now."); + } + if (!type.equalsIgnoreCase("VCARD")) { + // TODO: add test case for this + Log.e(LOG_TAG, "This is not VCARD!"); + } + + mCurrentContactStruct = new ContactStruct(mNameOrderType); + } + + public void endRecord() { + mCurrentContactStruct.consolidateFields(); + for (EntryHandler entryHandler : mEntryHandlers) { + entryHandler.onEntryCreated(mCurrentContactStruct); + } + mCurrentContactStruct = null; + } + + public void startProperty() { + mCurrentProperty.clear(); + } + + public void endProperty() { + mCurrentContactStruct.addProperty(mCurrentProperty); + } + + public void propertyName(String name) { + mCurrentProperty.setPropertyName(name); + } + + public void propertyGroup(String group) { + // ContactStruct does not support Group. + } + + public void propertyParamType(String type) { + if (mParamType != null) { + Log.e(LOG_TAG, + "propertyParamType() is called more than once " + + "before propertyParamValue() is called"); + } + mParamType = type; + } + + public void propertyParamValue(String value) { + if (mParamType == null) { + mParamType = "TYPE"; + } + mCurrentProperty.addParameter(mParamType, value); + mParamType = null; + } + + private String encodeString(String originalString, String targetCharset) { + if (mSourceCharset.equalsIgnoreCase(targetCharset)) { + return originalString; + } + Charset charset = Charset.forName(mSourceCharset); + ByteBuffer byteBuffer = charset.encode(originalString); + // byteBuffer.array() "may" return byte array which is larger than + // byteBuffer.remaining(). Here, we keep on the safe side. + byte[] bytes = new byte[byteBuffer.remaining()]; + byteBuffer.get(bytes); + try { + return new String(bytes, targetCharset); + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); + return null; + } + } + + private String handleOneValue(String value, String targetCharset, String encoding) { + if (encoding != null) { + if (encoding.equals("BASE64") || encoding.equals("B")) { + mCurrentProperty.setPropertyBytes(Base64.decodeBase64(value.getBytes())); + return value; + } else if (encoding.equals("QUOTED-PRINTABLE")) { + // "= " -> " ", "=\t" -> "\t". + // Previous code had done this replacement. Keep on the safe side. + StringBuilder builder = new StringBuilder(); + int length = value.length(); + for (int i = 0; i < length; i++) { + char ch = value.charAt(i); + if (ch == '=' && i < length - 1) { + char nextCh = value.charAt(i + 1); + if (nextCh == ' ' || nextCh == '\t') { + + builder.append(nextCh); + i++; + continue; + } + } + builder.append(ch); + } + String quotedPrintable = builder.toString(); + + String[] lines; + if (mStrictLineBreakParsing) { + lines = quotedPrintable.split("\r\n"); + } else { + builder = new StringBuilder(); + length = quotedPrintable.length(); + ArrayList<String> list = new ArrayList<String>(); + for (int i = 0; i < length; i++) { + char ch = quotedPrintable.charAt(i); + if (ch == '\n') { + list.add(builder.toString()); + builder = new StringBuilder(); + } else if (ch == '\r') { + list.add(builder.toString()); + builder = new StringBuilder(); + if (i < length - 1) { + char nextCh = quotedPrintable.charAt(i + 1); + if (nextCh == '\n') { + i++; + } + } + } else { + builder.append(ch); + } + } + String finalLine = builder.toString(); + if (finalLine.length() > 0) { + list.add(finalLine); + } + lines = list.toArray(new String[0]); + } + + builder = new StringBuilder(); + for (String line : lines) { + if (line.endsWith("=")) { + line = line.substring(0, line.length() - 1); + } + builder.append(line); + } + byte[] bytes; + try { + bytes = builder.toString().getBytes(mSourceCharset); + } catch (UnsupportedEncodingException e1) { + Log.e(LOG_TAG, "Failed to encode: charset=" + mSourceCharset); + bytes = builder.toString().getBytes(); + } + + try { + bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes); + } catch (DecoderException e) { + Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e); + return ""; + } + + try { + return new String(bytes, targetCharset); + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); + return new String(bytes); + } + } + // Unknown encoding. Fall back to default. + } + return encodeString(value, targetCharset); + } + + public void propertyValues(List<String> values) { + if (values == null || values.size() == 0) { + return; + } + + final Collection<String> charsetCollection = mCurrentProperty.getParameters("CHARSET"); + String charset = + ((charsetCollection != null) ? charsetCollection.iterator().next() : null); + String targetCharset = CharsetUtils.nameForDefaultVendor(charset); + + final Collection<String> encodingCollection = mCurrentProperty.getParameters("ENCODING"); + String encoding = + ((encodingCollection != null) ? encodingCollection.iterator().next() : null); + + if (targetCharset == null || targetCharset.length() == 0) { + targetCharset = mTargetCharset; + } + + for (String value : values) { + mCurrentProperty.addToPropertyValueList( + handleOneValue(value, targetCharset, encoding)); + } + } + + public void showPerformanceInfo() { + Log.d(LOG_TAG, "time for insert ContactStruct to database: " + + mTimePushIntoContentResolver + " ms"); + } +} diff --git a/core/java/android/pim/vcard/VCardEntryCounter.java b/core/java/android/pim/vcard/VCardEntryCounter.java new file mode 100644 index 000000000000..f99b46c83ede --- /dev/null +++ b/core/java/android/pim/vcard/VCardEntryCounter.java @@ -0,0 +1,60 @@ +/* + * 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.pim.vcard; + +import java.util.List; + +public class VCardEntryCounter implements VCardBuilder { + private int mCount; + + public int getCount() { + return mCount; + } + + public void start() { + } + + public void end() { + } + + public void startRecord(String type) { + } + + public void endRecord() { + mCount++; + } + + public void startProperty() { + } + + public void endProperty() { + } + + public void propertyGroup(String group) { + } + + public void propertyName(String name) { + } + + public void propertyParamType(String type) { + } + + public void propertyParamValue(String value) { + } + + public void propertyValues(List<String> values) { + } +}
\ No newline at end of file diff --git a/core/java/android/pim/vcard/VCardParser.java b/core/java/android/pim/vcard/VCardParser.java new file mode 100644 index 000000000000..b5e504982b36 --- /dev/null +++ b/core/java/android/pim/vcard/VCardParser.java @@ -0,0 +1,90 @@ +/* + * 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.pim.vcard; + +import android.pim.vcard.exception.VCardException; + +import java.io.IOException; +import java.io.InputStream; + +public abstract class VCardParser { + + protected boolean mCanceled; + + /** + * Parses the given stream and send the VCard data into VCardBuilderBase object. + * + * Note that vCard 2.1 specification allows "CHARSET" parameter, and some career sets + * local encoding to it. For example, Japanese phone career uses Shift_JIS, which is + * formally allowed in VCard 2.1, but not recommended in VCard 3.0. In VCard 2.1, + * In some exreme case, some VCard may have different charsets in one VCard (though + * we do not see any device which emits such kind of malicious data) + * + * In order to avoid "misunderstanding" charset as much as possible, this method + * use "ISO-8859-1" for reading the stream. When charset is specified in some property + * (with "CHARSET=..." attribute), the string is decoded to raw bytes and encoded to + * the charset. This method assumes that "ISO-8859-1" has 1 to 1 mapping in all 8bit + * characters, which is not completely sure. In some cases, this "decoding-encoding" + * scheme may fail. To avoid the case, + * + * We recommend you to use VCardSourceDetector and detect which kind of source the + * VCard comes from and explicitly specify a charset using the result. + * + * @param is The source to parse. + * @param builder The VCardBuilderBase object which used to construct data. If you want to + * include multiple VCardBuilderBase objects in this field, consider using + * {#link VCardBuilderCollection} class. + * @return Returns true for success. Otherwise returns false. + * @throws IOException, VCardException + */ + public abstract boolean parse(InputStream is, VCardBuilder builder) + throws IOException, VCardException; + + /** + * The method variants which accept charset. + * + * RFC 2426 "recommends" (not forces) to use UTF-8, so it may be OK to use + * UTF-8 as an encoding when parsing vCard 3.0. But note that some Japanese + * phone uses Shift_JIS as a charset (e.g. W61SH), and another uses + * "CHARSET=SHIFT_JIS", which is explicitly prohibited in vCard 3.0 specification + * (e.g. W53K). + * + * @param is The source to parse. + * @param charset Charset to be used. + * @param builder The VCardBuilderBase object. + * @return Returns true when successful. Otherwise returns false. + * @throws IOException, VCardException + */ + public abstract boolean parse(InputStream is, String charset, VCardBuilder builder) + throws IOException, VCardException; + + /** + * The method variants which tells this object the operation is already canceled. + * XXX: Is this really necessary? + * @hide + */ + public abstract void parse(InputStream is, String charset, + VCardBuilder builder, boolean canceled) + throws IOException, VCardException; + + /** + * Cancel parsing. + * Actual cancel is done after the end of the current one vcard entry parsing. + */ + public void cancel() { + mCanceled = true; + } +} diff --git a/core/java/android/pim/vcard/VCardParser_V21.java b/core/java/android/pim/vcard/VCardParser_V21.java new file mode 100644 index 000000000000..17a138fab981 --- /dev/null +++ b/core/java/android/pim/vcard/VCardParser_V21.java @@ -0,0 +1,948 @@ +/* + * 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.pim.vcard; + +import android.pim.vcard.exception.VCardException; +import android.pim.vcard.exception.VCardNestedException; +import android.pim.vcard.exception.VCardNotSupportedException; +import android.pim.vcard.exception.VCardVersionException; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; + +/** + * This class is used to parse vcard. Please refer to vCard Specification 2.1. + */ +public class VCardParser_V21 extends VCardParser { + private static final String LOG_TAG = "VCardParser_V21"; + + /** Store the known-type */ + private static final HashSet<String> sKnownTypeSet = new HashSet<String>( + Arrays.asList("DOM", "INTL", "POSTAL", "PARCEL", "HOME", "WORK", + "PREF", "VOICE", "FAX", "MSG", "CELL", "PAGER", "BBS", + "MODEM", "CAR", "ISDN", "VIDEO", "AOL", "APPLELINK", + "ATTMAIL", "CIS", "EWORLD", "INTERNET", "IBMMAIL", + "MCIMAIL", "POWERSHARE", "PRODIGY", "TLX", "X400", "GIF", + "CGM", "WMF", "BMP", "MET", "PMB", "DIB", "PICT", "TIFF", + "PDF", "PS", "JPEG", "QTIME", "MPEG", "MPEG2", "AVI", + "WAVE", "AIFF", "PCM", "X509", "PGP")); + + /** Store the known-value */ + private static final HashSet<String> sKnownValueSet = new HashSet<String>( + Arrays.asList("INLINE", "URL", "CONTENT-ID", "CID")); + + /** Store the property names available in vCard 2.1 */ + private static final HashSet<String> sAvailablePropertyNameV21 = + new HashSet<String>(Arrays.asList( + "BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND", + "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL", + "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER")); + + // Though vCard 2.1 specification does not allow "B" encoding, some data may have it. + // We allow it for safety... + private static final HashSet<String> sAvailableEncodingV21 = + new HashSet<String>(Arrays.asList( + "7BIT", "8BIT", "QUOTED-PRINTABLE", "BASE64", "B")); + + // Used only for parsing END:VCARD. + private String mPreviousLine; + + /** The builder to build parsed data */ + protected VCardBuilder mBuilder = null; + + /** The encoding type */ + protected String mEncoding = null; + + protected final String sDefaultEncoding = "8BIT"; + + // Should not directly read a line from this. Use getLine() instead. + protected BufferedReader mReader; + + // In some cases, vCard is nested. Currently, we only consider the most interior vCard data. + // See v21_foma_1.vcf in test directory for more information. + private int mNestCount; + + // In order to reduce warning message as much as possible, we hold the value which made Logger + // emit a warning message. + protected HashSet<String> mWarningValueMap = new HashSet<String>(); + + // Just for debugging + private long mTimeTotal; + private long mTimeStartRecord; + private long mTimeEndRecord; + private long mTimeStartProperty; + private long mTimeEndProperty; + private long mTimeParseItems; + private long mTimeParseItem1; + private long mTimeParseItem2; + private long mTimeParseItem3; + private long mTimeHandlePropertyValue1; + private long mTimeHandlePropertyValue2; + private long mTimeHandlePropertyValue3; + + /** + * Create a new VCard parser. + */ + public VCardParser_V21() { + super(); + } + + public VCardParser_V21(VCardSourceDetector detector) { + super(); + if (detector != null && detector.getType() == VCardSourceDetector.TYPE_FOMA) { + mNestCount = 1; + } + } + + /** + * Parse the file at the given position + * vcard_file = [wsls] vcard [wsls] + */ + protected void parseVCardFile() throws IOException, VCardException { + boolean firstReading = true; + while (true) { + if (mCanceled) { + break; + } + if (!parseOneVCard(firstReading)) { + break; + } + firstReading = false; + } + + if (mNestCount > 0) { + boolean useCache = true; + for (int i = 0; i < mNestCount; i++) { + readEndVCard(useCache, true); + useCache = false; + } + } + } + + protected String getVersion() { + return "2.1"; + } + + /** + * @return true when the propertyName is a valid property name. + */ + protected boolean isValidPropertyName(String propertyName) { + if (!(sAvailablePropertyNameV21.contains(propertyName.toUpperCase()) || + propertyName.startsWith("X-")) && + !mWarningValueMap.contains(propertyName)) { + mWarningValueMap.add(propertyName); + Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName); + } + return true; + } + + /** + * @return true when the encoding is a valid encoding. + */ + protected boolean isValidEncoding(String encoding) { + return sAvailableEncodingV21.contains(encoding.toUpperCase()); + } + + /** + * @return String. It may be null, or its length may be 0 + * @throws IOException + */ + protected String getLine() throws IOException { + return mReader.readLine(); + } + + /** + * @return String with it's length > 0 + * @throws IOException + * @throws VCardException when the stream reached end of line + */ + protected String getNonEmptyLine() throws IOException, VCardException { + String line; + while (true) { + line = getLine(); + if (line == null) { + throw new VCardException("Reached end of buffer."); + } else if (line.trim().length() > 0) { + return line; + } + } + } + + /** + * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF + * items *CRLF + * "END" [ws] ":" [ws] "VCARD" + */ + private boolean parseOneVCard(boolean firstReading) throws IOException, VCardException { + boolean allowGarbage = false; + if (firstReading) { + if (mNestCount > 0) { + for (int i = 0; i < mNestCount; i++) { + if (!readBeginVCard(allowGarbage)) { + return false; + } + allowGarbage = true; + } + } + } + + if (!readBeginVCard(allowGarbage)) { + return false; + } + long start; + if (mBuilder != null) { + start = System.currentTimeMillis(); + mBuilder.startRecord("VCARD"); + mTimeStartRecord += System.currentTimeMillis() - start; + } + start = System.currentTimeMillis(); + parseItems(); + mTimeParseItems += System.currentTimeMillis() - start; + readEndVCard(true, false); + if (mBuilder != null) { + start = System.currentTimeMillis(); + mBuilder.endRecord(); + mTimeEndRecord += System.currentTimeMillis() - start; + } + return true; + } + + /** + * @return True when successful. False when reaching the end of line + * @throws IOException + * @throws VCardException + */ + protected boolean readBeginVCard(boolean allowGarbage) + throws IOException, VCardException { + String line; + do { + while (true) { + line = getLine(); + if (line == null) { + return false; + } else if (line.trim().length() > 0) { + break; + } + } + String[] strArray = line.split(":", 2); + int length = strArray.length; + + // Though vCard 2.1/3.0 specification does not allow lower cases, + // some data may have them, so we allow it (Actually, previous code + // had explicitly allowed "BEGIN:vCard" though there's no example). + // + // TODO: ignore non vCard entry (e.g. vcalendar). + // XXX: Not sure, but according to VDataBuilder.java, vcalendar + // entry + // may be nested. Just seeking "END:SOMETHING" may not be enough. + // e.g. + // BEGIN:VCARD + // ... (Valid. Must parse this) + // END:VCARD + // BEGIN:VSOMETHING + // ... (Must ignore this) + // BEGIN:VSOMETHING2 + // ... (Must ignore this) + // END:VSOMETHING2 + // ... (Must ignore this!) + // END:VSOMETHING + // BEGIN:VCARD + // ... (Valid. Must parse this) + // END:VCARD + // INVALID_STRING (VCardException should be thrown) + if (length == 2 && + strArray[0].trim().equalsIgnoreCase("BEGIN") && + strArray[1].trim().equalsIgnoreCase("VCARD")) { + return true; + } else if (!allowGarbage) { + if (mNestCount > 0) { + mPreviousLine = line; + return false; + } else { + throw new VCardException( + "Expected String \"BEGIN:VCARD\" did not come " + + "(Instead, \"" + line + "\" came)"); + } + } + } while(allowGarbage); + + throw new VCardException("Reached where must not be reached."); + } + + /** + * The arguments useCache and allowGarbase are usually true and false accordingly when + * this function is called outside this function itself. + * + * @param useCache When true, line is obtained from mPreviousline. Otherwise, getLine() + * is used. + * @param allowGarbage When true, ignore non "END:VCARD" line. + * @throws IOException + * @throws VCardException + */ + protected void readEndVCard(boolean useCache, boolean allowGarbage) + throws IOException, VCardException { + String line; + do { + if (useCache) { + // Though vCard specification does not allow lower cases, + // some data may have them, so we allow it. + line = mPreviousLine; + } else { + while (true) { + line = getLine(); + if (line == null) { + throw new VCardException("Expected END:VCARD was not found."); + } else if (line.trim().length() > 0) { + break; + } + } + } + + String[] strArray = line.split(":", 2); + if (strArray.length == 2 && + strArray[0].trim().equalsIgnoreCase("END") && + strArray[1].trim().equalsIgnoreCase("VCARD")) { + return; + } else if (!allowGarbage) { + throw new VCardException("END:VCARD != \"" + mPreviousLine + "\""); + } + useCache = false; + } while (allowGarbage); + } + + /** + * items = *CRLF item + * / item + */ + protected void parseItems() throws IOException, VCardException { + /* items *CRLF item / item */ + boolean ended = false; + + if (mBuilder != null) { + long start = System.currentTimeMillis(); + mBuilder.startProperty(); + mTimeStartProperty += System.currentTimeMillis() - start; + } + ended = parseItem(); + if (mBuilder != null && !ended) { + long start = System.currentTimeMillis(); + mBuilder.endProperty(); + mTimeEndProperty += System.currentTimeMillis() - start; + } + + while (!ended) { + // follow VCARD ,it wont reach endProperty + if (mBuilder != null) { + long start = System.currentTimeMillis(); + mBuilder.startProperty(); + mTimeStartProperty += System.currentTimeMillis() - start; + } + ended = parseItem(); + if (mBuilder != null && !ended) { + long start = System.currentTimeMillis(); + mBuilder.endProperty(); + mTimeEndProperty += System.currentTimeMillis() - start; + } + } + } + + /** + * item = [groups "."] name [params] ":" value CRLF + * / [groups "."] "ADR" [params] ":" addressparts CRLF + * / [groups "."] "ORG" [params] ":" orgparts CRLF + * / [groups "."] "N" [params] ":" nameparts CRLF + * / [groups "."] "AGENT" [params] ":" vcard CRLF + */ + protected boolean parseItem() throws IOException, VCardException { + mEncoding = sDefaultEncoding; + + String line = getNonEmptyLine(); + long start = System.currentTimeMillis(); + + String[] propertyNameAndValue = separateLineAndHandleGroup(line); + if (propertyNameAndValue == null) { + return true; + } + if (propertyNameAndValue.length != 2) { + throw new VCardException("Invalid line \"" + line + "\""); + } + String propertyName = propertyNameAndValue[0].toUpperCase(); + String propertyValue = propertyNameAndValue[1]; + + mTimeParseItem1 += System.currentTimeMillis() - start; + + if (propertyName.equals("ADR") || + propertyName.equals("ORG") || + propertyName.equals("N")) { + start = System.currentTimeMillis(); + handleMultiplePropertyValue(propertyName, propertyValue); + mTimeParseItem3 += System.currentTimeMillis() - start; + return false; + } else if (propertyName.equals("AGENT")) { + handleAgent(propertyValue); + return false; + } else if (isValidPropertyName(propertyName)) { + if (propertyName.equals("BEGIN")) { + if (propertyValue.equals("VCARD")) { + throw new VCardNestedException("This vCard has nested vCard data in it."); + } else { + throw new VCardException("Unknown BEGIN type: " + propertyValue); + } + } else if (propertyName.equals("VERSION") && + !propertyValue.equals(getVersion())) { + throw new VCardVersionException("Incompatible version: " + + propertyValue + " != " + getVersion()); + } + start = System.currentTimeMillis(); + handlePropertyValue(propertyName, propertyValue); + mTimeParseItem2 += System.currentTimeMillis() - start; + return false; + } + + throw new VCardException("Unknown property name: \"" + + propertyName + "\""); + } + + static private final int STATE_GROUP_OR_PROPNAME = 0; + static private final int STATE_PARAMS = 1; + // vCard 3.1 specification allows double-quoted param-value, while vCard 2.1 does not. + // This is just for safety. + static private final int STATE_PARAMS_IN_DQUOTE = 2; + + protected String[] separateLineAndHandleGroup(String line) throws VCardException { + int length = line.length(); + int state = STATE_GROUP_OR_PROPNAME; + int nameIndex = 0; + + String[] propertyNameAndValue = new String[2]; + + for (int i = 0; i < length; i++) { + char ch = line.charAt(i); + switch (state) { + case STATE_GROUP_OR_PROPNAME: + if (ch == ':') { + String propertyName = line.substring(nameIndex, i); + if (propertyName.equalsIgnoreCase("END")) { + mPreviousLine = line; + return null; + } + if (mBuilder != null) { + mBuilder.propertyName(propertyName); + } + propertyNameAndValue[0] = propertyName; + if (i < length - 1) { + propertyNameAndValue[1] = line.substring(i + 1); + } else { + propertyNameAndValue[1] = ""; + } + return propertyNameAndValue; + } else if (ch == '.') { + String groupName = line.substring(nameIndex, i); + if (mBuilder != null) { + mBuilder.propertyGroup(groupName); + } + nameIndex = i + 1; + } else if (ch == ';') { + String propertyName = line.substring(nameIndex, i); + if (propertyName.equalsIgnoreCase("END")) { + mPreviousLine = line; + return null; + } + if (mBuilder != null) { + mBuilder.propertyName(propertyName); + } + propertyNameAndValue[0] = propertyName; + nameIndex = i + 1; + state = STATE_PARAMS; + } + break; + case STATE_PARAMS: + if (ch == '"') { + state = STATE_PARAMS_IN_DQUOTE; + } else if (ch == ';') { + handleParams(line.substring(nameIndex, i)); + nameIndex = i + 1; + } else if (ch == ':') { + handleParams(line.substring(nameIndex, i)); + if (i < length - 1) { + propertyNameAndValue[1] = line.substring(i + 1); + } else { + propertyNameAndValue[1] = ""; + } + return propertyNameAndValue; + } + break; + case STATE_PARAMS_IN_DQUOTE: + if (ch == '"') { + state = STATE_PARAMS; + } + break; + } + } + + throw new VCardException("Invalid line: \"" + line + "\""); + } + + + /** + * params = ";" [ws] paramlist + * paramlist = paramlist [ws] ";" [ws] param + * / param + * param = "TYPE" [ws] "=" [ws] ptypeval + * / "VALUE" [ws] "=" [ws] pvalueval + * / "ENCODING" [ws] "=" [ws] pencodingval + * / "CHARSET" [ws] "=" [ws] charsetval + * / "LANGUAGE" [ws] "=" [ws] langval + * / "X-" word [ws] "=" [ws] word + * / knowntype + */ + protected void handleParams(String params) throws VCardException { + String[] strArray = params.split("=", 2); + if (strArray.length == 2) { + String paramName = strArray[0].trim(); + String paramValue = strArray[1].trim(); + if (paramName.equals("TYPE")) { + handleType(paramValue); + } else if (paramName.equals("VALUE")) { + handleValue(paramValue); + } else if (paramName.equals("ENCODING")) { + handleEncoding(paramValue); + } else if (paramName.equals("CHARSET")) { + handleCharset(paramValue); + } else if (paramName.equals("LANGUAGE")) { + handleLanguage(paramValue); + } else if (paramName.startsWith("X-")) { + handleAnyParam(paramName, paramValue); + } else { + throw new VCardException("Unknown type \"" + paramName + "\""); + } + } else { + handleType(strArray[0]); + } + } + + /** + * ptypeval = knowntype / "X-" word + */ + protected void handleType(String ptypeval) { + String upperTypeValue = ptypeval; + if (!(sKnownTypeSet.contains(upperTypeValue) || upperTypeValue.startsWith("X-")) && + !mWarningValueMap.contains(ptypeval)) { + mWarningValueMap.add(ptypeval); + Log.w(LOG_TAG, "Type unsupported by vCard 2.1: " + ptypeval); + } + if (mBuilder != null) { + mBuilder.propertyParamType("TYPE"); + mBuilder.propertyParamValue(upperTypeValue); + } + } + + /** + * pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word + */ + protected void handleValue(String pvalueval) throws VCardException { + if (sKnownValueSet.contains(pvalueval.toUpperCase()) || + pvalueval.startsWith("X-")) { + if (mBuilder != null) { + mBuilder.propertyParamType("VALUE"); + mBuilder.propertyParamValue(pvalueval); + } + } else { + throw new VCardException("Unknown value \"" + pvalueval + "\""); + } + } + + /** + * pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word + */ + protected void handleEncoding(String pencodingval) throws VCardException { + if (isValidEncoding(pencodingval) || + pencodingval.startsWith("X-")) { + if (mBuilder != null) { + mBuilder.propertyParamType("ENCODING"); + mBuilder.propertyParamValue(pencodingval); + } + mEncoding = pencodingval; + } else { + throw new VCardException("Unknown encoding \"" + pencodingval + "\""); + } + } + + /** + * vCard specification only allows us-ascii and iso-8859-xxx (See RFC 1521), + * but some vCard contains other charset, so we allow them. + */ + protected void handleCharset(String charsetval) { + if (mBuilder != null) { + mBuilder.propertyParamType("CHARSET"); + mBuilder.propertyParamValue(charsetval); + } + } + + /** + * See also Section 7.1 of RFC 1521 + */ + protected void handleLanguage(String langval) throws VCardException { + String[] strArray = langval.split("-"); + if (strArray.length != 2) { + throw new VCardException("Invalid Language: \"" + langval + "\""); + } + String tmp = strArray[0]; + int length = tmp.length(); + for (int i = 0; i < length; i++) { + if (!isLetter(tmp.charAt(i))) { + throw new VCardException("Invalid Language: \"" + langval + "\""); + } + } + tmp = strArray[1]; + length = tmp.length(); + for (int i = 0; i < length; i++) { + if (!isLetter(tmp.charAt(i))) { + throw new VCardException("Invalid Language: \"" + langval + "\""); + } + } + if (mBuilder != null) { + mBuilder.propertyParamType("LANGUAGE"); + mBuilder.propertyParamValue(langval); + } + } + + /** + * Mainly for "X-" type. This accepts any kind of type without check. + */ + protected void handleAnyParam(String paramName, String paramValue) { + if (mBuilder != null) { + mBuilder.propertyParamType(paramName); + mBuilder.propertyParamValue(paramValue); + } + } + + protected void handlePropertyValue( + String propertyName, String propertyValue) throws + IOException, VCardException { + if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) { + long start = System.currentTimeMillis(); + String result = getQuotedPrintable(propertyValue); + if (mBuilder != null) { + ArrayList<String> v = new ArrayList<String>(); + v.add(result); + mBuilder.propertyValues(v); + } + mTimeHandlePropertyValue2 += System.currentTimeMillis() - start; + } else if (mEncoding.equalsIgnoreCase("BASE64") || + mEncoding.equalsIgnoreCase("B")) { + long start = System.currentTimeMillis(); + // It is very rare, but some BASE64 data may be so big that + // OutOfMemoryError occurs. To ignore such cases, use try-catch. + try { + String result = getBase64(propertyValue); + if (mBuilder != null) { + ArrayList<String> v = new ArrayList<String>(); + v.add(result); + mBuilder.propertyValues(v); + } + } catch (OutOfMemoryError error) { + Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!"); + if (mBuilder != null) { + mBuilder.propertyValues(null); + } + } + mTimeHandlePropertyValue3 += System.currentTimeMillis() - start; + } else { + if (!(mEncoding == null || mEncoding.equalsIgnoreCase("7BIT") + || mEncoding.equalsIgnoreCase("8BIT") + || mEncoding.toUpperCase().startsWith("X-"))) { + Log.w(LOG_TAG, "The encoding unsupported by vCard spec: \"" + mEncoding + "\"."); + } + + long start = System.currentTimeMillis(); + if (mBuilder != null) { + ArrayList<String> v = new ArrayList<String>(); + v.add(maybeUnescapeText(propertyValue)); + mBuilder.propertyValues(v); + } + mTimeHandlePropertyValue1 += System.currentTimeMillis() - start; + } + } + + protected String getQuotedPrintable(String firstString) throws IOException, VCardException { + // Specifically, there may be some padding between = and CRLF. + // See the following: + // + // qp-line := *(qp-segment transport-padding CRLF) + // qp-part transport-padding + // qp-segment := qp-section *(SPACE / TAB) "=" + // ; Maximum length of 76 characters + // + // e.g. (from RFC 2045) + // Now's the time = + // for all folk to come= + // to the aid of their country. + if (firstString.trim().endsWith("=")) { + // remove "transport-padding" + int pos = firstString.length() - 1; + while(firstString.charAt(pos) != '=') { + } + StringBuilder builder = new StringBuilder(); + builder.append(firstString.substring(0, pos + 1)); + builder.append("\r\n"); + String line; + while (true) { + line = getLine(); + if (line == null) { + throw new VCardException( + "File ended during parsing quoted-printable String"); + } + if (line.trim().endsWith("=")) { + // remove "transport-padding" + pos = line.length() - 1; + while(line.charAt(pos) != '=') { + } + builder.append(line.substring(0, pos + 1)); + builder.append("\r\n"); + } else { + builder.append(line); + break; + } + } + return builder.toString(); + } else { + return firstString; + } + } + + protected String getBase64(String firstString) throws IOException, VCardException { + StringBuilder builder = new StringBuilder(); + builder.append(firstString); + + while (true) { + String line = getLine(); + if (line == null) { + throw new VCardException( + "File ended during parsing BASE64 binary"); + } + if (line.length() == 0) { + break; + } + builder.append(line); + } + + return builder.toString(); + } + + /** + * Mainly for "ADR", "ORG", and "N" + * We do not care the number of strnosemi here. + * + * addressparts = 0*6(strnosemi ";") strnosemi + * ; PO Box, Extended Addr, Street, Locality, Region, + * Postal Code, Country Name + * orgparts = *(strnosemi ";") strnosemi + * ; First is Organization Name, + * remainder are Organization Units. + * nameparts = 0*4(strnosemi ";") strnosemi + * ; Family, Given, Middle, Prefix, Suffix. + * ; Example:Public;John;Q.;Reverend Dr.;III, Esq. + * strnosemi = *(*nonsemi ("\;" / "\" CRLF)) *nonsemi + * ; To include a semicolon in this string, it must be escaped + * ; with a "\" character. + * + * We are not sure whether we should add "\" CRLF to each value. + * For now, we exclude them. + */ + protected void handleMultiplePropertyValue( + String propertyName, String propertyValue) throws IOException, VCardException { + // vCard 2.1 does not allow QUOTED-PRINTABLE here, but some data have it. + if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) { + propertyValue = getQuotedPrintable(propertyValue); + } + + if (mBuilder != null) { + // TODO: limit should be set in accordance with propertyName? + StringBuilder builder = new StringBuilder(); + ArrayList<String> list = new ArrayList<String>(); + int length = propertyValue.length(); + for (int i = 0; i < length; i++) { + char ch = propertyValue.charAt(i); + if (ch == '\\' && i < length - 1) { + char nextCh = propertyValue.charAt(i + 1); + String unescapedString = maybeUnescape(nextCh); + if (unescapedString != null) { + builder.append(unescapedString); + i++; + } else { + builder.append(ch); + } + } else if (ch == ';') { + list.add(builder.toString()); + builder = new StringBuilder(); + } else { + builder.append(ch); + } + } + list.add(builder.toString()); + mBuilder.propertyValues(list); + } + } + + /** + * vCard 2.1 specifies AGENT allows one vcard entry. It is not encoded at all. + * + * item = ... + * / [groups "."] "AGENT" + * [params] ":" vcard CRLF + * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF + * items *CRLF "END" [ws] ":" [ws] "VCARD" + * + */ + protected void handleAgent(String propertyValue) throws VCardException { + throw new VCardNotSupportedException("AGENT Property is not supported now."); + /* This is insufficient support. Also, AGENT Property is very rare. + Ignore it for now. + TODO: fix this. + + String[] strArray = propertyValue.split(":", 2); + if (!(strArray.length == 2 || + strArray[0].trim().equalsIgnoreCase("BEGIN") && + strArray[1].trim().equalsIgnoreCase("VCARD"))) { + throw new VCardException("BEGIN:VCARD != \"" + propertyValue + "\""); + } + parseItems(); + readEndVCard(); + */ + } + + /** + * For vCard 3.0. + */ + protected String maybeUnescapeText(String text) { + return text; + } + + /** + * Returns unescaped String if the character should be unescaped. Return null otherwise. + * e.g. In vCard 2.1, "\;" should be unescaped into ";" while "\x" should not be. + */ + protected String maybeUnescape(char ch) { + // Original vCard 2.1 specification does not allow transformation + // "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous implementation of + // this class allowed them, so keep it as is. + if (ch == '\\' || ch == ';' || ch == ':' || ch == ',') { + return String.valueOf(ch); + } else { + return null; + } + } + + @Override + public boolean parse(InputStream is, VCardBuilder builder) + throws IOException, VCardException { + return parse(is, VCardConfig.DEFAULT_CHARSET, builder); + } + + @Override + public boolean parse(InputStream is, String charset, VCardBuilder builder) + throws IOException, VCardException { + // TODO: make this count error entries instead of just throwing VCardException. + + { + // TODO: If we really need to allow only CRLF as line break, + // we will have to develop our own BufferedReader(). + final InputStreamReader tmpReader = new InputStreamReader(is, charset); + if (VCardConfig.showPerformanceLog()) { + mReader = new CustomBufferedReader(tmpReader); + } else { + mReader = new BufferedReader(tmpReader); + } + } + + mBuilder = builder; + + long start = System.currentTimeMillis(); + if (mBuilder != null) { + mBuilder.start(); + } + parseVCardFile(); + if (mBuilder != null) { + mBuilder.end(); + } + mTimeTotal += System.currentTimeMillis() - start; + + if (VCardConfig.showPerformanceLog()) { + showPerformanceInfo(); + } + + return true; + } + + @Override + public void parse(InputStream is, String charset, VCardBuilder builder, boolean canceled) + throws IOException, VCardException { + mCanceled = canceled; + parse(is, charset, builder); + } + + private void showPerformanceInfo() { + Log.d(LOG_TAG, "total parsing time: " + mTimeTotal + " ms"); + if (mReader instanceof CustomBufferedReader) { + Log.d(LOG_TAG, "total readLine time: " + + ((CustomBufferedReader)mReader).getTotalmillisecond() + " ms"); + } + Log.d(LOG_TAG, "mTimeStartRecord: " + mTimeStartRecord + " ms"); + Log.d(LOG_TAG, "mTimeEndRecord: " + mTimeEndRecord + " ms"); + Log.d(LOG_TAG, "mTimeParseItem1: " + mTimeParseItem1 + " ms"); + Log.d(LOG_TAG, "mTimeParseItem2: " + mTimeParseItem2 + " ms"); + Log.d(LOG_TAG, "mTimeParseItem3: " + mTimeParseItem3 + " ms"); + Log.d(LOG_TAG, "mTimeHandlePropertyValue1: " + mTimeHandlePropertyValue1 + " ms"); + Log.d(LOG_TAG, "mTimeHandlePropertyValue2: " + mTimeHandlePropertyValue2 + " ms"); + Log.d(LOG_TAG, "mTimeHandlePropertyValue3: " + mTimeHandlePropertyValue3 + " ms"); + } + + private boolean isLetter(char ch) { + if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { + return true; + } + return false; + } +} + +class CustomBufferedReader extends BufferedReader { + private long mTime; + + public CustomBufferedReader(Reader in) { + super(in); + } + + @Override + public String readLine() throws IOException { + long start = System.currentTimeMillis(); + String ret = super.readLine(); + long end = System.currentTimeMillis(); + mTime += end - start; + return ret; + } + + public long getTotalmillisecond() { + return mTime; + } +} diff --git a/core/java/android/pim/vcard/VCardParser_V30.java b/core/java/android/pim/vcard/VCardParser_V30.java new file mode 100644 index 000000000000..634d9f5d20dd --- /dev/null +++ b/core/java/android/pim/vcard/VCardParser_V30.java @@ -0,0 +1,306 @@ +/* + * 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.pim.vcard; + +import android.pim.vcard.exception.VCardException; +import android.util.Log; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; + +/** + * This class is used to parse vcard3.0. <br> + * Please refer to vCard Specification 3.0 (http://tools.ietf.org/html/rfc2426) + */ +public class VCardParser_V30 extends VCardParser_V21 { + private static final String LOG_TAG = "VCardParser_V30"; + + private static final HashSet<String> sAcceptablePropsWithParam = new HashSet<String>( + Arrays.asList( + "BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND", + "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL", + "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER", // 2.1 + "NAME", "PROFILE", "SOURCE", "NICKNAME", "CLASS", + "SORT-STRING", "CATEGORIES", "PRODID")); // 3.0 + + // Although "7bit" and "BASE64" is not allowed in vCard 3.0, we allow it for safety. + private static final HashSet<String> sAcceptableEncodingV30 = new HashSet<String>( + Arrays.asList("7BIT", "8BIT", "BASE64", "B")); + + // Although RFC 2426 specifies some property must not have parameters, we allow it, + // since there may be some careers which violates the RFC... + private static final HashSet<String> acceptablePropsWithoutParam = new HashSet<String>(); + + private String mPreviousLine; + + @Override + protected String getVersion() { + return "3.0"; + } + + @Override + protected boolean isValidPropertyName(String propertyName) { + if (!(sAcceptablePropsWithParam.contains(propertyName) || + acceptablePropsWithoutParam.contains(propertyName) || + propertyName.startsWith("X-")) && + !mWarningValueMap.contains(propertyName)) { + mWarningValueMap.add(propertyName); + Log.w(LOG_TAG, "Property name unsupported by vCard 3.0: " + propertyName); + } + return true; + } + + @Override + protected boolean isValidEncoding(String encoding) { + return sAcceptableEncodingV30.contains(encoding.toUpperCase()); + } + + @Override + protected String getLine() throws IOException { + if (mPreviousLine != null) { + String ret = mPreviousLine; + mPreviousLine = null; + return ret; + } else { + return mReader.readLine(); + } + } + + /** + * vCard 3.0 requires that the line with space at the beginning of the line + * must be combined with previous line. + */ + @Override + protected String getNonEmptyLine() throws IOException, VCardException { + String line; + StringBuilder builder = null; + while (true) { + line = mReader.readLine(); + if (line == null) { + if (builder != null) { + return builder.toString(); + } else if (mPreviousLine != null) { + String ret = mPreviousLine; + mPreviousLine = null; + return ret; + } + throw new VCardException("Reached end of buffer."); + } else if (line.length() == 0) { + if (builder != null) { + return builder.toString(); + } else if (mPreviousLine != null) { + String ret = mPreviousLine; + mPreviousLine = null; + return ret; + } + } else if (line.charAt(0) == ' ' || line.charAt(0) == '\t') { + if (builder != null) { + // See Section 5.8.1 of RFC 2425 (MIME-DIR document). + // Following is the excerpts from it. + // + // DESCRIPTION:This is a long description that exists on a long line. + // + // Can be represented as: + // + // DESCRIPTION:This is a long description + // that exists on a long line. + // + // It could also be represented as: + // + // DESCRIPTION:This is a long descrip + // tion that exists o + // n a long line. + builder.append(line.substring(1)); + } else if (mPreviousLine != null) { + builder = new StringBuilder(); + builder.append(mPreviousLine); + mPreviousLine = null; + builder.append(line.substring(1)); + } else { + throw new VCardException("Space exists at the beginning of the line"); + } + } else { + if (mPreviousLine == null) { + mPreviousLine = line; + if (builder != null) { + return builder.toString(); + } + } else { + String ret = mPreviousLine; + mPreviousLine = line; + return ret; + } + } + } + } + + + /** + * vcard = [group "."] "BEGIN" ":" "VCARD" 1*CRLF + * 1*(contentline) + * ;A vCard object MUST include the VERSION, FN and N types. + * [group "."] "END" ":" "VCARD" 1*CRLF + */ + @Override + protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException { + // TODO: vCard 3.0 supports group. + return super.readBeginVCard(allowGarbage); + } + + @Override + protected void readEndVCard(boolean useCache, boolean allowGarbage) + throws IOException, VCardException { + // TODO: vCard 3.0 supports group. + super.readEndVCard(useCache, allowGarbage); + } + + /** + * vCard 3.0 allows iana-token as paramType, while vCard 2.1 does not. + */ + @Override + protected void handleParams(String params) throws VCardException { + try { + super.handleParams(params); + } catch (VCardException e) { + // maybe IANA type + String[] strArray = params.split("=", 2); + if (strArray.length == 2) { + handleAnyParam(strArray[0], strArray[1]); + } else { + // Must not come here in the current implementation. + throw new VCardException( + "Unknown params value: " + params); + } + } + } + + @Override + protected void handleAnyParam(String paramName, String paramValue) { + // vCard 3.0 accept comma-separated multiple values, but + // current PropertyNode does not accept it. + // For now, we do not split the values. + // + // TODO: fix this. + super.handleAnyParam(paramName, paramValue); + } + + /** + * vCard 3.0 defines + * + * param = param-name "=" param-value *("," param-value) + * param-name = iana-token / x-name + * param-value = ptext / quoted-string + * quoted-string = DQUOTE QSAFE-CHAR DQUOTE + */ + @Override + protected void handleType(String ptypevalues) { + String[] ptypeArray = ptypevalues.split(","); + mBuilder.propertyParamType("TYPE"); + for (String value : ptypeArray) { + int length = value.length(); + if (length >= 2 && value.startsWith("\"") && value.endsWith("\"")) { + mBuilder.propertyParamValue(value.substring(1, value.length() - 1)); + } else { + mBuilder.propertyParamValue(value); + } + } + } + + @Override + protected void handleAgent(String propertyValue) throws VCardException { + // The way how vCard 3.0 supports "AGENT" is completely different from vCard 2.0. + // + // e.g. + // AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1-919-555-7878\n + // TITLE:Area Administrator\, Assistant\n EMAIL\;TYPE=INTERN\n + // ET:jfriday@host.com\nEND:VCARD\n + // + // TODO: fix this. + // + // issue: + // vCard 3.0 also allows this as an example. + // + // AGENT;VALUE=uri: + // CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com + // + // This is not VCARD. Should we support this? + throw new VCardException("AGENT in vCard 3.0 is not supported yet."); + } + + /** + * vCard 3.0 does not require two CRLF at the last of BASE64 data. + * It only requires that data should be MIME-encoded. + */ + @Override + protected String getBase64(String firstString) throws IOException, VCardException { + StringBuilder builder = new StringBuilder(); + builder.append(firstString); + + while (true) { + String line = getLine(); + if (line == null) { + throw new VCardException( + "File ended during parsing BASE64 binary"); + } + if (line.length() == 0) { + break; + } else if (!line.startsWith(" ") && !line.startsWith("\t")) { + mPreviousLine = line; + break; + } + builder.append(line); + } + + return builder.toString(); + } + + /** + * ESCAPED-CHAR = "\\" / "\;" / "\," / "\n" / "\N") + * ; \\ encodes \, \n or \N encodes newline + * ; \; encodes ;, \, encodes , + * + * Note: Apple escape ':' into '\:' while does not escape '\' + */ + @Override + protected String maybeUnescapeText(String text) { + StringBuilder builder = new StringBuilder(); + int length = text.length(); + for (int i = 0; i < length; i++) { + char ch = text.charAt(i); + if (ch == '\\' && i < length - 1) { + char next_ch = text.charAt(++i); + if (next_ch == 'n' || next_ch == 'N') { + builder.append("\r\n"); + } else { + builder.append(next_ch); + } + } else { + builder.append(ch); + } + } + return builder.toString(); + } + + @Override + protected String maybeUnescape(char ch) { + if (ch == 'n' || ch == 'N') { + return "\r\n"; + } else { + return String.valueOf(ch); + } + } +} diff --git a/core/java/android/pim/vcard/VCardSourceDetector.java b/core/java/android/pim/vcard/VCardSourceDetector.java new file mode 100644 index 000000000000..7e2be2b12eed --- /dev/null +++ b/core/java/android/pim/vcard/VCardSourceDetector.java @@ -0,0 +1,137 @@ +/* + * 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.pim.vcard; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Class which tries to detects the source of the vCard from its properties. + * Currently this implementation is very premature. + * @hide + */ +public class VCardSourceDetector implements VCardBuilder { + // Should only be used in package. + static final int TYPE_UNKNOWN = 0; + static final int TYPE_APPLE = 1; + static final int TYPE_JAPANESE_MOBILE_PHONE = 2; // Used in Japanese mobile phones. + static final int TYPE_FOMA = 3; // Used in some Japanese FOMA mobile phones. + static final int TYPE_WINDOWS_MOBILE_JP = 4; + // TODO: Excel, etc. + + private static Set<String> APPLE_SIGNS = new HashSet<String>(Arrays.asList( + "X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", "X-PHONETIC-LAST-NAME", + "X-ABADR", "X-ABUID")); + + private static Set<String> JAPANESE_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList( + "X-GNO", "X-GN", "X-REDUCTION")); + + private static Set<String> WINDOWS_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList( + "X-MICROSOFT-ASST_TEL", "X-MICROSOFT-ASSISTANT", "X-MICROSOFT-OFFICELOC")); + + // Note: these signes appears before the signs of the other type (e.g. "X-GN"). + // In other words, Japanese FOMA mobile phones are detected as FOMA, not JAPANESE_MOBILE_PHONES. + private static Set<String> FOMA_SIGNS = new HashSet<String>(Arrays.asList( + "X-SD-VERN", "X-SD-FORMAT_VER", "X-SD-CATEGORIES", "X-SD-CLASS", "X-SD-DCREATED", + "X-SD-DESCRIPTION")); + private static String TYPE_FOMA_CHARSET_SIGN = "X-SD-CHAR_CODE"; + + private int mType = TYPE_UNKNOWN; + // Some mobile phones (like FOMA) tells us the charset of the data. + private boolean mNeedParseSpecifiedCharset; + private String mSpecifiedCharset; + + public void start() { + } + + public void end() { + } + + public void startRecord(String type) { + } + + public void startProperty() { + mNeedParseSpecifiedCharset = false; + } + + public void endProperty() { + } + + public void endRecord() { + } + + public void propertyGroup(String group) { + } + + public void propertyName(String name) { + if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) { + mType = TYPE_FOMA; + mNeedParseSpecifiedCharset = true; + return; + } + if (mType != TYPE_UNKNOWN) { + return; + } + if (WINDOWS_MOBILE_PHONE_SIGNS.contains(name)) { + mType = TYPE_WINDOWS_MOBILE_JP; + } else if (FOMA_SIGNS.contains(name)) { + mType = TYPE_FOMA; + } else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(name)) { + mType = TYPE_JAPANESE_MOBILE_PHONE; + } else if (APPLE_SIGNS.contains(name)) { + mType = TYPE_APPLE; + } + } + + public void propertyParamType(String type) { + } + + public void propertyParamValue(String value) { + } + + public void propertyValues(List<String> values) { + if (mNeedParseSpecifiedCharset && values.size() > 0) { + mSpecifiedCharset = values.get(0); + } + } + + int getType() { + return mType; + } + + /** + * Return charset String guessed from the source's properties. + * This method must be called after parsing target file(s). + * @return Charset String. Null is returned if guessing the source fails. + */ + public String getEstimatedCharset() { + if (mSpecifiedCharset != null) { + return mSpecifiedCharset; + } + switch (mType) { + case TYPE_WINDOWS_MOBILE_JP: + case TYPE_FOMA: + case TYPE_JAPANESE_MOBILE_PHONE: + return "SHIFT_JIS"; + case TYPE_APPLE: + return "UTF-8"; + default: + return null; + } + } +} diff --git a/core/java/android/pim/vcard/exception/VCardException.java b/core/java/android/pim/vcard/exception/VCardException.java new file mode 100644 index 000000000000..e557219f235a --- /dev/null +++ b/core/java/android/pim/vcard/exception/VCardException.java @@ -0,0 +1,35 @@ +/* + * 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.pim.vcard.exception; + +public class VCardException extends java.lang.Exception { + /** + * Constructs a VCardException object + */ + public VCardException() { + super(); + } + + /** + * Constructs a VCardException object + * + * @param message the error message + */ + public VCardException(String message) { + super(message); + } + +} diff --git a/core/java/android/pim/vcard/exception/VCardNestedException.java b/core/java/android/pim/vcard/exception/VCardNestedException.java new file mode 100644 index 000000000000..503c2fbcf536 --- /dev/null +++ b/core/java/android/pim/vcard/exception/VCardNestedException.java @@ -0,0 +1,29 @@ +/* + * 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.pim.vcard.exception; + +/** + * VCardException thrown when VCard is nested without VCardParser's being notified. + */ +public class VCardNestedException extends VCardNotSupportedException { + public VCardNestedException() { + super(); + } + public VCardNestedException(String message) { + super(message); + } +} diff --git a/core/java/android/pim/vcard/exception/VCardNotSupportedException.java b/core/java/android/pim/vcard/exception/VCardNotSupportedException.java new file mode 100644 index 000000000000..616aa7763b0e --- /dev/null +++ b/core/java/android/pim/vcard/exception/VCardNotSupportedException.java @@ -0,0 +1,33 @@ +/* + * 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.pim.vcard.exception; + +/** + * The exception which tells that the input VCard is probably valid from the view of + * specification but not supported in the current framework for now. + * + * This is a kind of a good news from the view of development. + * It may be good to ask users to send a report with the VCard example + * for the future development. + */ +public class VCardNotSupportedException extends VCardException { + public VCardNotSupportedException() { + super(); + } + public VCardNotSupportedException(String message) { + super(message); + } +}
\ No newline at end of file diff --git a/core/java/android/pim/vcard/exception/VCardVersionException.java b/core/java/android/pim/vcard/exception/VCardVersionException.java new file mode 100644 index 000000000000..9fe8b7f92af9 --- /dev/null +++ b/core/java/android/pim/vcard/exception/VCardVersionException.java @@ -0,0 +1,29 @@ +/* + * 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.pim.vcard.exception; + +/** + * VCardException used only when the version of the vCard is different. + */ +public class VCardVersionException extends VCardException { + public VCardVersionException() { + super(); + } + public VCardVersionException(String message) { + super(message); + } +} diff --git a/core/java/android/pim/vcard/exception/package.html b/core/java/android/pim/vcard/exception/package.html new file mode 100644 index 000000000000..26b8a328b132 --- /dev/null +++ b/core/java/android/pim/vcard/exception/package.html @@ -0,0 +1,5 @@ +<HTML> +<BODY> +{@hide} +</BODY> +</HTML>
\ No newline at end of file diff --git a/core/java/android/pim/vcard/package.html b/core/java/android/pim/vcard/package.html new file mode 100644 index 000000000000..26b8a328b132 --- /dev/null +++ b/core/java/android/pim/vcard/package.html @@ -0,0 +1,5 @@ +<HTML> +<BODY> +{@hide} +</BODY> +</HTML>
\ No newline at end of file diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index b2c597f2da5a..b779d59b533a 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -192,7 +192,6 @@ public final class ContactsContract { public static final Uri CONTENT_SUMMARY_GROUP_URI = Uri.withAppendedPath( CONTENT_SUMMARY_URI, "group"); - /** * The MIME type of {@link #CONTENT_URI} providing a directory of * people. @@ -601,9 +600,20 @@ public final class ContactsContract { } /** + * The base types that all "Typed" data kinds support. + */ + public interface BaseTypes { + + /** + * A custom type. The custom label should be supplied by user. + */ + public static int TYPE_CUSTOM = 0; + } + + /** * Columns common across the specific types. */ - private interface CommonColumns { + private interface CommonColumns extends BaseTypes{ /** * The type of data, for example Home or Work. * <P>Type: INTEGER</P> @@ -624,17 +634,6 @@ public final class ContactsContract { } /** - * The base types that all "Typed" data kinds support. - */ - public interface BaseTypes { - - /** - * A custom type. The custom label should be supplied by user. - */ - public static int TYPE_CUSTOM = 0; - } - - /** * Parts of the name. */ public static final class StructuredName { @@ -700,18 +699,12 @@ public final class ContactsContract { /** * A nickname. */ - public static final class Nickname implements BaseTypes { + public static final class Nickname implements CommonColumns { private Nickname() {} /** Mime-type used when storing this in data table. */ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/nickname"; - /** - * The type of data, for example Home or Work. - * <P>Type: INTEGER</P> - */ - public static final String TYPE = "data1"; - public static final int TYPE_DEFAULT = 1; public static final int TYPE_OTHER_NAME = 2; public static final int TYPE_MAINDEN_NAME = 3; @@ -721,19 +714,13 @@ public final class ContactsContract { /** * The name itself */ - public static final String NAME = "data2"; - - /** - * The user provided label, only used if TYPE is {@link #TYPE_CUSTOM}. - * <P>Type: TEXT</P> - */ - public static final String LABEL = "data3"; + public static final String NAME = DATA; } /** * Common data definition for telephone numbers. */ - public static final class Phone implements BaseCommonColumns, CommonColumns, BaseTypes { + public static final class Phone implements BaseCommonColumns, CommonColumns { private Phone() {} /** Mime-type used when storing this in data table. */ @@ -774,13 +761,13 @@ public final class ContactsContract { * The phone number as the user entered it. * <P>Type: TEXT</P> */ - public static final String NUMBER = "data2"; + public static final String NUMBER = DATA; } /** * Common data definition for email addresses. */ - public static final class Email implements BaseCommonColumns, CommonColumns, BaseTypes { + public static final class Email implements BaseCommonColumns, CommonColumns { private Email() {} /** Mime-type used when storing this in data table. */ @@ -794,7 +781,7 @@ public final class ContactsContract { /** * Common data definition for postal addresses. */ - public static final class Postal implements BaseCommonColumns, CommonColumns, BaseTypes { + public static final class Postal implements BaseCommonColumns, CommonColumns { private Postal() {} /** Mime-type used when storing this in data table. */ @@ -822,7 +809,7 @@ public final class ContactsContract { /** * Common data definition for IM addresses. */ - public static final class Im implements BaseCommonColumns, CommonColumns, BaseTypes { + public static final class Im implements BaseCommonColumns, CommonColumns { private Im() {} /** Mime-type used when storing this in data table. */ @@ -882,33 +869,20 @@ public final class ContactsContract { /** * Common data definition for organizations. */ - public static final class Organization implements BaseCommonColumns, BaseTypes { + public static final class Organization implements BaseCommonColumns, CommonColumns { private Organization() {} /** Mime-type used when storing this in data table. */ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/organization"; - /** - * The type of data, for example Home or Work. - * <P>Type: INTEGER</P> - */ - public static final String TYPE = "data1"; - - public static final int TYPE_HOME = 1; - public static final int TYPE_WORK = 2; - public static final int TYPE_OTHER = 3; - - /** - * The user provided label, only used if TYPE is {@link #TYPE_CUSTOM}. - * <P>Type: TEXT</P> - */ - public static final String LABEL = "data2"; + public static final int TYPE_WORK = 1; + public static final int TYPE_OTHER = 2; /** * The company as the user entered it. * <P>Type: TEXT</P> */ - public static final String COMPANY = "data3"; + public static final String COMPANY = DATA; /** * The position title at this company as the user entered it. diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java index 13d980d336a0..b780f419504e 100644 --- a/core/java/android/server/BluetoothDeviceService.java +++ b/core/java/android/server/BluetoothDeviceService.java @@ -959,7 +959,38 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { return setPinNative(address, pinString, data.intValue()); } - public synchronized boolean cancelPin(String address) { + public synchronized boolean setPasskey(String address, int passkey) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + if (passkey < 0 || passkey > 999999 || !BluetoothDevice.checkBluetoothAddress(address)) { + return false; + } + address = address.toUpperCase(); + Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); + if (data == null) { + Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " + + "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" + + " or by bluez.\n"); + return false; + } + return setPasskeyNative(address, passkey, data.intValue()); + } + + public synchronized boolean setPairingConfirmation(String address, boolean confirm) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + address = address.toUpperCase(); + Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); + if (data == null) { + Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " + + "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" + + " or by bluez.\n"); + return false; + } + return setPairingConfirmationNative(address, confirm, data.intValue()); + } + + public synchronized boolean cancelPairingUserInput(String address) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); if (!BluetoothDevice.checkBluetoothAddress(address)) { @@ -968,12 +999,12 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { address = address.toUpperCase(); Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); if (data == null) { - Log.w(TAG, "cancelPin(" + address + ") called but no native data available, " + - "ignoring. Maybe the PasskeyAgent Request was already cancelled by the remote " + - "or by bluez.\n"); + Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " + + "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " + + "by the remote or by bluez.\n"); return false; } - return cancelPinNative(address, data.intValue()); + return cancelPairingUserInputNative(address, data.intValue()); } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @@ -1160,7 +1191,10 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { private native int getDeviceServiceChannelNative(String objectPath, String uuid, int attributeId); - private native boolean cancelPinNative(String address, int nativeData); + private native boolean cancelPairingUserInputNative(String address, int nativeData); private native boolean setPinNative(String address, String pin, int nativeData); + private native boolean setPasskeyNative(String address, int passkey, int nativeData); + private native boolean setPairingConfirmationNative(String address, boolean confirm, + int nativeData); } diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 76906b6e4d49..dc84d1f376c9 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -317,23 +317,53 @@ class BluetoothEventLoop { } mBluetoothService.setRemoteDeviceProperty(address, name, uuid); } - } - private void onRequestPinCode(String objectPath, int nativeData) { + private String checkPairingRequestAndGetAddress(String objectPath, int nativeData) { String address = mBluetoothService.getAddressFromObjectPath(objectPath); if (address == null) { - Log.e(TAG, "Unable to get device address in onRequestPinCode, returning null"); - return; + Log.e(TAG, "Unable to get device address in checkPairingRequestAndGetAddress, " + + "returning null"); + return null; } address = address.toUpperCase(); mPasskeyAgentRequestData.put(address, new Integer(nativeData)); if (mBluetoothService.getBluetoothState() == BluetoothDevice.BLUETOOTH_STATE_TURNING_OFF) { // shutdown path - mBluetoothService.cancelPin(address); - return; + mBluetoothService.cancelPairingUserInput(address); + return null; } + return address; + } + + private void onRequestConfirmation(String objectPath, int passkey, int nativeData) { + String address = checkPairingRequestAndGetAddress(objectPath, nativeData); + if (address == null) return; + + Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION); + intent.putExtra(BluetoothIntent.ADDRESS, address); + intent.putExtra(BluetoothIntent.PASSKEY, passkey); + intent.putExtra(BluetoothIntent.PAIRING_VARIANT, + BluetoothDevice.PAIRING_VARIANT_CONFIRMATION); + mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); + return; + } + + private void onRequestPasskey(String objectPath, int nativeData) { + String address = checkPairingRequestAndGetAddress(objectPath, nativeData); + if (address == null) return; + + Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION); + intent.putExtra(BluetoothIntent.ADDRESS, address); + intent.putExtra(BluetoothIntent.PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_PASSKEY); + mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); + return; + } + + private void onRequestPinCode(String objectPath, int nativeData) { + String address = checkPairingRequestAndGetAddress(objectPath, nativeData); + if (address == null) return; if (mBluetoothService.getBondState().getBondState(address) == BluetoothDevice.BOND_BONDING) { @@ -358,6 +388,7 @@ class BluetoothEventLoop { } Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); + intent.putExtra(BluetoothIntent.PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_PIN); mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); return; } @@ -386,9 +417,9 @@ class BluetoothEventLoop { } private void onAgentCancel() { - // We immediately response to DBUS Authorize() so this should not - // usually happen - log("onAgentCancel"); + Intent intent = new Intent(BluetoothIntent.PAIRING_CANCEL_ACTION); + mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); + return; } private void onRestartRequired() { diff --git a/core/java/android/server/search/SearchDialogWrapper.java b/core/java/android/server/search/SearchDialogWrapper.java index 70c7d7313bae..d3ef5de8634f 100644 --- a/core/java/android/server/search/SearchDialogWrapper.java +++ b/core/java/android/server/search/SearchDialogWrapper.java @@ -45,8 +45,6 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { private static final String TAG = "SearchManagerService"; private static final boolean DBG = false; - private static final String DISABLE_SEARCH_PROPERTY = "dev.disablesearchdialog"; - private static final String SEARCH_UI_THREAD_NAME = "SearchDialog"; private static final int SEARCH_UI_THREAD_PRIORITY = android.os.Process.THREAD_PRIORITY_DEFAULT; @@ -88,12 +86,11 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { // Identity of currently resumed activity. private int mResumedIdent = 0; - - // Allows disabling of search dialog for stress testing runs - private final boolean mDisabledOnBoot; // True if we have registered our receivers. private boolean mReceiverRegistered; + + private volatile boolean mVisible = false; /** * Creates a new search dialog wrapper and a search UI thread. The search dialog itself will @@ -104,8 +101,6 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { public SearchDialogWrapper(Context context) { mContext = context; - mDisabledOnBoot = !TextUtils.isEmpty(SystemProperties.get(DISABLE_SEARCH_PROPERTY)); - // Create the search UI thread HandlerThread t = new HandlerThread(SEARCH_UI_THREAD_NAME, SEARCH_UI_THREAD_PRIORITY); t.start(); @@ -115,6 +110,10 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { mSearchUiThread.sendEmptyMessage(MSG_INIT); } + public boolean isVisible() { + return mVisible; + } + /** * Initializes the search UI. * Must be called from the search UI thread. @@ -151,8 +150,10 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { - if (DBG) debug(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - performStopSearch(); + if (!"search".equals(intent.getStringExtra("reason"))) { + if (DBG) debug(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + performStopSearch(); + } } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { if (DBG) debug(Intent.ACTION_CONFIGURATION_CHANGED); performOnConfigurationChanged(); @@ -205,7 +206,7 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { * Can be called from any thread. */ public void activityResuming(int ident) { - if (DBG) debug("startSearch()"); + if (DBG) debug("activityResuming(ident=" + ident + ")"); Message msg = Message.obtain(); msg.what = MSG_ACTIVITY_RESUMING; msg.arg1 = ident; @@ -256,20 +257,6 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { } - void updateDialogVisibility() { - if (mStartedIdent != 0) { - // mResumedIdent == 0 means we have just booted and the user - // hasn't yet gone anywhere. - if (mResumedIdent == 0 || mStartedIdent == mResumedIdent) { - if (DBG) Log.v(TAG, "******************* DIALOG: show"); - mSearchDialog.show(); - } else { - if (DBG) Log.v(TAG, "******************* DIALOG: hide"); - mSearchDialog.hide(); - } - } - } - /** * Actually launches the search UI. * This must be called on the search UI thread. @@ -283,19 +270,20 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { int ident) { if (DBG) debug("performStartSearch()"); - if (mDisabledOnBoot) { - Log.d(TAG, "ignoring start search request because " + DISABLE_SEARCH_PROPERTY - + " system property is set."); - return; - } - registerBroadcastReceiver(); mCallback = searchManagerCallback; + + // clean up any hidden dialog that we were waiting to resume + if (mStartedIdent != 0) { + mSearchDialog.dismiss(); + } + mStartedIdent = ident; if (DBG) Log.v(TAG, "******************* DIALOG: start"); + mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData, globalSearch); - updateDialogVisibility(); + mVisible = true; } /** @@ -306,6 +294,7 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { if (DBG) debug("performStopSearch()"); if (DBG) Log.v(TAG, "******************* DIALOG: cancel"); mSearchDialog.cancel(); + mVisible = false; mStartedIdent = 0; } @@ -317,7 +306,21 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { if (DBG) debug("performResumingActivity(): mStartedIdent=" + mStartedIdent + ", resuming: " + ident); this.mResumedIdent = ident; - updateDialogVisibility(); + if (mStartedIdent != 0) { + if (mStartedIdent == mResumedIdent) { + // we are resuming into the activity where we previously hid the dialog, bring it + // back + if (DBG) Log.v(TAG, "******************* DIALOG: show"); + mSearchDialog.show(); + mVisible = true; + } else { + // resuming into some other activity; hide ourselves in case we ever come back + // so we can show ourselves quickly again + if (DBG) Log.v(TAG, "******************* DIALOG: hide"); + mSearchDialog.hide(); + mVisible = false; + } + } } /** @@ -333,27 +336,38 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { */ public void onDismiss(DialogInterface dialog) { if (DBG) debug("onDismiss()"); - if (mCallback != null) { - try { - // should be safe to do on the search UI thread, since it's a oneway interface - mCallback.onDismiss(); - } catch (DeadObjectException ex) { - // The process that hosted the callback has died, do nothing - } catch (RemoteException ex) { - Log.e(TAG, "onDismiss() failed: " + ex); - } - // we don't need the callback anymore, release it - mCallback = null; - } + mStartedIdent = 0; + mVisible = false; + callOnDismiss(); + + // we don't need the callback anymore, release it + mCallback = null; unregisterBroadcastReceiver(); } + /** * Called by {@link SearchDialog} when the user or activity cancels search. * Whenever this method is called, {@link #onDismiss} is always called afterwards. */ public void onCancel(DialogInterface dialog) { if (DBG) debug("onCancel()"); + callOnCancel(); + } + + private void callOnDismiss() { + if (mCallback == null) return; + try { + // should be safe to do on the search UI thread, since it's a oneway interface + mCallback.onDismiss(); + } catch (DeadObjectException ex) { + // The process that hosted the callback has died, do nothing + } catch (RemoteException ex) { + Log.e(TAG, "onDismiss() failed: " + ex); + } + } + + private void callOnCancel() { if (mCallback != null) { try { // should be safe to do on the search UI thread, since it's a oneway interface diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java index 762991260dad..fdeb8f9e3fc2 100644 --- a/core/java/android/server/search/SearchManagerService.java +++ b/core/java/android/server/search/SearchManagerService.java @@ -238,4 +238,8 @@ public class SearchManagerService extends ISearchManager.Stub { getSearchDialog().stopSearch(); } + public boolean isVisible() { + return mSearchDialog != null && mSearchDialog.isVisible(); + } + } diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index 9071bf029f9e..bfab49d60fa1 100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -27,17 +27,31 @@ import android.os.*; */ public class DisplayMetrics { /** + * Standard quantized DPI for low-density screens. + */ + public static final int DENSITY_LOW = 120; + + /** + * Standard quantized DPI for medium-density screens. + */ + public static final int DENSITY_MEDIUM = 160; + + /** + * Standard quantized DPI for high-density screens. + */ + public static final int DENSITY_HIGH = 240; + + /** * The reference density used throughout the system. - * - * @hide Pending API council approval */ - public static final int DEFAULT_DENSITY = 160; + public static final int DENSITY_DEFAULT = DENSITY_MEDIUM; /** * The device's density. - * @hide + * @hide becase eventually this should be able to change while + * running, so shouldn't be a constant. */ - public static final int DEVICE_DENSITY = getDeviceDensity(); + public static final int DENSITY_DEVICE = getDeviceDensity(); /** * The absolute width of the display in pixels. @@ -62,7 +76,7 @@ public class DisplayMetrics { * 320x480 but the screen size remained 1.5"x2" then the density would be * increased (probably to 1.5). * - * @see #DEFAULT_DENSITY + * @see #DENSITY_DEFAULT */ public float density; /** @@ -95,10 +109,10 @@ public class DisplayMetrics { public void setToDefaults() { widthPixels = 0; heightPixels = 0; - density = DEVICE_DENSITY / (float) DEFAULT_DENSITY; + density = DENSITY_DEVICE / (float) DENSITY_DEFAULT; scaledDensity = density; - xdpi = DEVICE_DENSITY; - ydpi = DEVICE_DENSITY; + xdpi = DENSITY_DEVICE; + ydpi = DENSITY_DEVICE; } /** @@ -176,6 +190,6 @@ public class DisplayMetrics { // The reason for this is that ro.sf.lcd_density is write-once and is // set by the init process when it parses build.prop before anything else. return SystemProperties.getInt("qemu.sf.lcd_density", - SystemProperties.getInt("ro.sf.lcd_density", DEFAULT_DENSITY)); + SystemProperties.getInt("ro.sf.lcd_density", DENSITY_DEFAULT)); } } diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java index d4ba9e21ac13..ed4529851d8f 100644 --- a/core/java/android/util/TypedValue.java +++ b/core/java/android/util/TypedValue.java @@ -140,12 +140,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 + * treated as the system's default density value: {@link DisplayMetrics#DENSITY_DEFAULT}. */ public static final int DENSITY_DEFAULT = 0; + /** + * If {@link #density} is equal to this value, then there is no density + * associated with the resource and it should not be scaled. + */ + public static final int DENSITY_NONE = 0xffff; + /* ------------------------------------------------------------ */ /** The type held by this value, as defined by the constants here. @@ -171,8 +175,6 @@ public class TypedValue { /** * If the Value came from a resource, this holds the corresponding pixel density. - * - * @hide Pending API council approval * */ public int density; diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 13a6e7aacf19..25bbb6a3bf5d 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -17,7 +17,6 @@ package android.view; import android.content.Context; -import android.content.res.CompatibilityInfo; import android.content.res.CompatibilityInfo.Translator; import android.graphics.Canvas; import android.graphics.PixelFormat; @@ -257,9 +256,9 @@ public class SurfaceView extends View { public boolean dispatchTouchEvent(MotionEvent event) { // SurfaceView uses pre-scaled size unless fixed size is requested. This hook // scales the event back to the pre-scaled coordinates for such surface. - if (mRequestedWidth < 0 && mTranslator != null) { + if (mScaled) { MotionEvent scaledBack = MotionEvent.obtain(event); - scaledBack.scale(mTranslator.applicationScale); + mTranslator.translateEventInScreenToAppWindow(event); try { return super.dispatchTouchEvent(scaledBack); } finally { @@ -291,12 +290,15 @@ public class SurfaceView extends View { public void setWindowType(int type) { mWindowType = type; } + + boolean mScaled = false; private void updateWindow(boolean force) { if (!mHaveFrame) { return; } - mTranslator = ((ViewRoot)getRootView().getParent()).mTranslator; + ViewRoot viewRoot = (ViewRoot) getRootView().getParent(); + mTranslator = viewRoot.mTranslator; float appScale = mTranslator == null ? 1.0f : mTranslator.applicationScale; @@ -310,6 +312,9 @@ public class SurfaceView extends View { if (mRequestedWidth <= 0 && mTranslator != null) { myWidth *= appScale; myHeight *= appScale; + mScaled = true; + } else { + mScaled = false; } getLocationInWindow(mLocation); @@ -353,8 +358,10 @@ public class SurfaceView extends View { | WindowManager.LayoutParams.FLAG_SCALED | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE - | WindowManager.LayoutParams.FLAG_NO_COMPATIBILITY_SCALING ; + if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) { + mLayout.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; + } mLayout.memoryType = mRequestedType; @@ -534,6 +541,7 @@ public class SurfaceView extends View { private SurfaceHolder mSurfaceHolder = new SurfaceHolder() { private static final String LOG_TAG = "SurfaceHolder"; + private int mSaveCount; public boolean isCreating() { return mIsCreating; @@ -628,6 +636,10 @@ public class SurfaceView extends View { if (localLOGV) Log.i(TAG, "Returned canvas: " + c); if (c != null) { mLastLockTime = SystemClock.uptimeMillis(); + if (mScaled) { + mSaveCount = c.save(); + mTranslator.translateCanvas(c); + } return c; } @@ -650,6 +662,9 @@ public class SurfaceView extends View { } public void unlockCanvasAndPost(Canvas canvas) { + if (mScaled) { + canvas.restoreToCount(mSaveCount); + } mSurface.unlockCanvasAndPost(canvas); mSurfaceLock.unlock(); } diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 1e3cdb34f436..2acf79081306 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -504,8 +504,12 @@ public final class ViewRoot extends Handler implements ViewParent, void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) { synchronized (this) { int oldSoftInputMode = mWindowAttributes.softInputMode; + // preserve compatible window flag if exists. + int compatibleWindowFlag = + mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; mWindowAttributes.copyFrom(attrs); - + mWindowAttributes.flags |= compatibleWindowFlag; + if (newView) { mSoftInputMode = attrs.softInputMode; requestLayout(); @@ -1308,7 +1312,8 @@ public final class ViewRoot extends Handler implements ViewParent, if (DEBUG_DRAW) { Context cxt = mView.getContext(); Log.i(TAG, "Drawing: package:" + cxt.getPackageName() + - ", metrics=" + mView.getContext().getResources().getDisplayMetrics()); + ", metrics=" + cxt.getResources().getDisplayMetrics() + + ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo()); } int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); try { diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index e96a15ba05f2..6a26a3144128 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -483,19 +483,12 @@ public interface WindowManager extends ViewManager { * {@hide} */ public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000; - /** Window flag: special flag to let a window ignore the compatibility scaling. - * This is used by SurfaceView to pass this info into ViewRoot, and not used - * by WindowManager. - * - * {@hide} */ - public static final int FLAG_NO_COMPATIBILITY_SCALING = 0x00100000; - /** Window flag: special flag to limit the size of the window to be * original size ([320x480] x density). Used to create window for applications * running under compatibility mode. * * {@hide} */ - public static final int FLAG_COMPATIBLE_WINDOW = 0x00200000; + public static final int FLAG_COMPATIBLE_WINDOW = 0x00100000; /** Window flag: a special option intended for system dialogs. When * this flag is set, the window will demand focus unconditionally when @@ -986,6 +979,9 @@ public interface WindowManager extends ViewManager { sb.append(" or="); sb.append(screenOrientation); } + if ((flags & FLAG_COMPATIBLE_WINDOW) != 0) { + sb.append(" compatible=true"); + } sb.append('}'); return sb.toString(); } diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index 5a7a233c14fa..e6ccd70ab74c 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -550,12 +550,14 @@ class BrowserFrame extends Handler { mCallbackProxy.onLoadResource(url); if (LoadListener.getNativeLoaderCount() > MAX_OUTSTANDING_REQUESTS) { + // send an error message, so that loadListener can be deleted + // after this is returned. This is important as LoadListener's + // nativeError will remove the request from its DocLoader's request + // list. But the set up is not done until this method is returned. loadListener.error( android.net.http.EventHandler.ERROR, mContext.getString( com.android.internal.R.string.httpErrorTooManyRequests)); - loadListener.notifyError(); - loadListener.tearDown(); - return null; + return loadListener; } // during synchronous load, the WebViewCore thread is blocked, so we diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index 2be74851aa17..da1443c6db3e 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -118,7 +118,11 @@ import java.util.ArrayList; if (!isArrowKey && mWebView.nativeFocusNodePointer() != mNodePointer) { mWebView.nativeClearCursor(); - remove(); + // Do not call remove() here, which hides the soft keyboard. If + // the soft keyboard is being displayed, the user will still want + // it there. + mWebView.removeView(this); + mWebView.requestFocus(); return mWebView.dispatchKeyEvent(event); } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 4d345bbae0b7..c1ba690cf9d2 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -4918,6 +4918,11 @@ public class WebView extends AbsoluteLayout } } setNewZoomScale(scale, false); + // As we are on a new page, remove the WebTextView. This + // is necessary for page loads driven by webkit, and in + // particular when the user was on a password field, so + // the WebTextView was visible. + clearTextEntry(); break; case MOVE_OUT_OF_PLUGIN: if (nativePluginEatsNavKey()) { diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 20f72396a9d1..99ceec276f49 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -32,6 +32,8 @@ import android.os.Process; import android.util.Log; import android.util.SparseBooleanArray; import android.view.KeyEvent; +import android.view.SurfaceHolder; +import android.view.SurfaceView; import java.util.ArrayList; @@ -1826,6 +1828,76 @@ final class WebViewCore { } } + // This class looks like a SurfaceView to native code. In java, we can + // assume the passed in SurfaceView is this class so we can talk to the + // ViewManager through the ChildView. + private class SurfaceViewProxy extends SurfaceView + implements SurfaceHolder.Callback { + private final ViewManager.ChildView mChildView; + private int mPointer; + SurfaceViewProxy(Context context, ViewManager.ChildView childView, + int pointer) { + super(context); + setWillNotDraw(false); // this prevents the black box artifact + getHolder().addCallback(this); + mChildView = childView; + mChildView.mView = this; + mPointer = pointer; + } + void destroy() { + mPointer = 0; + mChildView.removeView(); + } + void attach(int x, int y, int width, int height) { + mChildView.attachView(x, y, width, height); + } + + // SurfaceHolder.Callback methods + public void surfaceCreated(SurfaceHolder holder) { + if (mPointer != 0) { + nativeSurfaceChanged(mPointer, 0, 0, 0, 0); + } + } + public void surfaceChanged(SurfaceHolder holder, int format, int width, + int height) { + if (mPointer != 0) { + nativeSurfaceChanged(mPointer, 1, format, width, height); + } + } + public void surfaceDestroyed(SurfaceHolder holder) { + if (mPointer != 0) { + nativeSurfaceChanged(mPointer, 2, 0, 0, 0); + } + } + } + + // PluginWidget functions for mainting SurfaceViews for the Surface drawing + // model. + private SurfaceView createSurface(int nativePointer) { + if (mWebView == null) { + return null; + } + return new SurfaceViewProxy(mContext, + mWebView.mViewManager.createView(), nativePointer); + } + + private void destroySurface(SurfaceView surface) { + SurfaceViewProxy proxy = (SurfaceViewProxy) surface; + proxy.destroy(); + } + + private void attachSurface(SurfaceView surface, int x, int y, + int width, int height) { + SurfaceViewProxy proxy = (SurfaceViewProxy) surface; + proxy.attach(x, y, width, height); + } + + // Callback for the SurfaceHolder.Callback. Called for all the surface + // callbacks. The state parameter is one of Created(0), Changed(1), + // Destroyed(2). + private native void nativeSurfaceChanged(int pointer, int state, int format, + int width, int height); + private native void nativePause(); private native void nativeResume(); private native void nativeFreeMemory(); diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index ae509c503b01..09a547ff5e11 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -210,8 +210,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe if (mDropDownAlwaysVisible && mPopup.isShowing() && mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED) { - mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); - showDropDown(); + ensureImeVisible(); } } @@ -1072,11 +1071,21 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe /** * Issues a runnable to show the dropdown as soon as possible. * - * @hide internal used only by Search Dialog + * @hide internal used only by SearchDialog */ public void showDropDownAfterLayout() { post(mShowDropDownRunnable); } + + /** + * Ensures that the drop down is not obscuring the IME. + * + * @hide internal used only here and SearchDialog + */ + public void ensureImeVisible() { + mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); + showDropDown(); + } /** * <p>Displays the drop down on screen.</p> @@ -1270,11 +1279,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } } - // Max height available on the screen for a popup. If this AutoCompleteTextView has - // the dropDownAlwaysVisible attribute, and the input method is not currently required, - // we then we ask for the height ignoring any bottom decorations like the input method. - // Otherwise we respect the input method. - boolean ignoreBottomDecorations = mDropDownAlwaysVisible && + // Max height available on the screen for a popup. + boolean ignoreBottomDecorations = mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED; final int maxHeight = mPopup.getMaxAvailableHeight( getDropDownAnchorView(), mDropDownVerticalOffset, ignoreBottomDecorations); diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index e62dda58ba42..24c0e2a4a2d9 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -1295,11 +1295,8 @@ public class RelativeLayout extends ViewGroup { if (rule > 0) { // The node this node depends on final Node dependency = keyNodes.get(rule); - if (dependency == node) { - throw new IllegalStateException("A view cannot have a dependency" + - " on itself"); - } - if (dependency == null) { + // Skip unknowns and self dependencies + if (dependency == null || dependency == node) { continue; } // Add the current node as a dependent diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index c322b17e5991..63dc9e831360 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -746,6 +746,15 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) opt.optionString = "-Xincludeselectedmethod"; mOptions.add(opt); } + + /* + * Enable profile collection on JIT'ed code. + */ + property_get("dalvik.vm.jit.profile", propBuf, ""); + if (strlen(propBuf) > 0) { + opt.optionString = "-Xjitprofile"; + mOptions.add(opt); + } #endif if (executionMode == kEMIntPortable) { diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 957b825c3cc1..002d3db3f55e 100644 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -224,7 +224,7 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, SkBitmap bitmap;
bitmap.setConfig(config, width, height);
- if (!GraphicsJNI::setJavaPixelRef(env, &bitmap, NULL)) {
+ if (!GraphicsJNI::setJavaPixelRef(env, &bitmap, NULL, true)) {
return NULL;
}
@@ -240,7 +240,7 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,
SkBitmap::Config dstConfig, jboolean isMutable) {
SkBitmap result;
- JavaPixelAllocator allocator(env);
+ JavaPixelAllocator allocator(env, true);
if (!src->copyTo(&result, dstConfig, &allocator)) {
return NULL;
@@ -356,7 +356,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { }
}
- if (!GraphicsJNI::setJavaPixelRef(env, bitmap, ctable)) {
+ if (!GraphicsJNI::setJavaPixelRef(env, bitmap, ctable, true)) {
ctable->safeUnref();
delete bitmap;
return NULL;
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 137707fa93bf..0c842650962f 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -23,6 +23,7 @@ static jfieldID gOptions_configFieldID; static jfieldID gOptions_ditherFieldID; static jfieldID gOptions_purgeableFieldID; static jfieldID gOptions_shareableFieldID; +static jfieldID gOptions_nativeAllocFieldID; static jfieldID gOptions_widthFieldID; static jfieldID gOptions_heightFieldID; static jfieldID gOptions_mimeFieldID; @@ -300,6 +301,11 @@ static bool optionsShareable(JNIEnv* env, jobject options) { env->GetBooleanField(options, gOptions_shareableFieldID); } +static bool optionsReportSizeToVM(JNIEnv* env, jobject options) { + return NULL == options || + !env->GetBooleanField(options, gOptions_nativeAllocFieldID); +} + static jobject nullObjectReturn(const char msg[]) { if (msg) { SkDebugf("--- %s\n", msg); @@ -330,6 +336,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, SkBitmap::Config prefConfig = SkBitmap::kNo_Config; bool doDither = true; bool isPurgeable = allowPurgeable && optionsPurgeable(env, options); + bool reportSizeToVM = optionsReportSizeToVM(env, options); if (NULL != options) { sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); @@ -355,7 +362,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, decoder->setDitherImage(doDither); NinePatchPeeker peeker; - JavaPixelAllocator javaAllocator(env); + JavaPixelAllocator javaAllocator(env, reportSizeToVM); SkBitmap* bitmap = new SkBitmap; Res_png_9patch dummy9Patch; @@ -699,6 +706,7 @@ int register_android_graphics_BitmapFactory(JNIEnv* env) { gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z"); gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z"); gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z"); + gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z"); gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I"); gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I"); gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;"); diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 6eebbdcfb563..6e159a8853d5 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -5,6 +5,7 @@ #include "SkRegion.h" #include <android_runtime/AndroidRuntime.h> +//#define REPORT_SIZE_TO_JVM //#define TRACK_LOCK_COUNT void doThrow(JNIEnv* env, const char* exc, const char* msg) { @@ -444,7 +445,7 @@ private: }; bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, - SkColorTable* ctable) { + SkColorTable* ctable, bool reportSizeToVM) { Sk64 size64 = bitmap->getSize64(); if (size64.isNeg() || !size64.is32()) { doThrow(env, "java/lang/IllegalArgumentException", @@ -453,35 +454,41 @@ bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, } size_t size = size64.get32(); - // SkDebugf("-------------- inform VM we've allocated %d bytes\n", size); jlong jsize = size; // the VM wants longs for the size - bool r = env->CallBooleanMethod(gVMRuntime_singleton, - gVMRuntime_trackExternalAllocationMethodID, - jsize); - if (GraphicsJNI::hasException(env)) { - return false; - } - if (!r) { - LOGE("VM won't let us allocate %zd bytes\n", size); - doThrowOOME(env, "bitmap size exceeds VM budget"); - return false; + if (reportSizeToVM) { + // SkDebugf("-------------- inform VM we've allocated %d bytes\n", size); + bool r = env->CallBooleanMethod(gVMRuntime_singleton, + gVMRuntime_trackExternalAllocationMethodID, + jsize); + if (GraphicsJNI::hasException(env)) { + return false; + } + if (!r) { + LOGE("VM won't let us allocate %zd bytes\n", size); + doThrowOOME(env, "bitmap size exceeds VM budget"); + return false; + } } - // call the version of malloc that returns null on failure void* addr = sk_malloc_flags(size, 0); if (NULL == addr) { - // SkDebugf("-------------- inform VM we're releasing %d bytes which we couldn't allocate\n", size); - // we didn't actually allocate it, so inform the VM - env->CallVoidMethod(gVMRuntime_singleton, - gVMRuntime_trackExternalFreeMethodID, - jsize); - if (!GraphicsJNI::hasException(env)) { - doThrowOOME(env, "bitmap size too large for malloc"); + if (reportSizeToVM) { + // SkDebugf("-------------- inform VM we're releasing %d bytes which we couldn't allocate\n", size); + // we didn't actually allocate it, so inform the VM + env->CallVoidMethod(gVMRuntime_singleton, + gVMRuntime_trackExternalFreeMethodID, + jsize); + if (!GraphicsJNI::hasException(env)) { + doThrowOOME(env, "bitmap size too large for malloc"); + } } return false; } - bitmap->setPixelRef(new AndroidPixelRef(env, addr, size, ctable))->unref(); + SkPixelRef* pr = reportSizeToVM ? + new AndroidPixelRef(env, addr, size, ctable) : + new SkMallocPixelRef(addr, size, ctable); + bitmap->setPixelRef(pr)->unref(); // since we're already allocated, we lockPixels right away // HeapAllocator behaves this way too bitmap->lockPixels(); @@ -490,12 +497,11 @@ bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, /////////////////////////////////////////////////////////////////////////////// -JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env) : fEnv(env) -{ -} +JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM) + : fEnv(env), fReportSizeToVM(reportSizeToVM) {} bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { - return GraphicsJNI::setJavaPixelRef(fEnv, bitmap, ctable); + return GraphicsJNI::setJavaPixelRef(fEnv, bitmap, ctable, fReportSizeToVM); } //////////////////////////////////////////////////////////////////////////////// diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index e2dc9acf1beb..16925e41ab4f 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -59,7 +59,8 @@ public: Returns true on success. If it returns false, then it failed, and the appropriate exception will have been raised. */ - static bool setJavaPixelRef(JNIEnv*, SkBitmap*, SkColorTable* ctable); + static bool setJavaPixelRef(JNIEnv*, SkBitmap*, SkColorTable* ctable, + bool reportSizeToVM); /** Copy the colors in colors[] to the bitmap, convert to the correct format along the way. @@ -71,12 +72,13 @@ public: class JavaPixelAllocator : public SkBitmap::Allocator { public: - JavaPixelAllocator(JNIEnv* env); + JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM); // overrides virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable); private: JNIEnv* fEnv; + bool fReportSizeToVM; }; class AutoJavaFloatArray { diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index e71e3481862e..44a9e8cbcc58 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -212,8 +212,10 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, // failure: native_init_failure: + env->DeleteGlobalRef(lpCallbackData->audioRecord_class); + env->DeleteGlobalRef(lpCallbackData->audioRecord_ref); delete lpCallbackData; - + native_track_failure: delete lpRecorder; @@ -274,6 +276,8 @@ static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) { thiz, javaAudioRecordFields.nativeCallbackCookie); if (lpCookie) { LOGV("deleting lpCookie: %x\n", (int)lpCookie); + env->DeleteGlobalRef(lpCookie->audioRecord_class); + env->DeleteGlobalRef(lpCookie->audioRecord_ref); delete lpCookie; } diff --git a/core/jni/android_server_BluetoothDeviceService.cpp b/core/jni/android_server_BluetoothDeviceService.cpp index b02a19b135a4..444e6287a4d6 100644 --- a/core/jni/android_server_BluetoothDeviceService.cpp +++ b/core/jni/android_server_BluetoothDeviceService.cpp @@ -437,6 +437,65 @@ static jint isEnabledNative(JNIEnv *env, jobject object) { return -1; } +static jboolean setPairingConfirmationNative(JNIEnv *env, jobject object, + jstring address, bool confirm, + int nativeData) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *msg = (DBusMessage *)nativeData; + DBusMessage *reply; + if (confirm) { + reply = dbus_message_new_method_return(msg); + } else { + reply = dbus_message_new_error(msg, + "org.bluez.Error.Rejected", "User rejected confirmation"); + } + + if (!reply) { + LOGE("%s: Cannot create message reply to RequestConfirmation to " + "D-Bus\n", __FUNCTION__); + dbus_message_unref(msg); + return JNI_FALSE; + } + + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(msg); + dbus_message_unref(reply); + return JNI_TRUE; + } +#endif + return JNI_FALSE; +} + +static jboolean setPasskeyNative(JNIEnv *env, jobject object, jstring address, + int passkey, int nativeData) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *msg = (DBusMessage *)nativeData; + DBusMessage *reply = dbus_message_new_method_return(msg); + if (!reply) { + LOGE("%s: Cannot create message reply to return Passkey code to " + "D-Bus\n", __FUNCTION__); + dbus_message_unref(msg); + return JNI_FALSE; + } + + dbus_message_append_args(reply, DBUS_TYPE_UINT32, (uint32_t *)&passkey, + DBUS_TYPE_INVALID); + + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(msg); + dbus_message_unref(reply); + return JNI_TRUE; + } +#endif + return JNI_FALSE; +} + static jboolean setPinNative(JNIEnv *env, jobject object, jstring address, jstring pin, int nativeData) { #ifdef HAVE_BLUETOOTH @@ -467,17 +526,17 @@ static jboolean setPinNative(JNIEnv *env, jobject object, jstring address, return JNI_FALSE; } -static jboolean cancelPinNative(JNIEnv *env, jobject object, jstring address, - int nativeData) { +static jboolean cancelPairingUserInputNative(JNIEnv *env, jobject object, + jstring address, int nativeData) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); native_data_t *nat = get_native_data(env, object); if (nat) { DBusMessage *msg = (DBusMessage *)nativeData; DBusMessage *reply = dbus_message_new_error(msg, - "org.bluez.Error.Canceled", "PIN Entry was canceled"); + "org.bluez.Error.Canceled", "Pairing User Input was canceled"); if (!reply) { - LOGE("%s: Cannot create message reply to return PIN cancel to " + LOGE("%s: Cannot create message reply to return cancelUserInput to" "D-BUS\n", __FUNCTION__); dbus_message_unref(msg); return JNI_FALSE; @@ -665,8 +724,12 @@ static JNINativeMethod sMethods[] = { {"getDeviceServiceChannelNative", "(Ljava/lang/String;Ljava/lang/String;I)I", (void *)getDeviceServiceChannelNative}, + {"setPairingConfirmationNative", "(Ljava/lang/String;ZI)Z", + (void *)setPairingConfirmationNative}, + {"setPasskeyNative", "(Ljava/lang/String;II)Z", (void *)setPasskeyNative}, {"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative}, - {"cancelPinNative", "(Ljava/lang/String;I)Z", (void *)cancelPinNative}, + {"cancelPairingUserInputNative", "(Ljava/lang/String;I)Z", + (void *)cancelPairingUserInputNative}, }; int register_android_server_BluetoothDeviceService(JNIEnv *env) { diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index 0857cb3a5225..4a13e8004ea8 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -50,6 +50,8 @@ static jmethodID method_onCreatePairedDeviceResult; static jmethodID method_onGetDeviceServiceChannelResult; static jmethodID method_onRequestPinCode; +static jmethodID method_onRequestPasskey; +static jmethodID method_onRequestConfirmation; static jmethodID method_onAgentAuthorize; static jmethodID method_onAgentCancel; @@ -89,6 +91,10 @@ static void classInitNative(JNIEnv* env, jclass clazz) { method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V"); method_onRequestPinCode = env->GetMethodID(clazz, "onRequestPinCode", "(Ljava/lang/String;I)V"); + method_onRequestPasskey = env->GetMethodID(clazz, "onRequestPasskey", + "(Ljava/lang/String;I)V"); + method_onRequestConfirmation = env->GetMethodID(clazz, "onRequestConfirmation", + "(Ljava/lang/String;II)V"); field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I"); #endif @@ -872,6 +878,38 @@ DBusHandlerResult agent_event_filter(DBusConnection *conn, int(msg)); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_method_call(msg, + "org.bluez.Agent", "RequestPasskey")) { + char *object_path; + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_OBJECT_PATH, &object_path, + DBUS_TYPE_INVALID)) { + LOGE("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + dbus_message_ref(msg); // increment refcount because we pass to java + env->CallVoidMethod(nat->me, method_onRequestPasskey, + env->NewStringUTF(object_path), + int(msg)); + } else if (dbus_message_is_method_call(msg, + "org.bluez.Agent", "RequestConfirmation")) { + char *object_path; + uint32_t passkey; + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_OBJECT_PATH, &object_path, + DBUS_TYPE_UINT32, &passkey, + DBUS_TYPE_INVALID)) { + LOGE("%s: Invalid arguments for RequestConfirmation() method", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + dbus_message_ref(msg); // increment refcount because we pass to java + env->CallVoidMethod(nat->me, method_onRequestConfirmation, + env->NewStringUTF(object_path), + passkey, + int(msg)); + return DBUS_HANDLER_RESULT_HANDLED; + } else if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Release")) { // reply DBusMessage *reply = dbus_message_new_method_return(msg); diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 770c75525225..09a0d70bcf40 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -368,7 +368,7 @@ static int pid_compare(const void* v1, const void* v2) return *((const jint*)v1) - *((const jint*)v2); } -jint android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz) +static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz) { int fd = open("/proc/meminfo", O_RDONLY); @@ -388,7 +388,7 @@ jint android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz) buffer[len] = 0; int numFound = 0; - int mem = 0; + jlong mem = 0; static const char* const sums[] = { "MemFree:", "Cached:", NULL }; static const int sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), NULL }; @@ -407,7 +407,7 @@ jint android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz) p++; if (*p == 0) p--; } - mem += atoi(num) * 1024; + mem += atoll(num) * 1024; numFound++; break; } @@ -857,7 +857,7 @@ static const JNINativeMethod methods[] = { {"setGid", "(I)I", (void*)android_os_Process_setGid}, {"sendSignal", "(II)V", (void*)android_os_Process_sendSignal}, {"supportsProcesses", "()Z", (void*)android_os_Process_supportsProcesses}, - {"getFreeMemory", "()I", (void*)android_os_Process_getFreeMemory}, + {"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory}, {"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V", (void*)android_os_Process_readProcLines}, {"getPids", "(Ljava/lang/String;[I)[I", (void*)android_os_Process_getPids}, {"readProcFile", "(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_readProcFile}, diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index e2e93eb87e67..df659ef224de 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -18,6 +18,7 @@ package android.graphics; import android.os.Parcel; import android.os.Parcelable; +import android.util.DisplayMetrics; import java.io.OutputStream; import java.nio.Buffer; @@ -31,8 +32,6 @@ public final class Bitmap implements Parcelable { * * @see Bitmap#getDensityScale() * @see Bitmap#setDensityScale(float) - * - * @hide pending API council approval */ public static final float DENSITY_SCALE_UNKNOWN = -1.0f; @@ -84,11 +83,9 @@ public final class Bitmap implements Parcelable { * @see #setDensityScale(float) * @see #isAutoScalingEnabled() * @see #setAutoScalingEnabled(boolean) - * @see android.util.DisplayMetrics#DEFAULT_DENSITY + * @see android.util.DisplayMetrics#DENSITY_DEFAULT * @see android.util.DisplayMetrics#density * @see #DENSITY_SCALE_UNKNOWN - * - * @hide pending API council approval */ public float getDensityScale() { return mDensityScale; @@ -106,11 +103,9 @@ public final class Bitmap implements Parcelable { * @see #getDensityScale() * @see #isAutoScalingEnabled() * @see #setAutoScalingEnabled(boolean) - * @see android.util.DisplayMetrics#DEFAULT_DENSITY + * @see android.util.DisplayMetrics#DENSITY_DEFAULT * @see android.util.DisplayMetrics#density * @see #DENSITY_SCALE_UNKNOWN - * - * @hide pending API council approval */ public void setDensityScale(float densityScale) { mDensityScale = densityScale; @@ -132,8 +127,6 @@ public final class Bitmap implements Parcelable { * @see #setAutoScalingEnabled(boolean) * @see #getDensityScale() * @see #setDensityScale(float) - * - * @hide pending API council approval */ public boolean isAutoScalingEnabled() { return mAutoScaling; @@ -150,8 +143,6 @@ public final class Bitmap implements Parcelable { * the bitmap will never be automatically scaled at drawing time.</p> * * @param autoScalingEnabled True to scale the bitmap at drawing time, false otherwise. - * - * @hide pending API council approval */ public void setAutoScalingEnabled(boolean autoScalingEnabled) { mAutoScaling = autoScalingEnabled; @@ -465,8 +456,8 @@ public final class Bitmap implements Parcelable { // The new bitmap was created from a known bitmap source so assume that // they use the same density scale - bitmap.setDensityScale(source.getDensityScale()); - bitmap.setAutoScalingEnabled(source.isAutoScalingEnabled()); + bitmap.mDensityScale = source.mDensityScale; + bitmap.mAutoScaling = source.mAutoScaling; return bitmap; } @@ -615,26 +606,60 @@ public final class Bitmap implements Parcelable { * Convenience method that returns the width of this bitmap divided * by the density scale factor. * + * @param canvas The Canvas the bitmap will be drawn to. * @return The scaled width of this bitmap, according to the density scale factor. - * - * @hide pending API council approval */ - public int getScaledWidth() { - final float scale = getDensityScale(); - return scale == DENSITY_SCALE_UNKNOWN ? getWidth() : (int) (getWidth() / scale); + public int getScaledWidth(Canvas canvas) { + final float scale = mDensityScale; + if (!mAutoScaling || scale < 0) { + return getWidth(); + } + return (int)(getWidth() * canvas.getDensityScale() / scale); } /** * Convenience method that returns the height of this bitmap divided * by the density scale factor. * + * @param canvas The Canvas the bitmap will be drawn to. * @return The scaled height of this bitmap, according to the density scale factor. + */ + public int getScaledHeight(Canvas canvas) { + final float scale = mDensityScale; + if (!mAutoScaling || scale < 0) { + return getHeight(); + } + return (int)(getHeight() * canvas.getDensityScale() / scale); + } + + /** + * Convenience method that returns the width of this bitmap divided + * by the density scale factor. + * + * @param metrics The target display metrics. + * @return The scaled width of this bitmap, according to the density scale factor. + */ + public int getScaledWidth(DisplayMetrics metrics) { + final float scale = mDensityScale; + if (!mAutoScaling || scale < 0) { + return getWidth(); + } + return (int)(getWidth() * metrics.density / scale); + } + + /** + * Convenience method that returns the height of this bitmap divided + * by the density scale factor. * - * @hide pending API council approval + * @param metrics The target display metrics. + * @return The scaled height of this bitmap, according to the density scale factor. */ - public int getScaledHeight() { - final float scale = getDensityScale(); - return scale == DENSITY_SCALE_UNKNOWN ? getWidth() : (int) (getHeight() / scale); + public int getScaledHeight(DisplayMetrics metrics) { + final float scale = mDensityScale; + if (!mAutoScaling || scale < 0) { + return getHeight(); + } + return (int)(getHeight() * metrics.density / scale); } /** diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index 2a3998776e2a..975bc1abb90f 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -82,10 +82,8 @@ public class BitmapFactory { /** * The desired pixel density of the bitmap. * - * @see android.util.DisplayMetrics#DEFAULT_DENSITY + * @see android.util.DisplayMetrics#DENSITY_DEFAULT * @see android.util.DisplayMetrics#density - * - * @hide pending API council approval */ public int inDensity; @@ -98,8 +96,6 @@ public class BitmapFactory { * a non-scaled version of the bitmap. In this case, * {@link android.graphics.Bitmap#setAutoScalingEnabled(boolean)} can be used * to properly scale the bitmap at drawing time.</p> - * - * @hide pending API council approval */ public boolean inScaled; @@ -130,6 +126,19 @@ public class BitmapFactory { public boolean inInputShareable; /** + * Normally bitmap allocations count against the dalvik heap, which + * means they help trigger GCs when a lot have been allocated. However, + * in rare cases, the caller may want to allocate the bitmap outside of + * that heap. To request that, set inNativeAlloc to true. In these + * rare instances, it is solely up to the caller to ensure that OOM is + * managed explicitly by calling bitmap.recycle() as soon as such a + * bitmap is no longer needed. + * + * @hide pending API council approval + */ + public boolean inNativeAlloc; + + /** * The resulting width of the bitmap, set independent of the state of * inJustDecodeBounds. However, if there is an error trying to decode, * outWidth will be set to -1. @@ -226,8 +235,6 @@ public class BitmapFactory { /** * Decode a new Bitmap from an InputStream. This InputStream was obtained from * resources, which we pass to be able to scale the bitmap accordingly. - * - * @hide */ public static Bitmap decodeStream(Resources res, TypedValue value, InputStream is, Rect pad, Options opts) { @@ -239,15 +246,19 @@ public class BitmapFactory { Bitmap bm = decodeStream(is, pad, opts); if (bm != null && res != null && value != null) { + final int density = value.density; + if (density == TypedValue.DENSITY_NONE) { + return bm; + } + byte[] np = bm.getNinePatchChunk(); final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np); - final int density = value.density; if (opts.inDensity == 0) { opts.inDensity = density == TypedValue.DENSITY_DEFAULT ? - DisplayMetrics.DEFAULT_DENSITY : density; + DisplayMetrics.DENSITY_DEFAULT : density; } - float scale = opts.inDensity / (float) DisplayMetrics.DEFAULT_DENSITY; + float scale = opts.inDensity / (float) DisplayMetrics.DENSITY_DEFAULT; if (opts.inScaled || isNinePatch) { bm.setDensityScale(1.0f); diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index 4498e1a2e117..da7359772ee3 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -184,8 +184,6 @@ public class Canvas { * * @see #setDensityScale(float) * @see Bitmap#getDensityScale() - * - * @hide pending API council approval */ public float getDensityScale() { if (mBitmap != null) { @@ -205,8 +203,6 @@ public class Canvas { * * @see #getDensityScale() * @see Bitmap#setDensityScale(float) - * - * @hide pending API council approval */ public void setDensityScale(float densityScale) { if (mBitmap != null) { diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h index 9102b40f5ec9..97d55aa0537a 100644 --- a/include/media/MediaPlayerInterface.h +++ b/include/media/MediaPlayerInterface.h @@ -29,7 +29,10 @@ namespace android { +typedef int32_t MetadataType; + class Parcel; +template<typename T> class SortedVector; enum player_type { PV_PLAYER = 1, @@ -112,12 +115,23 @@ public: mCookie = cookie; mNotify = notifyFunc; } // Invoke a generic method on the player by using opaque parcels // for the request and reply. + // // @param request Parcel that is positioned at the start of the // data sent by the java layer. // @param[out] reply Parcel to hold the reply data. Cannot be null. - // @return OK if the invocation was made successfully. A player - // not supporting the direct API should return INVALID_OPERATION. + // @return OK if the call was successful. virtual status_t invoke(const Parcel& request, Parcel *reply) = 0; + + // The Client in the MetadataPlayerService calls this method on + // the native player to retrieve all or a subset of metadata. + // + // @param ids SortedList of metadata ID to be fetch. If empty, all + // the known metadata should be returned. + // @param[inout] records Parcel where the player appends its metadata. + // @return OK if the call was successful. + virtual status_t getMetadata(const SortedVector<MetadataType>& ids, + Parcel *records) = 0; + protected: virtual void sendEvent(int msg, int ext1=0, int ext2=0) { if (mNotify) mNotify(mCookie, msg, ext1, ext2); } diff --git a/include/media/PVPlayer.h b/include/media/PVPlayer.h index d8a677fb8edb..40ccc14b2ecb 100644 --- a/include/media/PVPlayer.h +++ b/include/media/PVPlayer.h @@ -53,6 +53,8 @@ public: virtual status_t setLooping(int loop); virtual player_type playerType() { return PV_PLAYER; } virtual status_t invoke(const Parcel& request, Parcel *reply); + virtual status_t getMetadata(const SortedVector<MetadataType>& ids, + Parcel *records); // make available to PlayerDriver void sendEvent(int msg, int ext1=0, int ext2=0) { MediaPlayerBase::sendEvent(msg, ext1, ext2); } diff --git a/include/ui/Overlay.h b/include/ui/Overlay.h index 9ba2f7b6fd1c..acc9bea4e4b5 100644 --- a/include/ui/Overlay.h +++ b/include/ui/Overlay.h @@ -82,6 +82,10 @@ public: /* release the overlay buffer and post it */ status_t queueBuffer(overlay_buffer_t buffer); + status_t setCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h) ; + + status_t getCrop(uint32_t* x, uint32_t* y, uint32_t* w, uint32_t* h) ; + /* returns the address of a given buffer if supported, NULL otherwise. */ void* getBufferAddress(overlay_buffer_t buffer); diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 93bca4aefc08..381933556d2f 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -825,7 +825,8 @@ struct ResTable_config }; enum { - DENSITY_ANY = 0 + DENSITY_DEFAULT = 0, + DENSITY_NONE = 0xffff }; union { diff --git a/keystore/java/android/security/CertTool.java b/keystore/java/android/security/CertTool.java index 26d22aec57fb..c96cd4f4d907 100644 --- a/keystore/java/android/security/CertTool.java +++ b/keystore/java/android/security/CertTool.java @@ -16,11 +16,19 @@ package android.security; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; + import android.content.Context; import android.content.Intent; import android.security.Keystore; import android.text.TextUtils; - +import android.util.Log; /** * The CertTool class provides the functions to list the certs/keys, @@ -41,12 +49,12 @@ public class CertTool { public static final String KEY_NAMESPACE = "namespace"; public static final String KEY_DESCRIPTION = "description"; - private static final String TAG = "CertTool"; + public static final String TITLE_CA_CERT = "CA Certificate"; + public static final String TITLE_USER_CERT = "User Certificate"; + public static final String TITLE_PKCS12_KEYSTORE = "PKCS12 Keystore"; + public static final String TITLE_PRIVATE_KEY = "Private Key"; - private static final String TITLE_CA_CERT = "CA Certificate"; - private static final String TITLE_USER_CERT = "User Certificate"; - private static final String TITLE_PKCS12_KEYSTORE = "PKCS12 Keystore"; - private static final String TITLE_PRIVATE_KEY = "Private Key"; + private static final String TAG = "CertTool"; private static final String UNKNOWN = "Unknown"; private static final String ISSUER_NAME = "Issuer Name:"; private static final String DISTINCT_NAME = "Distinct Name:"; @@ -58,6 +66,11 @@ public class CertTool { private static final String KEYNAME_DELIMITER = "_"; private static final Keystore sKeystore = Keystore.getInstance(); + private native int getPkcs12Handle(byte[] data, String password); + private native String getPkcs12Certificate(int handle); + private native String getPkcs12PrivateKey(int handle); + private native String popPkcs12CertificateStack(int handle); + private native void freePkcs12Handle(int handle); private native String generateCertificateRequest(int bits, String subject); private native boolean isPkcs12Keystore(byte[] data); private native int generateX509Certificate(byte[] data); @@ -130,10 +143,35 @@ public class CertTool { intent.putExtra(KEY_NAMESPACE + "1", namespace); } + public int addPkcs12Keystore(byte[] p12Data, String password, + String keyname) { + int handle, i = 0; + String pemData; + Log.i("CertTool", "addPkcs12Keystore()"); + + if ((handle = getPkcs12Handle(p12Data, password)) == 0) return -1; + if ((pemData = getPkcs12Certificate(handle)) != null) { + sKeystore.put(USER_CERTIFICATE, keyname, pemData); + } + if ((pemData = getPkcs12PrivateKey(handle)) != null) { + sKeystore.put(USER_KEY, keyname, pemData); + } + while ((pemData = this.popPkcs12CertificateStack(handle)) != null) { + if (i++ > 0) { + sKeystore.put(CA_CERTIFICATE, keyname + i, pemData); + } else { + sKeystore.put(CA_CERTIFICATE, keyname, pemData); + } + } + freePkcs12Handle(handle); + return 0; + } + public synchronized void addCertificate(byte[] data, Context context) { int handle; Intent intent = null; + Log.i("CertTool", "addCertificate()"); if (isPkcs12Keystore(data)) { intent = prepareIntent(TITLE_PKCS12_KEYSTORE, data, USER_KEY, UNKNOWN, UNKNOWN); diff --git a/keystore/jni/cert.c b/keystore/jni/cert.c index cc36b84e99a3..0db28fd0e822 100644 --- a/keystore/jni/cert.c +++ b/keystore/jni/cert.c @@ -136,30 +136,126 @@ err: return ret_code; } -int is_pkcs12(const char *buf, int bufLen) +PKCS12 *get_p12_handle(const char *buf, int bufLen) { - int ret = 0; BIO *bp = NULL; PKCS12 *p12 = NULL; - if (!buf || bufLen < 1) goto err; + if (!buf || (bufLen < 1) || (buf[0] != 48)) goto err; bp = BIO_new(BIO_s_mem()); if (!bp) goto err; - if (buf[0] != 48) goto err; // it is not DER. - if (!BIO_write(bp, buf, bufLen)) goto err; - if ((p12 = d2i_PKCS12_bio(bp, NULL)) != NULL) { - PKCS12_free(p12); - ret = 1; - } + p12 = d2i_PKCS12_bio(bp, NULL); + err: if (bp) BIO_free(bp); + return p12; +} + +PKCS12_KEYSTORE *get_pkcs12_keystore_handle(const char *buf, int bufLen, + const char *passwd) +{ + PKCS12_KEYSTORE *p12store = NULL; + EVP_PKEY *pkey = NULL; + X509 *cert = NULL; + STACK_OF(X509) *certs = NULL; + PKCS12 *p12 = get_p12_handle(buf, bufLen); + + if (p12 == NULL) return NULL; + if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) { + LOGE("Can not parse PKCS12 content"); + PKCS12_free(p12); + return NULL; + } + if ((p12store = malloc(sizeof(PKCS12_KEYSTORE))) == NULL) { + if (cert) X509_free(cert); + if (pkey) EVP_PKEY_free(pkey); + if (certs) sk_X509_free(certs); + } + p12store->p12 = p12; + p12store->pkey = pkey; + p12store->cert = cert; + p12store->certs = certs; + return p12store; +} + +void free_pkcs12_keystore(PKCS12_KEYSTORE *p12store) +{ + if (p12store != NULL) { + if (p12store->cert) X509_free(p12store->cert); + if (p12store->pkey) EVP_PKEY_free(p12store->pkey); + if (p12store->certs) sk_X509_free(p12store->certs); + free(p12store); + } +} + +int is_pkcs12(const char *buf, int bufLen) +{ + int ret = 0; + PKCS12 *p12 = get_p12_handle(buf, bufLen); + if (p12 != NULL) ret = 1; + PKCS12_free(p12); return ret; } +static int convert_to_pem(void *data, int is_cert, char *buf, int size) +{ + int len = 0; + BIO *bio = NULL; + + if (data == NULL) return -1; + + if ((bio = BIO_new(BIO_s_mem())) == NULL) goto err; + if (is_cert) { + if ((len = PEM_write_bio_X509(bio, (X509*)data)) == 0) { + goto err; + } + } else { + if ((len = PEM_write_bio_PrivateKey(bio, (EVP_PKEY *)data, NULL, + NULL, 0, NULL, NULL)) == 0) { + goto err; + } + } + if (len < size && (len = BIO_read(bio, buf, size - 1)) > 0) { + buf[len] = 0; + } +err: + if (bio) BIO_free(bio); + return (len == 0) ? -1 : 0; +} + +int get_pkcs12_certificate(PKCS12_KEYSTORE *p12store, char *buf, int size) +{ + if ((p12store != NULL) && (p12store->cert != NULL)) { + return convert_to_pem((void*)p12store->cert, 1, buf, size); + } + return -1; +} + +int get_pkcs12_private_key(PKCS12_KEYSTORE *p12store, char *buf, int size) +{ + if ((p12store != NULL) && (p12store->pkey != NULL)) { + return convert_to_pem((void*)p12store->pkey, 0, buf, size); + } + return -1; +} + +int pop_pkcs12_certs_stack(PKCS12_KEYSTORE *p12store, char *buf, int size) +{ + X509 *cert = NULL; + + if ((p12store != NULL) && (p12store->certs != NULL) && + ((cert = sk_X509_pop(p12store->certs)) != NULL)) { + int ret = convert_to_pem((void*)cert, 1, buf, size); + X509_free(cert); + return ret; + } + return -1; +} + X509* parse_cert(const char *buf, int bufLen) { X509 *cert = NULL; diff --git a/keystore/jni/cert.h b/keystore/jni/cert.h index a9807b1b20f0..aaa7602d8126 100644 --- a/keystore/jni/cert.h +++ b/keystore/jni/cert.h @@ -41,6 +41,13 @@ typedef struct { int key_len; } PKEY_STORE; +typedef struct { + PKCS12 *p12; + EVP_PKEY *pkey; + X509 *cert; + STACK_OF(X509) *certs; +} PKCS12_KEYSTORE; + #define PKEY_STORE_free(x) { \ if(x.pkey) EVP_PKEY_free(x.pkey); \ if(x.public_key) free(x.public_key); \ @@ -49,8 +56,14 @@ typedef struct { #define nelem(x) (sizeof (x) / sizeof *(x)) int gen_csr(int bits, const char *organizations, char reply[REPLY_MAX]); +PKCS12_KEYSTORE *get_pkcs12_keystore_handle(const char *buf, int bufLen, + const char *passwd); +int get_pkcs12_certificate(PKCS12_KEYSTORE *p12store, char *buf, int size); +int get_pkcs12_private_key(PKCS12_KEYSTORE *p12store, char *buf, int size); +int pop_pkcs12_certs_stack(PKCS12_KEYSTORE *p12store, char *buf, int size); +void free_pkcs12_keystore(PKCS12_KEYSTORE *p12store); int is_pkcs12(const char *buf, int bufLen); -X509* parse_cert(const char *buf, int bufLen); +X509 *parse_cert(const char *buf, int bufLen); int get_cert_name(X509 *cert, char *buf, int size); int get_issuer_name(X509 *cert, char *buf, int size); int is_ca_cert(X509 *cert); diff --git a/keystore/jni/certtool.c b/keystore/jni/certtool.c index fabf5cdf3fb7..1ae8dab4281f 100644 --- a/keystore/jni/certtool.c +++ b/keystore/jni/certtool.c @@ -19,10 +19,13 @@ #include <string.h> #include <jni.h> #include <cutils/log.h> +#include <openssl/pkcs12.h> #include <openssl/x509v3.h> #include "cert.h" +typedef int PKCS12_KEYSTORE_FUNC(PKCS12_KEYSTORE *store, char *buf, int size); + jstring android_security_CertTool_generateCertificateRequest(JNIEnv* env, jobject thiz, @@ -42,12 +45,88 @@ android_security_CertTool_isPkcs12Keystore(JNIEnv* env, jobject thiz, jbyteArray data) { - char buf[REPLY_MAX]; int len = (*env)->GetArrayLength(env, data); - if (len > REPLY_MAX) return 0; - (*env)->GetByteArrayRegion(env, data, 0, len, (jbyte*)buf); - return (jboolean) is_pkcs12(buf, len); + if (len > 0) { + PKCS12 *handle = NULL; + char buf[len]; + + (*env)->GetByteArrayRegion(env, data, 0, len, (jbyte*)buf); + return (jboolean)is_pkcs12(buf, len); + } else { + return 0; + } +} + +jint +android_security_CertTool_getPkcs12Handle(JNIEnv* env, + jobject thiz, + jbyteArray data, + jstring jPassword) +{ + jboolean bIsCopy; + int len = (*env)->GetArrayLength(env, data); + const char* passwd = (*env)->GetStringUTFChars(env, jPassword , &bIsCopy); + + if (len > 0) { + PKCS12_KEYSTORE *handle = NULL; + char buf[len]; + + (*env)->GetByteArrayRegion(env, data, 0, len, (jbyte*)buf); + handle = get_pkcs12_keystore_handle(buf, len, passwd); + (*env)->ReleaseStringUTFChars(env, jPassword, passwd); + return (jint)handle; + } else { + return 0; + } +} + +jstring call_pkcs12_ks_func(PKCS12_KEYSTORE_FUNC *func, + JNIEnv* env, + jobject thiz, + jint phandle) +{ + char buf[REPLY_MAX]; + + if (phandle == 0) return NULL; + if (func((PKCS12_KEYSTORE*)phandle, buf, sizeof(buf)) == 0) { + return (*env)->NewStringUTF(env, buf); + } + return NULL; +} + +jstring +android_security_CertTool_getPkcs12Certificate(JNIEnv* env, + jobject thiz, + jint phandle) +{ + return call_pkcs12_ks_func((PKCS12_KEYSTORE_FUNC *)get_pkcs12_certificate, + env, thiz, phandle); +} + +jstring +android_security_CertTool_getPkcs12PrivateKey(JNIEnv* env, + jobject thiz, + jint phandle) +{ + return call_pkcs12_ks_func((PKCS12_KEYSTORE_FUNC *)get_pkcs12_private_key, + env, thiz, phandle); +} + +jstring +android_security_CertTool_popPkcs12CertificateStack(JNIEnv* env, + jobject thiz, + jint phandle) +{ + return call_pkcs12_ks_func((PKCS12_KEYSTORE_FUNC *)pop_pkcs12_certs_stack, + env, thiz, phandle); +} + +void android_security_CertTool_freePkcs12Handle(JNIEnv* env, + jobject thiz, + jint handle) +{ + if (handle != 0) free_pkcs12_keystore((PKCS12_KEYSTORE*)handle); } jint @@ -117,6 +196,16 @@ static JNINativeMethod gCertToolMethods[] = { (void*)android_security_CertTool_generateCertificateRequest}, {"isPkcs12Keystore", "([B)Z", (void*)android_security_CertTool_isPkcs12Keystore}, + {"getPkcs12Handle", "([BLjava/lang/String;)I", + (void*)android_security_CertTool_getPkcs12Handle}, + {"getPkcs12Certificate", "(I)Ljava/lang/String;", + (void*)android_security_CertTool_getPkcs12Certificate}, + {"getPkcs12PrivateKey", "(I)Ljava/lang/String;", + (void*)android_security_CertTool_getPkcs12PrivateKey}, + {"popPkcs12CertificateStack", "(I)Ljava/lang/String;", + (void*)android_security_CertTool_popPkcs12CertificateStack}, + {"freePkcs12Handle", "(I)V", + (void*)android_security_CertTool_freePkcs12Handle}, {"generateX509Certificate", "([B)I", (void*)android_security_CertTool_generateX509Certificate}, {"isCaCertificate", "(I)Z", diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk index 5f4787198cf5..0091e322d08f 100644 --- a/libs/rs/Android.mk +++ b/libs/rs/Android.mk @@ -98,6 +98,7 @@ LOCAL_SRC_FILES:= \ rsSampler.cpp \ rsScript.cpp \ rsScriptC.cpp \ + rsScriptC_Lib.cpp \ rsThreadIO.cpp \ rsType.cpp \ rsTriangleMesh.cpp diff --git a/libs/rs/RenderScriptEnv.h b/libs/rs/RenderScriptEnv.h index 53de1f1f1ec4..07893014ac70 100644 --- a/libs/rs/RenderScriptEnv.h +++ b/libs/rs/RenderScriptEnv.h @@ -30,65 +30,7 @@ typedef struct { #define RS_PROGRAM_VERTEX_PROJECTION_OFFSET 16 #define RS_PROGRAM_VERTEX_TEXTURE_OFFSET 32 -typedef struct { - const void * (*loadEnvVp)(uint32_t bank, uint32_t offset); - - float (*loadEnvF)(uint32_t bank, uint32_t offset); - int32_t (*loadEnvI32)(uint32_t bank, uint32_t offset); - uint32_t (*loadEnvU32)(uint32_t bank, uint32_t offset); - void (*loadEnvVec4)(uint32_t bank, uint32_t offset, rsc_Vector4 *); - void (*loadEnvMatrix)(uint32_t bank, uint32_t offset, rsc_Matrix *); - - void (*storeEnvF)(uint32_t bank, uint32_t offset, float); - void (*storeEnvI32)(uint32_t bank, uint32_t offset, int32_t); - void (*storeEnvU32)(uint32_t bank, uint32_t offset, uint32_t); - void (*storeEnvVec4)(uint32_t bank, uint32_t offset, const rsc_Vector4 *); - void (*storeEnvMatrix)(uint32_t bank, uint32_t offset, const rsc_Matrix *); - - void (*matrixLoadIdentity)(rsc_Matrix *); - void (*matrixLoadFloat)(rsc_Matrix *, const float *); - void (*matrixLoadMat)(rsc_Matrix *, const rsc_Matrix *); - void (*matrixLoadRotate)(rsc_Matrix *, float rot, float x, float y, float z); - void (*matrixLoadScale)(rsc_Matrix *, float x, float y, float z); - void (*matrixLoadTranslate)(rsc_Matrix *, float x, float y, float z); - void (*matrixLoadMultiply)(rsc_Matrix *, const rsc_Matrix *lhs, const rsc_Matrix *rhs); - void (*matrixMultiply)(rsc_Matrix *, const rsc_Matrix *rhs); - void (*matrixRotate)(rsc_Matrix *, float rot, float x, float y, float z); - void (*matrixScale)(rsc_Matrix *, float x, float y, float z); - void (*matrixTranslate)(rsc_Matrix *, float x, float y, float z); - - void (*color)(float r, float g, float b, float a); - - void (*programFragmentBindTexture)(RsProgramFragment, uint32_t slot, RsAllocation); - void (*programFragmentBindSampler)(RsProgramFragment, uint32_t slot, RsAllocation); - - void (*materialDiffuse)(float r, float g, float b, float a); - void (*materialSpecular)(float r, float g, float b, float a); - void (*lightPosition)(float x, float y, float z, float w); - void (*materialShininess)(float s); - - void (*uploadToTexture)(RsAllocation va, uint32_t baseMipLevel); - - void (*enable)(uint32_t); - void (*disable)(uint32_t); - - uint32_t (*rand)(uint32_t max); - - void (*contextBindProgramFragment)(RsProgramFragment pf); - void (*contextBindProgramFragmentStore)(RsProgramFragmentStore pfs); - - - // Drawing funcs - void (*renderTriangleMesh)(RsTriangleMesh); - void (*renderTriangleMeshRange)(RsTriangleMesh, uint32_t start, uint32_t count); - - // Assumes (GL_FIXED) x,y,z (GL_UNSIGNED_BYTE)r,g,b,a - void (*drawTriangleArray)(RsAllocation alloc, uint32_t count); - - void (*drawRect)(int32_t x1, int32_t x2, int32_t y1, int32_t y2); -} rsc_FunctionTable; - -typedef int (*rsc_RunScript)(uint32_t launchIndex, const rsc_FunctionTable *); +//typedef int (*rsc_RunScript)(uint32_t launchIndex, const rsc_FunctionTable *); /* EnableCap */ diff --git a/libs/rs/java/Film/res/raw/filmstrip.c b/libs/rs/java/Film/res/raw/filmstrip.c index 1687a3121068..863b930e63fb 100644 --- a/libs/rs/java/Film/res/raw/filmstrip.c +++ b/libs/rs/java/Film/res/raw/filmstrip.c @@ -23,47 +23,36 @@ int main(int index) { int f1,f2,f3,f4, f5,f6,f7,f8, f9,f10,f11,f12, f13,f14,f15,f16; int g1,g2,g3,g4, g5,g6,g7,g8, g9,g10,g11,g12, g13,g14,g15,g16; - int float_1; - int float_0; - int float_2; - int float_90; - int float_0_5; - int trans; // float - int rot; // float + float trans; + float rot; int x; float focusPos; // float int focusID; int lastFocusID; int imgCount; - float_2 = intToFloat(2); - float_1 = intToFloat(1); - float_0 = intToFloat(0); - float_90= intToFloat(90); - float_0_5 = fixedtoFloat(0x8000); - trans = loadF(1, 0); rot = loadF(1, 1); - matrixLoadScale(&f16, float_2, float_2, float_2); - matrixTranslate(&f16, 0, 0, trans); - matrixRotate(&f16, float_90, 0, 0, float_1); - matrixRotate(&f16, rot, float_1, 0, 0); - storeEnvMatrix(3, 0, &f16); + matrixLoadScale(&f16, 2.f, 2.f, 2.f); + matrixTranslate(&f16, 0.f, 0.f, trans); + matrixRotate(&f16, 90.f, 0.f, 0.f, 1.f); + matrixRotate(&f16, rot, 1.f, 0.f, 0.f); + storeMatrix(3, 0, &f16); //materialDiffuse(con, 0.0f, 0.0f, 0.0f, 1.0f); //materialSpecular(con, 0.5f, 0.5f, 0.5f, 0.5f); //materialShininess(intToFloat(20)); //lightPosition(con, 0.2f, -0.2f, -2.0f, 0.0f); //enable(con, GL_LIGHTING); - renderTriangleMesh(NAMED_mesh); + drawTriangleMesh(NAMED_mesh); //int imgId = 0; - contextBindProgramFragmentStore(NAMED_PFImages); - contextBindProgramFragment(NAMED_PFSImages); + bindProgramFragmentStore(NAMED_PFImages); + bindProgramFragment(NAMED_PFSImages); //focusPos = loadF(1, 2); //focusID = 0; diff --git a/libs/rs/java/Fountain/AndroidManifest.xml b/libs/rs/java/Fountain/AndroidManifest.xml index a10938bae863..dd0e42876f53 100644 --- a/libs/rs/java/Fountain/AndroidManifest.xml +++ b/libs/rs/java/Fountain/AndroidManifest.xml @@ -3,7 +3,7 @@ package="com.android.fountain"> <application android:label="Fountain"> <activity android:name="Fountain" - android:theme="@android:style/Theme.Black.NoTitleBar"> + android:theme="@android:style/Theme.Translucent"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> diff --git a/libs/rs/java/Fountain/res/raw/fountain.c b/libs/rs/java/Fountain/res/raw/fountain.c index 69195654732a..99551fcdf915 100644 --- a/libs/rs/java/Fountain/res/raw/fountain.c +++ b/libs/rs/java/Fountain/res/raw/fountain.c @@ -2,8 +2,8 @@ #pragma version(1) #pragma stateVertex(default) -#pragma stateFragment(PgmFragBackground) -#pragma stateFragmentStore(PFSReplace) +#pragma stateFragment(PgmFragParts) +#pragma stateFragmentStore(PFSBlend) int main(int launchID) { @@ -34,15 +34,11 @@ int main(int launchID) { } } - drawRect(0, 256, 0, 512); - contextBindProgramFragment(NAMED_PgmFragParts); - contextBindProgramFragmentStore(NAMED_PFSBlend); - if (touch) { newPart = loadI32(2, 0); for (ct2=0; ct2<rate; ct2++) { - dx = scriptRand(0x10000) - 0x8000; - dy = scriptRand(0x10000) - 0x8000; + dx = (int)((randf(1.f) - 0.5f) * 0x10000); + dy = (int)((randf(1.f) - 0.5f) * 0x10000); idx = newPart * 5 + 1; storeI32(2, idx, dx); @@ -74,15 +70,15 @@ int main(int launchID) { dstIdx = drawCount * 9; c = 0xffafcf | ((life >> lifeShift) << 24); - storeU32(1, dstIdx, c); + storeI32(1, dstIdx, c); storeI32(1, dstIdx + 1, posx); storeI32(1, dstIdx + 2, posy); - storeU32(1, dstIdx + 3, c); + storeI32(1, dstIdx + 3, c); storeI32(1, dstIdx + 4, posx + 0x10000); storeI32(1, dstIdx + 5, posy + dy * 4); - storeU32(1, dstIdx + 6, c); + storeI32(1, dstIdx + 6, c); storeI32(1, dstIdx + 7, posx - 0x10000); storeI32(1, dstIdx + 8, posy + dy * 4); drawCount ++; diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java index 745635fb1269..c8e9a1ed9a7f 100644 --- a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java +++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java @@ -56,11 +56,7 @@ public class FountainRS { private RenderScript.Allocation mVertAlloc; private RenderScript.Script mScript; private RenderScript.ProgramFragmentStore mPFS; - private RenderScript.ProgramFragmentStore mPFS2; private RenderScript.ProgramFragment mPF; - private RenderScript.ProgramFragment mPF2; - private RenderScript.Allocation mTexture; - private RenderScript.Sampler mSampler; private Bitmap mBackground; @@ -74,16 +70,6 @@ public class FountainRS { mPartAlloc.setName("PartBuffer"); mVertAlloc = mRS.allocationCreatePredefSized(RenderScript.ElementPredefined.USER_I32, partCount * 5 + 1); - { - Drawable d = mRes.getDrawable(R.drawable.gadgets_clock_mp3); - BitmapDrawable bd = (BitmapDrawable)d; - Bitmap b = bd.getBitmap(); - mTexture = mRS.allocationCreateFromBitmap(b, - RenderScript.ElementPredefined.RGB_565, - false); - mTexture.uploadToTexture(0); - } - mRS.programFragmentStoreBegin(null, null); mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.SRC_ALPHA, RenderScript.BlendDstFunc.ONE); mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.ALWAYS); @@ -92,33 +78,10 @@ public class FountainRS { mPFS = mRS.programFragmentStoreCreate(); mPFS.setName("PFSBlend"); - mRS.programFragmentStoreBegin(null, null); - mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.ALWAYS); - mRS.programFragmentStoreDepthMask(false); - mRS.programFragmentStoreDitherEnable(false); - mPFS2 = mRS.programFragmentStoreCreate(); - mPFS2.setName("PFSReplace"); - mRS.contextBindProgramFragmentStore(mPFS2); - - mRS.samplerBegin(); - mRS.samplerSet(RenderScript.SamplerParam.FILTER_MAG, RenderScript.SamplerValue.NEAREST); - mRS.samplerSet(RenderScript.SamplerParam.FILTER_MIN, RenderScript.SamplerValue.NEAREST); - mSampler = mRS.samplerCreate(); - - mRS.programFragmentBegin(null, null); mPF = mRS.programFragmentCreate(); mPF.setName("PgmFragParts"); - mRS.programFragmentBegin(null, null); - mRS.programFragmentSetTexEnable(0, true); - mPF2 = mRS.programFragmentCreate(); - mRS.contextBindProgramFragment(mPF2); - mPF2.bindTexture(mTexture, 0); - mPF2.bindSampler(mSampler, 0); - mPF2.setName("PgmFragBackground"); - - mParams[0] = 0; mParams[1] = partCount; mParams[2] = 0; diff --git a/libs/rs/java/RenderScript/android/renderscript/RenderScript.java b/libs/rs/java/RenderScript/android/renderscript/RenderScript.java index 09d18369f39c..733009e9a555 100644 --- a/libs/rs/java/RenderScript/android/renderscript/RenderScript.java +++ b/libs/rs/java/RenderScript/android/renderscript/RenderScript.java @@ -846,6 +846,10 @@ public class RenderScript { nProgramFragmentSetTexEnable(slot, enable); } + public void programFragmentSetTexEnvMode(int slot, EnvMode env) { + nProgramFragmentSetEnvMode(slot, env.mID); + } + public ProgramFragment programFragmentCreate() { int id = nProgramFragmentCreate(); return new ProgramFragment(id); diff --git a/libs/rs/java/Rollo/res/raw/rollo.c b/libs/rs/java/Rollo/res/raw/rollo.c index 91413ca06e3c..d338d0df1b0c 100644 --- a/libs/rs/java/Rollo/res/raw/rollo.c +++ b/libs/rs/java/Rollo/res/raw/rollo.c @@ -4,33 +4,26 @@ #pragma stateFragmentStore(PFS) // Scratch buffer layout -// 0: fadeIn -// 1: zoomFade - #define SCRATCH_FADE 0 #define SCRATCH_ZOOM 1 #define SCRATCH_ROT 2 - -#define STATE_POS_X 0 -#define STATE_POS_Y 1 -#define STATE_PRESSURE 2 +//#define STATE_POS_X 0 +#define STATE_DONE 1 +//#define STATE_PRESSURE 2 #define STATE_ZOOM 3 -#define STATE_WARP 4 +//#define STATE_WARP 4 #define STATE_ORIENTATION 5 #define STATE_SELECTION 6 #define STATE_FIRST_VISIBLE 7 #define STATE_COUNT 8 +#define STATE_TOUCH 9 -void pfClearColor(float, float, float, float); -float loadF(int, int); -void storeF(int, int, float); -void drawQuad(float x1, float y1, float z1, - float x2, float y2, float z2, - float x3, float y3, float z3, - float x4, float y4, float z4); -float sinf(float); -float cosf(float); +float filter(float val, float target, float str) +{ + float delta = (target - val); + return val + delta * str; +} int main(void* con, int ft, int launchID) { @@ -38,84 +31,70 @@ int main(void* con, int ft, int launchID) int row; int col; int imageID; - int iconCount; - int pressure; + int done = loadI32(0, STATE_DONE); + int selectedID = loadI32(0, STATE_SELECTION); float f = loadF(2, 0); + pfClearColor(0.0f, 0.0f, 0.0f, f); - if (f < 0.8f) { - f = f + 0.02f; - storeF(2, 0, f); + if (done) { + if (f > 0.02f) { + //f = f - 0.02f; + //storeF(2, 0, f); + } + } else { + if (f < 0.8f) { + f = f + 0.02f; + storeF(2, 0, f); + } + } + + float touchCut = 1.f; + if (loadI32(0, STATE_TOUCH)) { + touchCut = 5.f; } - float zoom = loadF(2, SCRATCH_ZOOM); - float targetZoom = ((float)loadI32(0, STATE_ZOOM)) / 10.f; - zoom = zoom + (targetZoom - zoom) * 0.15f; + + float targetZoom = ((float)loadI32(0, STATE_ZOOM)) / 1000.f; + float zoom = filter(loadF(2, SCRATCH_ZOOM), targetZoom, 0.15 * touchCut); storeF(2, SCRATCH_ZOOM, zoom); - float rot = loadF(2, SCRATCH_ROT); float targetRot = loadI32(0, STATE_FIRST_VISIBLE) / 180.0f * 3.14f; - rot = rot + (targetRot - rot) * 0.15f; + float rot = filter(loadF(2, SCRATCH_ROT), targetRot, 0.1f * touchCut); storeF(2, SCRATCH_ROT, rot); float diam = 8.f;// + curve * 2.f; float scale = 1.0f / zoom; - pressure = loadI32(0, STATE_PRESSURE); - if (pressure) { - contextBindProgramFragmentStore(NAMED_PFSShadow); - - // compute the projected shadow - float x = loadI32(0, STATE_POS_X) / 1000.f; - float y = loadI32(0, STATE_POS_Y) / 1000.f; - float s = loadI32(0, STATE_PRESSURE) / 1000.f; - - s = s * 3.f; - - float dxdy1 = (x - 0.5f - s) / (1.001f - y); - float dxdy2 = (x - 0.5f + s) / (1.001f - y); - - float xlt = y * dxdy1 + x; - float xrt = y * dxdy2 + x; - - float yb = (0.5f - y) * 5.f + 0.2f; - - drawQuad(xlt, 5.f, 1, - xrt, 5.f, 1, - x + s, yb, 1, - x - s, yb, 1); - - contextBindProgramFragmentStore(NAMED_PFS); - } - - rot = rot * scale; float rotStep = 20.0f / 180.0f * 3.14f * scale; - //pressure = loadI32(0, 2); rowCount = 4; - iconCount = 32;//loadI32(0, 1); + int index = 0; + int iconCount = loadI32(0, STATE_COUNT); while (iconCount) { float tmpSin = sinf(rot); float tmpCos = cosf(rot); - //tmpCos = tmpCos * curve; - float tx1 = tmpSin * diam - (tmpCos * scale); float tx2 = tx1 + (tmpCos * scale * 2.f); - float tz1 = tmpCos * diam + (tmpSin * scale); float tz2 = tz1 - (tmpSin * scale * 2.f); int y; - for (y = 0; (y < rowCount) && iconCount; y++) { + for (y = rowCount -1; (y >= 0) && iconCount; y--) { float ty1 = ((y * 3.0f) - 4.5f) * scale; float ty2 = ty1 + scale * 2.f; - pfBindTexture(NAMED_PF, 0, loadI32(1, y)); + bindTexture(NAMED_PF, 0, loadI32(1, y)); + color(1.0f, 1.0f, 1.0f, 1.0f); + if (done && (index != selectedID)) { + color(0.4f, 0.4f, 0.4f, 1.0f); + } drawQuad(tx1, ty1, tz1, tx2, ty1, tz2, tx2, ty2, tz2, tx1, ty2, tz1); iconCount--; + index++; } rot = rot + rotStep; } diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java index daf3aa6fa9f6..8f4833519a62 100644 --- a/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java +++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java @@ -38,15 +38,16 @@ import android.view.KeyEvent; import android.view.MotionEvent; public class RolloRS { - public static final int STATE_POS_X = 0; - public static final int STATE_POS_Y = 1; - public static final int STATE_PRESSURE = 2; + //public static final int STATE_SELECTED_ID = 0; + public static final int STATE_DONE = 1; + //public static final int STATE_PRESSURE = 2; public static final int STATE_ZOOM = 3; - public static final int STATE_WARP = 4; + //public static final int STATE_WARP = 4; public static final int STATE_ORIENTATION = 5; public static final int STATE_SELECTION = 6; public static final int STATE_FIRST_VISIBLE = 7; public static final int STATE_COUNT = 8; + public static final int STATE_TOUCH = 9; public RolloRS() { @@ -60,45 +61,36 @@ public class RolloRS { } public void setPosition(float column) { - mAllocStateBuf[STATE_FIRST_VISIBLE] = (int)(column * 20); + mAllocStateBuf[STATE_FIRST_VISIBLE] = (int)(column * (-20)); mAllocState.data(mAllocStateBuf); } - public void setShadow(float x, float y, float size) { - // x and y are normalized at this point. - mAllocStateBuf[STATE_POS_X] = (int)(x * 1000); - mAllocStateBuf[STATE_POS_Y] = (int)(y * 1000); - mAllocStateBuf[STATE_PRESSURE] = (int)(size * 1000); - - Log.e("rs","shadow x=" + Integer.toString(mAllocStateBuf[STATE_POS_X]) + - " y=" + Integer.toString(mAllocStateBuf[STATE_POS_X]) + - " s=" + Integer.toString(mAllocStateBuf[STATE_PRESSURE])); + public void setTouch(boolean touch) { + mAllocStateBuf[STATE_TOUCH] = touch ? 1 : 0; mAllocState.data(mAllocStateBuf); } public void setZoom(float z) { - Log.e("rs", "zoom " + Float.toString(z)); + //Log.e("rs", "zoom " + Float.toString(z)); - mAllocStateBuf[STATE_ZOOM] = (int)(z * 10.f); + mAllocStateBuf[STATE_ZOOM] = (int)(z * 1000.f); mAllocState.data(mAllocStateBuf); } - public void setCurve(float c) { - mAllocStateBuf[STATE_WARP] = (int)(c * 100); - Log.e("rs", "curve " + Integer.toString(mAllocStateBuf[STATE_WARP])); + public void setSelected(int index) { + Log.e("rs", "setSelected " + Integer.toString(index)); + + mAllocStateBuf[STATE_SELECTION] = index; + mAllocStateBuf[STATE_DONE] = 1; mAllocState.data(mAllocStateBuf); } private Resources mRes; private RenderScript mRS; - - private RenderScript.Script mScript; - private RenderScript.Sampler mSampler; private RenderScript.ProgramFragmentStore mPFSBackground; - private RenderScript.ProgramFragmentStore mPFSShadow; private RenderScript.ProgramFragment mPFBackground; private RenderScript.ProgramFragment mPFImages; private RenderScript.ProgramVertex mPV; @@ -130,13 +122,10 @@ public class RolloRS { mRS.programFragmentBegin(null, null); mRS.programFragmentSetTexEnable(0, true); - //mRS.programFragmentSetTexEnable(1, true); - //mRS.programFragmentSetEnvMode(0, RS_TEX_ENV_MODE_REPLACE); - //mRS.programFragmentSetEnvMode(1, RS_TEX_ENV_MODE_MODULATE); + mRS.programFragmentSetTexEnvMode(0, RenderScript.EnvMode.MODULATE); mPFImages = mRS.programFragmentCreate(); mPFImages.setName("PF"); mPFImages.bindSampler(mSampler, 0); - mPFImages.bindSampler(mSampler, 1); mRS.programFragmentStoreBegin(null, null); mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.LESS); @@ -147,16 +136,6 @@ public class RolloRS { mPFSBackground = mRS.programFragmentStoreCreate(); mPFSBackground.setName("PFS"); - mRS.programFragmentStoreBegin(null, null); - mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.ALWAYS); - mRS.programFragmentStoreDitherEnable(false); - mRS.programFragmentStoreDepthMask(true); - mRS.programFragmentStoreColorMask(false, false, false, false); - mPFSShadow = mRS.programFragmentStoreCreate(); - mPFSShadow.setName("PFSShadow"); - - - mPVAlloc = new ProgramVertexAlloc(mRS); mRS.programVertexBegin(null, null); mRS.programVertexSetTextureMatrixEnable(true); @@ -253,7 +232,7 @@ public class RolloRS { //mRS.scriptCSetClearDepth(0); mScript = mRS.scriptCCreate(); - mAllocStateBuf = new int[] {0, 0, 0, 8, 0, 0, 0, 0, 38, 0}; + mAllocStateBuf = new int[] {0, 0, 0, 8, 0, 0, 0, 0, 38, 0, 0}; mAllocState = mRS.allocationCreatePredefSized( RenderScript.ElementPredefined.USER_I32, mAllocStateBuf.length); mScript.bindAllocation(mAllocState, 0); diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java index 6e2768ce8cc6..b5e02af9867a 100644 --- a/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java +++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java @@ -40,8 +40,8 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.graphics.PixelFormat; -public class RolloView extends RSSurfaceView { +public class RolloView extends RSSurfaceView { public RolloView(Context context) { super(context); setFocusable(true); @@ -68,13 +68,48 @@ public class RolloView extends RSSurfaceView { } boolean mControlMode = false; + boolean mZoomMode = false; boolean mFlingMode = false; float mFlingX = 0; float mFlingY = 0; float mColumn = -1; - float mCurve = 1; + float mOldColumn; float mZoom = 1; + int mIconCount = 38; + int mRows = 4; + int mColumns = (mIconCount + mRows - 1) / mRows; + + float mMaxZoom = ((float)mColumns) / 3.f; + + + void setColumn(boolean clamp) + { + //Log.e("rs", " col = " + Float.toString(mColumn)); + float c = mColumn; + if(c > (mColumns -2)) { + c = (mColumns -2); + } + if(c < 1) { + c = 1; + } + mRender.setPosition(c); + if(clamp) { + mColumn = c; + } + } + + void computeSelection(float x, float y) + { + float col = mColumn + (x - 0.5f) * 3; + int iCol = (int)(col + 0.25f); + + float row = (y / 0.8f) * mRows; + int iRow = (int)(row - 0.25f); + + mRender.setSelected(iCol * mRows + iRow); + } + @Override public boolean onTouchEvent(MotionEvent ev) { @@ -87,76 +122,67 @@ public class RolloView extends RSSurfaceView { float nx = ev.getX() / getWidth(); float ny = ev.getY() / getHeight(); + mRender.setTouch(ret); + if((ny > 0.85f) || mControlMode) { - mRender.setShadow(0, 0, 0); mFlingMode = false; // Projector control if((nx > 0.2f) && (nx < 0.8f) || mControlMode) { if(act != ev.ACTION_UP) { - float zoom = 5.f;//1.f + 10.f * ev.getSize(); + float zoom = mMaxZoom; if(mControlMode) { + if(!mZoomMode) { + zoom = 1.f; + } float dx = nx - mFlingX; - if(ny < 0.9) { - zoom = 5.f - ((0.9f - ny) * 10.f); + if((ny < 0.9) && mZoomMode) { + zoom = mMaxZoom - ((0.9f - ny) * 10.f); if(zoom < 1) { zoom = 1; - mControlMode = false; + mZoomMode = false; } + mOldColumn = mColumn; } - mColumn += dx * 3;// * zoom; - mColumn += -(mZoom - zoom) * (nx - 0.5f) * 2 * zoom; - mZoom = zoom; - - if(mColumn > 1) { - mColumn = 1; + mColumn += dx * 4;// * zoom; + if(zoom > 1.01f) { + mColumn += (mZoom - zoom) * (nx - 0.5f) * 4 * zoom; } - mRender.setPosition(mColumn); } else { + mOldColumn = mColumn; + mColumn = ((float)mColumns) / 2; mControlMode = true; - mZoom = 5; + mZoomMode = true; } + mZoom = zoom; mFlingX = nx; mRender.setZoom(zoom); } else { + if(mControlMode && (mZoom < 1.01f)) { + computeSelection(nx, ny); + } mControlMode = false; + mColumn = mOldColumn; mRender.setZoom(1.f); } } else { - if(nx > 0.2f) { - mCurve += 0.1f; - if(mCurve > 2) { - mCurve = 2; - } - } - if(nx < 0.8f) { - mCurve -= 0.1f; - if(mCurve < (-2)) { - mCurve = -2; - } - } - mRender.setCurve(mCurve); + // Do something with corners here.... } + setColumn(true); } else { // icon control if(act != ev.ACTION_UP) { if(mFlingMode) { - float dx = nx - mFlingX; - mColumn += dx * 5; - if(mColumn > 1) { - mColumn = 1; - } - mRender.setPosition(mColumn); + mColumn += (mFlingX - nx) * 4; + setColumn(true); } mFlingMode = true; mFlingX = nx; mFlingY = ny; - //mRender.setShadow(nx, ny, ev.getSize()); } else { mFlingMode = false; - mRender.setShadow(nx, ny, 0); } } @@ -176,7 +202,7 @@ public class RolloView extends RSSurfaceView { return true; } - + } diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp index adbe3e9d07e6..e0928c3c53bc 100644 --- a/libs/rs/rsScriptC.cpp +++ b/libs/rs/rsScriptC.cpp @@ -46,422 +46,6 @@ ScriptC::~ScriptC() } } -extern "C" float fixedToFloat(int32_t f) -{ - return ((float)f) / 0x10000; -} - -extern "C" float intToFloat(int32_t f) -{ - return (float)f; -} - -extern "C" void matrixLoadIdentity(rsc_Matrix *mat) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->loadIdentity(); -} - -extern "C" void matrixLoadFloat(rsc_Matrix *mat, const float *f) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->load(f); -} - -extern "C" void matrixLoadMat(rsc_Matrix *mat, const rsc_Matrix *newmat) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->load(reinterpret_cast<const Matrix *>(newmat)); -} - -extern "C" void matrixLoadRotate(rsc_Matrix *mat, float rot, float x, float y, float z) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->loadRotate(rot, x, y, z); -} - -extern "C" void matrixLoadScale(rsc_Matrix *mat, float x, float y, float z) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->loadScale(x, y, z); -} - -extern "C" void matrixLoadTranslate(rsc_Matrix *mat, float x, float y, float z) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->loadTranslate(x, y, z); -} - -extern "C" void matrixLoadMultiply(rsc_Matrix *mat, const rsc_Matrix *lhs, const rsc_Matrix *rhs) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->loadMultiply(reinterpret_cast<const Matrix *>(lhs), - reinterpret_cast<const Matrix *>(rhs)); -} - -extern "C" void matrixMultiply(rsc_Matrix *mat, const rsc_Matrix *rhs) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->multiply(reinterpret_cast<const Matrix *>(rhs)); -} - -extern "C" void matrixRotate(rsc_Matrix *mat, float rot, float x, float y, float z) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->rotate(rot, x, y, z); -} - -extern "C" void matrixScale(rsc_Matrix *mat, float x, float y, float z) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->scale(x, y, z); -} - -extern "C" void matrixTranslate(rsc_Matrix *mat, float x, float y, float z) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->translate(x, y, z); -} - - -extern "C" const void * loadVp(uint32_t bank, uint32_t offset) -{ - GET_TLS(); - return &static_cast<const uint8_t *>(sc->mSlots[bank]->getPtr())[offset]; -} - -static float SC_loadF(uint32_t bank, uint32_t offset) -{ - GET_TLS(); - float f = static_cast<const float *>(sc->mSlots[bank]->getPtr())[offset]; - //LOGE("loadF %i %i = %f %x", bank, offset, f, ((int *)&f)[0]); - return f; -} - -static int32_t SC_loadI32(uint32_t bank, uint32_t offset) -{ - GET_TLS(); - int32_t t = static_cast<const int32_t *>(sc->mSlots[bank]->getPtr())[offset]; - //LOGE("loadI32 %i %i = %i", bank, offset, t); - return t; -} - -static uint32_t SC_loadU32(uint32_t bank, uint32_t offset) -{ - GET_TLS(); - return static_cast<const uint32_t *>(sc->mSlots[bank]->getPtr())[offset]; -} - -extern "C" void loadEnvVec4(uint32_t bank, uint32_t offset, rsc_Vector4 *v) -{ - GET_TLS(); - memcpy(v, &static_cast<const float *>(sc->mSlots[bank]->getPtr())[offset], sizeof(rsc_Vector4)); -} - -extern "C" void loadEnvMatrix(uint32_t bank, uint32_t offset, rsc_Matrix *m) -{ - GET_TLS(); - memcpy(m, &static_cast<const float *>(sc->mSlots[bank]->getPtr())[offset], sizeof(rsc_Matrix)); -} - - -static void SC_storeF(uint32_t bank, uint32_t offset, float v) -{ - //LOGE("storeF %i %i %f", bank, offset, v); - GET_TLS(); - static_cast<float *>(sc->mSlots[bank]->getPtr())[offset] = v; -} - -static void SC_storeI32(uint32_t bank, uint32_t offset, int32_t v) -{ - GET_TLS(); - static_cast<int32_t *>(sc->mSlots[bank]->getPtr())[offset] = v; -} - -static void SC_storeU32(uint32_t bank, uint32_t offset, uint32_t v) -{ - GET_TLS(); - static_cast<uint32_t *>(sc->mSlots[bank]->getPtr())[offset] = v; -} - -extern "C" void storeEnvVec4(uint32_t bank, uint32_t offset, const rsc_Vector4 *v) -{ - GET_TLS(); - memcpy(&static_cast<float *>(sc->mSlots[bank]->getPtr())[offset], v, sizeof(rsc_Vector4)); -} - -extern "C" void storeEnvMatrix(uint32_t bank, uint32_t offset, const rsc_Matrix *m) -{ - GET_TLS(); - memcpy(&static_cast<float *>(sc->mSlots[bank]->getPtr())[offset], m, sizeof(rsc_Matrix)); -} - - -extern "C" void color(float r, float g, float b, float a) -{ - glColor4f(r, g, b, a); -} - -extern "C" void renderTriangleMesh(RsTriangleMesh mesh) -{ - GET_TLS(); - rsi_TriangleMeshRender(rsc, mesh); -} - -extern "C" void renderTriangleMeshRange(RsTriangleMesh mesh, uint32_t start, uint32_t count) -{ - GET_TLS(); - rsi_TriangleMeshRenderRange(rsc, mesh, start, count); -} - -extern "C" void materialDiffuse(float r, float g, float b, float a) -{ - float v[] = {r, g, b, a}; - glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, v); -} - -extern "C" void materialSpecular(float r, float g, float b, float a) -{ - float v[] = {r, g, b, a}; - glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, v); -} - -extern "C" void lightPosition(float x, float y, float z, float w) -{ - float v[] = {x, y, z, w}; - glLightfv(GL_LIGHT0, GL_POSITION, v); -} - -extern "C" void materialShininess(float s) -{ - glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &s); -} - -extern "C" void uploadToTexture(RsAllocation va, uint32_t baseMipLevel) -{ - GET_TLS(); - rsi_AllocationUploadToTexture(rsc, va, baseMipLevel); -} - -extern "C" void enable(uint32_t p) -{ - glEnable(p); -} - -extern "C" void disable(uint32_t p) -{ - glDisable(p); -} - -extern "C" uint32_t scriptRand(uint32_t max) -{ - return (uint32_t)(((float)rand()) * max / RAND_MAX); -} - -// Assumes (GL_FIXED) x,y,z (GL_UNSIGNED_BYTE)r,g,b,a -extern "C" void drawTriangleArray(RsAllocation alloc, uint32_t count) -{ - GET_TLS(); - - const Allocation *a = (const Allocation *)alloc; - const uint32_t *ptr = (const uint32_t *)a->getPtr(); - - rsc->setupCheck(); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]); - - glEnableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - - glVertexPointer(2, GL_FIXED, 12, ptr + 1); - //glTexCoordPointer(2, GL_FIXED, 24, ptr + 1); - glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr); - - glDrawArrays(GL_TRIANGLES, 0, count * 3); -} - -extern "C" void drawRect(int32_t x1, int32_t x2, int32_t y1, int32_t y2) -{ - GET_TLS(); - x1 = (x1 << 16); - x2 = (x2 << 16); - y1 = (y1 << 16); - y2 = (y2 << 16); - - int32_t vtx[] = {x1,y1, x1,y2, x2,y1, x2,y2}; - static const int32_t tex[] = {0,0, 0,0x10000, 0x10000,0, 0x10000,0x10000}; - - - rsc->setupCheck(); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - - glVertexPointer(2, GL_FIXED, 8, vtx); - glTexCoordPointer(2, GL_FIXED, 8, tex); - //glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); -} - -static void SC_drawQuad(float x1, float y1, float z1, - float x2, float y2, float z2, - float x3, float y3, float z3, - float x4, float y4, float z4) -{ - GET_TLS(); - - //LOGE("Quad"); - //LOGE("%4.2f, %4.2f, %4.2f", x1, y1, z1); - //LOGE("%4.2f, %4.2f, %4.2f", x2, y2, z2); - //LOGE("%4.2f, %4.2f, %4.2f", x3, y3, z3); - //LOGE("%4.2f, %4.2f, %4.2f", x4, y4, z4); - - float vtx[] = {x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4}; - static const float tex[] = {0,0, 0,1, 1,1, 1,0}; - - - rsc->setupCheck(); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]); - - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(3, GL_FLOAT, 0, vtx); - - glClientActiveTexture(GL_TEXTURE0); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glTexCoordPointer(2, GL_FLOAT, 0, tex); - glClientActiveTexture(GL_TEXTURE1); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glTexCoordPointer(2, GL_FLOAT, 0, tex); - glClientActiveTexture(GL_TEXTURE0); - - glDisableClientState(GL_NORMAL_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - - //glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr); - - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); -} - -extern "C" float sinf(float angle) -{ - float s = (float)sin(angle); - return s; -} - -extern "C" float cosf(float angle) -{ - float s = (float)cos(angle); - return s; -} - -extern "C" void pfClearColor(float r, float g, float b, float a) -{ - //LOGE("c %f %f %f %f", r, g, b, a); - GET_TLS(); - sc->mEnviroment.mClearColor[0] = r; - sc->mEnviroment.mClearColor[1] = g; - sc->mEnviroment.mClearColor[2] = b; - sc->mEnviroment.mClearColor[3] = a; -} - -extern "C" void pfBindTexture(RsProgramFragment vpf, uint32_t slot, RsAllocation va) -{ - GET_TLS(); - rsi_ProgramFragmentBindTexture(rsc, - static_cast<ProgramFragment *>(vpf), - slot, - static_cast<Allocation *>(va)); - -} - -extern "C" void pfBindSampler(RsProgramFragment vpf, uint32_t slot, RsSampler vs) -{ - GET_TLS(); - rsi_ProgramFragmentBindSampler(rsc, - static_cast<ProgramFragment *>(vpf), - slot, - static_cast<Sampler *>(vs)); - -} - -extern "C" void contextBindProgramFragmentStore(RsProgramFragmentStore pfs) -{ - GET_TLS(); - rsi_ContextBindProgramFragmentStore(rsc, pfs); - -} - -extern "C" void contextBindProgramFragment(RsProgramFragment pf) -{ - GET_TLS(); - rsi_ContextBindProgramFragment(rsc, pf); - -} - - -static rsc_FunctionTable scriptCPtrTable = { - loadVp, - SC_loadF, - SC_loadI32, - SC_loadU32, - loadEnvVec4, - loadEnvMatrix, - - SC_storeF, - SC_storeI32, - SC_storeU32, - storeEnvVec4, - storeEnvMatrix, - - matrixLoadIdentity, - matrixLoadFloat, - matrixLoadMat, - matrixLoadRotate, - matrixLoadScale, - matrixLoadTranslate, - matrixLoadMultiply, - matrixMultiply, - matrixRotate, - matrixScale, - matrixTranslate, - - color, - - pfBindTexture, - pfBindSampler, - - materialDiffuse, - materialSpecular, - lightPosition, - materialShininess, - uploadToTexture, - enable, - disable, - - scriptRand, - contextBindProgramFragment, - contextBindProgramFragmentStore, - - - renderTriangleMesh, - renderTriangleMeshRange, - - drawTriangleArray, - drawRect - -}; - bool ScriptC::run(Context *rsc, uint32_t launchIndex) { @@ -478,9 +62,11 @@ bool ScriptC::run(Context *rsc, uint32_t launchIndex) rsc->setVertex(mEnviroment.mVertex.get()); } + bool ret = false; tls->mScript = this; - return mProgram.mScript(launchIndex, &scriptCPtrTable) != 0; + ret = mProgram.mScript(launchIndex) != 0; tls->mScript = NULL; + return ret; } ScriptCState::ScriptCState() @@ -514,35 +100,6 @@ void ScriptCState::clear() } -ScriptCState::SymbolTable_t ScriptCState::gSyms[] = { - { "loadI32", (void *)&SC_loadI32, "int loadI32(int, int)" }, - { "loadF", (void *)&SC_loadF, "float loadF(int, int)" }, - { "storeI32", (void *)&SC_storeI32, "void storeI32(int, int, int)" }, - { "storeF", (void *)&SC_storeF, "void storeF(int, int, float)" }, - { "drawQuad", (void *)&SC_drawQuad, "void drawQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4)" }, - { "sinf", (void *)&sinf, "float sinf(float)" }, - { "cosf", (void *)&cosf, "float cosf(float)" }, - { "contextBindProgramFragmentStore", (void *)&contextBindProgramFragmentStore, "void contextBindProgramFragmentStore(int)" }, - { "pfClearColor", (void *)&pfClearColor, "void pfClearColor(float, float, float, float)" }, - { "pfBindTexture", (void *)&pfBindTexture, "void pfBindTexture(int, int, int)" }, - - - { NULL, NULL, NULL } -}; - -const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbol(const char *sym) -{ - ScriptCState::SymbolTable_t *syms = gSyms; - - while (syms->mPtr) { - if (!strcmp(syms->mName, sym)) { - return syms; - } - syms++; - } - return NULL; -} - static ACCvoid* symbolLookup(ACCvoid* pContext, const ACCchar* name) { const ScriptCState::SymbolTable_t *sym = ScriptCState::lookupSymbol(name); @@ -557,16 +114,6 @@ static ACCvoid* symbolLookup(ACCvoid* pContext, const ACCchar* name) return NULL; } -void ScriptCState::appendDecls(String8 *str) -{ - ScriptCState::SymbolTable_t *syms = gSyms; - while (syms->mPtr) { - str->append(syms->mDecl); - str->append(";\n"); - syms++; - } -} - void ScriptCState::runCompiler(Context *rsc) { mAccScript = accCreateScript(); @@ -708,7 +255,7 @@ void rsi_ScriptCAddType(Context * rsc, RsType vt) void rsi_ScriptCSetScript(Context * rsc, void *vp) { ScriptCState *ss = &rsc->mScriptC; - ss->mProgram.mScript = reinterpret_cast<rsc_RunScript>(vp); + ss->mProgram.mScript = reinterpret_cast<ScriptC::RunScript_t>(vp); } void rsi_ScriptCSetRoot(Context * rsc, bool isRoot) diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h index 3a3f2f7ed43f..860b4d19e1ab 100644 --- a/libs/rs/rsScriptC.h +++ b/libs/rs/rsScriptC.h @@ -32,6 +32,7 @@ namespace renderscript { class ScriptC : public Script { public: + typedef int (*RunScript_t)(uint32_t launchIndex); ScriptC(); virtual ~ScriptC(); @@ -44,7 +45,7 @@ public: int mVersionMajor; int mVersionMinor; - rsc_RunScript mScript; + RunScript_t mScript; }; Program_t mProgram; @@ -73,7 +74,8 @@ public: struct SymbolTable_t { const char * mName; void * mPtr; - const char * mDecl; + const char * mRet; + const char * mParam; }; static SymbolTable_t gSyms[]; static const SymbolTable_t * lookupSymbol(const char *); diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp new file mode 100644 index 000000000000..59d8e5f0c51b --- /dev/null +++ b/libs/rs/rsScriptC_Lib.cpp @@ -0,0 +1,523 @@ +/* + * 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. + */ + +#include "rsContext.h" +#include "rsScriptC.h" +#include "rsMatrix.h" + +#include "acc/acc.h" +#include "utils/String8.h" + +#include <GLES/gl.h> +#include <GLES/glext.h> + +using namespace android; +using namespace android::renderscript; + +#define GET_TLS() Context::ScriptTLSStruct * tls = \ + (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey); \ + Context * rsc = tls->mContext; \ + ScriptC * sc = (ScriptC *) tls->mScript + + +////////////////////////////////////////////////////////////////////////////// +// IO routines +////////////////////////////////////////////////////////////////////////////// + +static float SC_loadF(uint32_t bank, uint32_t offset) +{ + GET_TLS(); + const void *vp = sc->mSlots[bank]->getPtr(); + const float *f = static_cast<const float *>(vp); + //LOGE("loadF %i %i = %f %x", bank, offset, f, ((int *)&f)[0]); + return f[offset]; +} + +static int32_t SC_loadI32(uint32_t bank, uint32_t offset) +{ + GET_TLS(); + const void *vp = sc->mSlots[bank]->getPtr(); + const int32_t *i = static_cast<const int32_t *>(vp); + //LOGE("loadI32 %i %i = %i", bank, offset, t); + return i[offset]; +} + +static uint32_t SC_loadU32(uint32_t bank, uint32_t offset) +{ + GET_TLS(); + const void *vp = sc->mSlots[bank]->getPtr(); + const uint32_t *i = static_cast<const uint32_t *>(vp); + return i[offset]; +} + +static void SC_loadVec4(uint32_t bank, uint32_t offset, rsc_Vector4 *v) +{ + GET_TLS(); + const void *vp = sc->mSlots[bank]->getPtr(); + const float *f = static_cast<const float *>(vp); + memcpy(v, &f[offset], sizeof(rsc_Vector4)); +} + +static void SC_loadMatrix(uint32_t bank, uint32_t offset, rsc_Matrix *m) +{ + GET_TLS(); + const void *vp = sc->mSlots[bank]->getPtr(); + const float *f = static_cast<const float *>(vp); + memcpy(m, &f[offset], sizeof(rsc_Matrix)); +} + + +static void SC_storeF(uint32_t bank, uint32_t offset, float v) +{ + //LOGE("storeF %i %i %f", bank, offset, v); + GET_TLS(); + void *vp = sc->mSlots[bank]->getPtr(); + float *f = static_cast<float *>(vp); + f[offset] = v; +} + +static void SC_storeI32(uint32_t bank, uint32_t offset, int32_t v) +{ + GET_TLS(); + void *vp = sc->mSlots[bank]->getPtr(); + int32_t *f = static_cast<int32_t *>(vp); + static_cast<int32_t *>(sc->mSlots[bank]->getPtr())[offset] = v; +} + +static void SC_storeU32(uint32_t bank, uint32_t offset, uint32_t v) +{ + GET_TLS(); + void *vp = sc->mSlots[bank]->getPtr(); + uint32_t *f = static_cast<uint32_t *>(vp); + static_cast<uint32_t *>(sc->mSlots[bank]->getPtr())[offset] = v; +} + +static void SC_storeVec4(uint32_t bank, uint32_t offset, const rsc_Vector4 *v) +{ + GET_TLS(); + void *vp = sc->mSlots[bank]->getPtr(); + float *f = static_cast<float *>(vp); + memcpy(&f[offset], v, sizeof(rsc_Vector4)); +} + +static void SC_storeMatrix(uint32_t bank, uint32_t offset, const rsc_Matrix *m) +{ + GET_TLS(); + void *vp = sc->mSlots[bank]->getPtr(); + float *f = static_cast<float *>(vp); + memcpy(&f[offset], m, sizeof(rsc_Matrix)); +} + + +////////////////////////////////////////////////////////////////////////////// +// Math routines +////////////////////////////////////////////////////////////////////////////// + +static float SC_randf(float max) +{ + float r = (float)rand(); + return r / RAND_MAX * max; +} + + + + + +////////////////////////////////////////////////////////////////////////////// +// Matrix routines +////////////////////////////////////////////////////////////////////////////// + + +static void SC_matrixLoadIdentity(rsc_Matrix *mat) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->loadIdentity(); +} + +static void SC_matrixLoadFloat(rsc_Matrix *mat, const float *f) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->load(f); +} + +static void SC_matrixLoadMat(rsc_Matrix *mat, const rsc_Matrix *newmat) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->load(reinterpret_cast<const Matrix *>(newmat)); +} + +static void SC_matrixLoadRotate(rsc_Matrix *mat, float rot, float x, float y, float z) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->loadRotate(rot, x, y, z); +} + +static void SC_matrixLoadScale(rsc_Matrix *mat, float x, float y, float z) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->loadScale(x, y, z); +} + +static void SC_matrixLoadTranslate(rsc_Matrix *mat, float x, float y, float z) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->loadTranslate(x, y, z); +} + +static void SC_matrixLoadMultiply(rsc_Matrix *mat, const rsc_Matrix *lhs, const rsc_Matrix *rhs) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->loadMultiply(reinterpret_cast<const Matrix *>(lhs), + reinterpret_cast<const Matrix *>(rhs)); +} + +static void SC_matrixMultiply(rsc_Matrix *mat, const rsc_Matrix *rhs) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->multiply(reinterpret_cast<const Matrix *>(rhs)); +} + +static void SC_matrixRotate(rsc_Matrix *mat, float rot, float x, float y, float z) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->rotate(rot, x, y, z); +} + +static void SC_matrixScale(rsc_Matrix *mat, float x, float y, float z) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->scale(x, y, z); +} + +static void SC_matrixTranslate(rsc_Matrix *mat, float x, float y, float z) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->translate(x, y, z); +} + + + + +////////////////////////////////////////////////////////////////////////////// +// Context +////////////////////////////////////////////////////////////////////////////// + +static void SC_bindTexture(RsProgramFragment vpf, uint32_t slot, RsAllocation va) +{ + GET_TLS(); + rsi_ProgramFragmentBindTexture(rsc, + static_cast<ProgramFragment *>(vpf), + slot, + static_cast<Allocation *>(va)); + +} + +static void SC_bindSampler(RsProgramFragment vpf, uint32_t slot, RsSampler vs) +{ + GET_TLS(); + rsi_ProgramFragmentBindSampler(rsc, + static_cast<ProgramFragment *>(vpf), + slot, + static_cast<Sampler *>(vs)); + +} + +static void SC_bindProgramFragmentStore(RsProgramFragmentStore pfs) +{ + GET_TLS(); + rsi_ContextBindProgramFragmentStore(rsc, pfs); + +} + +static void SC_bindProgramFragment(RsProgramFragment pf) +{ + GET_TLS(); + rsi_ContextBindProgramFragment(rsc, pf); + +} + + +////////////////////////////////////////////////////////////////////////////// +// Drawing +////////////////////////////////////////////////////////////////////////////// + +static void SC_drawTriangleMesh(RsTriangleMesh mesh) +{ + GET_TLS(); + rsi_TriangleMeshRender(rsc, mesh); +} + +static void SC_drawTriangleMeshRange(RsTriangleMesh mesh, uint32_t start, uint32_t count) +{ + GET_TLS(); + rsi_TriangleMeshRenderRange(rsc, mesh, start, count); +} + +// Assumes (GL_FIXED) x,y,z (GL_UNSIGNED_BYTE)r,g,b,a +static void SC_drawTriangleArray(int ialloc, uint32_t count) +{ + GET_TLS(); + RsAllocation alloc = (RsAllocation)ialloc; + + const Allocation *a = (const Allocation *)alloc; + const uint32_t *ptr = (const uint32_t *)a->getPtr(); + + rsc->setupCheck(); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]); + + glEnableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + glVertexPointer(2, GL_FIXED, 12, ptr + 1); + //glTexCoordPointer(2, GL_FIXED, 24, ptr + 1); + glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr); + + glDrawArrays(GL_TRIANGLES, 0, count * 3); +} + +static void SC_drawQuad(float x1, float y1, float z1, + float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4) +{ + GET_TLS(); + + //LOGE("Quad"); + //LOGE("%4.2f, %4.2f, %4.2f", x1, y1, z1); + //LOGE("%4.2f, %4.2f, %4.2f", x2, y2, z2); + //LOGE("%4.2f, %4.2f, %4.2f", x3, y3, z3); + //LOGE("%4.2f, %4.2f, %4.2f", x4, y4, z4); + + float vtx[] = {x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4}; + static const float tex[] = {0,1, 1,1, 1,0, 0,0}; + + + rsc->setupCheck(); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]); + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, vtx); + + glClientActiveTexture(GL_TEXTURE0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 0, tex); + glClientActiveTexture(GL_TEXTURE1); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 0, tex); + glClientActiveTexture(GL_TEXTURE0); + + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + //glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +extern "C" const void * loadVp(uint32_t bank, uint32_t offset) +{ + GET_TLS(); + return &static_cast<const uint8_t *>(sc->mSlots[bank]->getPtr())[offset]; +} + + + +static void SC_color(float r, float g, float b, float a) +{ + glColor4f(r, g, b, a); +} + + +extern "C" void materialDiffuse(float r, float g, float b, float a) +{ + float v[] = {r, g, b, a}; + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, v); +} + +extern "C" void materialSpecular(float r, float g, float b, float a) +{ + float v[] = {r, g, b, a}; + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, v); +} + +extern "C" void lightPosition(float x, float y, float z, float w) +{ + float v[] = {x, y, z, w}; + glLightfv(GL_LIGHT0, GL_POSITION, v); +} + +extern "C" void materialShininess(float s) +{ + glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &s); +} + +extern "C" void uploadToTexture(RsAllocation va, uint32_t baseMipLevel) +{ + GET_TLS(); + rsi_AllocationUploadToTexture(rsc, va, baseMipLevel); +} + +extern "C" void enable(uint32_t p) +{ + glEnable(p); +} + +extern "C" void disable(uint32_t p) +{ + glDisable(p); +} + + + +static void SC_ClearColor(float r, float g, float b, float a) +{ + //LOGE("c %f %f %f %f", r, g, b, a); + GET_TLS(); + sc->mEnviroment.mClearColor[0] = r; + sc->mEnviroment.mClearColor[1] = g; + sc->mEnviroment.mClearColor[2] = b; + sc->mEnviroment.mClearColor[3] = a; +} + + + +////////////////////////////////////////////////////////////////////////////// +// Class implementation +////////////////////////////////////////////////////////////////////////////// + +ScriptCState::SymbolTable_t ScriptCState::gSyms[] = { + // IO + { "loadI32", (void *)&SC_loadI32, + "int", "(int, int)" }, + //{ "loadU32", (void *)&SC_loadU32, "unsigned int", "(int, int)" }, + { "loadF", (void *)&SC_loadF, + "float", "(int, int)" }, + { "loadVec4", (void *)&SC_loadVec4, + "void", "(int, int, float *)" }, + { "loadMatrix", (void *)&SC_loadMatrix, + "void", "(int, int, float *)" }, + { "storeI32", (void *)&SC_storeI32, + "void", "(int, int, int)" }, + //{ "storeU32", (void *)&SC_storeU32, "void", "(int, int, unsigned int)" }, + { "storeF", (void *)&SC_storeF, + "void", "(int, int, float)" }, + { "storeVec4", (void *)&SC_storeVec4, + "void", "(int, int, float *)" }, + { "storeMatrix", (void *)&SC_storeMatrix, + "void", "(int, int, float *)" }, + + // math + { "sinf", (void *)&sinf, + "float", "(float)" }, + { "cosf", (void *)&cosf, + "float", "(float)" }, + { "fabs", (void *)&fabs, + "float", "(float)" }, + { "randf", (void *)&SC_randf, + "float", "(float)" }, + + // matrix + { "matrixLoadIdentity", (void *)&SC_matrixLoadIdentity, + "void", "(float *mat)" }, + { "matrixLoadFloat", (void *)&SC_matrixLoadFloat, + "void", "(float *mat, float *f)" }, + { "matrixLoadMat", (void *)&SC_matrixLoadMat, + "void", "(float *mat, float *newmat)" }, + { "matrixLoadRotate", (void *)&SC_matrixLoadRotate, + "void", "(float *mat, float rot, float x, float y, float z)" }, + { "matrixLoadScale", (void *)&SC_matrixLoadScale, + "void", "(float *mat, float x, float y, float z)" }, + { "matrixLoadTranslate", (void *)&SC_matrixLoadTranslate, + "void", "(float *mat, float x, float y, float z)" }, + { "matrixLoadMultiply", (void *)&SC_matrixLoadMultiply, + "void", "(float *mat, float *lhs, float *rhs)" }, + { "matrixMultiply", (void *)&SC_matrixMultiply, + "void", "(float *mat, float *rhs)" }, + { "matrixRotate", (void *)&SC_matrixRotate, + "void", "(float *mat, float rot, float x, float y, float z)" }, + { "matrixScale", (void *)&SC_matrixScale, + "void", "(float *mat, float x, float y, float z)" }, + { "matrixTranslate", (void *)&SC_matrixTranslate, + "void", "(float *mat, float x, float y, float z)" }, + + // context + { "bindProgramFragment", (void *)&SC_bindProgramFragment, + "void", "(int)" }, + { "bindProgramFragmentStore", (void *)&SC_bindProgramFragmentStore, + "void", "(int)" }, + { "bindSampler", (void *)&SC_bindSampler, + "void", "(int, int, int)" }, + { "bindTexture", (void *)&SC_bindTexture, + "void", "(int, int, int)" }, + + // drawing + { "drawQuad", (void *)&SC_drawQuad, + "void", "(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4)" }, + { "drawTriangleArray", (void *)&SC_drawTriangleArray, + "void", "(int ialloc, int count)" }, + { "drawTriangleMesh", (void *)&SC_drawTriangleMesh, + "void", "(int mesh)" }, + { "drawTriangleMeshRange", (void *)&SC_drawTriangleMeshRange, + "void", "(int mesh, int start, int count)" }, + + + // misc + { "pfClearColor", (void *)&SC_ClearColor, + "void", "(float, float, float, float)" }, + + { "color", (void *)&SC_color, + "void", "(float, float, float, float)" }, + + { NULL, NULL, NULL, NULL } +}; + +const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbol(const char *sym) +{ + ScriptCState::SymbolTable_t *syms = gSyms; + + while (syms->mPtr) { + if (!strcmp(syms->mName, sym)) { + return syms; + } + syms++; + } + return NULL; +} + +void ScriptCState::appendDecls(String8 *str) +{ + ScriptCState::SymbolTable_t *syms = gSyms; + while (syms->mPtr) { + str->append(syms->mRet); + str->append(" "); + str->append(syms->mName); + str->append(syms->mParam); + str->append(";\n"); + syms++; + } +} + + diff --git a/libs/ui/Overlay.cpp b/libs/ui/Overlay.cpp index a092f8dd54e0..4854d6a2a26f 100644 --- a/libs/ui/Overlay.cpp +++ b/libs/ui/Overlay.cpp @@ -59,6 +59,18 @@ status_t Overlay::queueBuffer(overlay_buffer_t buffer) return mOverlayData->queueBuffer(mOverlayData, buffer); } +status_t Overlay::setCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h) +{ + if (mStatus != NO_ERROR) return mStatus; + return mOverlayData->setCrop(mOverlayData, x, y, w, h); +} + +status_t Overlay::getCrop(uint32_t* x, uint32_t* y, uint32_t* w, uint32_t* h) +{ + if (mStatus != NO_ERROR) return mStatus; + return mOverlayData->getCrop(mOverlayData, x, y, w, h); +} + int32_t Overlay::getBufferCount() const { if (mStatus != NO_ERROR) return mStatus; @@ -73,6 +85,15 @@ void* Overlay::getBufferAddress(overlay_buffer_t buffer) void Overlay::destroy() { if (mStatus != NO_ERROR) return; + + // Must delete the objects in reverse creation order, thus the + // data side must be closed first and then the destroy send to + // the control side. + if (mOverlayData) { + overlay_data_close(mOverlayData); + mOverlayData = NULL; + } + mOverlayRef->mOverlayChannel->destroy(); } diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 87edb0125e73..98d450b76edc 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -4007,7 +4007,16 @@ void ResTable::print(bool inclValues) const printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); continue; } - printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%d key=%d infl=%d nav=%d w=%d h=%d lyt=%d\n", + char density[16]; + uint16_t dval = dtohs(type->config.density); + if (dval == ResTable_config::DENSITY_DEFAULT) { + strcpy(density, "def"); + } else if (dval == ResTable_config::DENSITY_NONE) { + strcpy(density, "non"); + } else { + sprintf(density, "%d", (int)dval); + } + printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%s key=%d infl=%d nav=%d w=%d h=%d lyt=%d\n", (int)configIndex, type->config.language[0] ? type->config.language[0] : '-', type->config.language[1] ? type->config.language[1] : '-', @@ -4015,7 +4024,7 @@ void ResTable::print(bool inclValues) const type->config.country[1] ? type->config.country[1] : '-', type->config.orientation, type->config.touchscreen, - dtohs(type->config.density), + density, type->config.keyboard, type->config.inputFlags, type->config.navigation, diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java index 3f0c2347d4d3..9e1a72c1a8af 100755 --- a/location/java/com/android/internal/location/GpsLocationProvider.java +++ b/location/java/com/android/internal/location/GpsLocationProvider.java @@ -184,8 +184,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub { // number of fixes we have received since we started navigating private int mFixCount; - private boolean mAgpsConfigured; - // true if we started navigation private boolean mStarted; @@ -356,7 +354,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub { try { int port = Integer.parseInt(portString); native_set_agps_server(AGPS_TYPE_SUPL, host, port); - mAgpsConfigured = true; } catch (NumberFormatException e) { Log.e(TAG, "unable to parse SUPL_PORT: " + portString); } @@ -368,7 +365,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub { try { int port = Integer.parseInt(portString); native_set_agps_server(AGPS_TYPE_C2K, host, port); - mAgpsConfigured = true; } catch (NumberFormatException e) { Log.e(TAG, "unable to parse C2K_PORT: " + portString); } @@ -722,7 +718,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub { if (DEBUG) Log.d(TAG, "startNavigating"); mStarted = true; int positionMode; - if (mAgpsConfigured && Settings.Secure.getInt(mContext.getContentResolver(), + if (Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ASSISTED_GPS_ENABLED, 0) != 0) { positionMode = GPS_POSITION_MODE_MS_BASED; } else { diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index ee41021d0a05..58c04f3a1f74 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -1327,10 +1327,12 @@ public class AudioService extends IAudioService.Stub { } private void persistVolume(VolumeStreamState streamState) { - System.putInt(mContentResolver, streamState.mVolumeIndexSettingName, - streamState.mIndex); - System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName, - streamState.mLastAudibleIndex); + if (streamState.mStreamType != AudioManager.STREAM_BLUETOOTH_SCO) { + System.putInt(mContentResolver, streamState.mVolumeIndexSettingName, + streamState.mIndex); + System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName, + streamState.mLastAudibleIndex); + } } private void persistRingerMode() { diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index fe1d30e0d060..d5801f7b3cdf 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -663,7 +663,7 @@ public class MediaScanner ContentValues values = toValues(); String title = values.getAsString(MediaStore.MediaColumns.TITLE); - if (TextUtils.isEmpty(title.trim())) { + if (title == null || TextUtils.isEmpty(title.trim())) { title = values.getAsString(MediaStore.MediaColumns.DATA); // extract file name after last slash int lastSlash = title.lastIndexOf('/'); diff --git a/media/java/android/media/Metadata.java b/media/java/android/media/Metadata.java index a345ef88b08d..7618435078fb 100644 --- a/media/java/android/media/Metadata.java +++ b/media/java/android/media/Metadata.java @@ -20,10 +20,12 @@ import android.graphics.Bitmap; import android.os.Parcel; import android.util.Log; +import java.util.Calendar; import java.util.Collections; import java.util.Date; -import java.util.Set; import java.util.HashMap; +import java.util.Set; +import java.util.TimeZone; /** @@ -95,22 +97,33 @@ public class Metadata public static final int VIDEO_WIDTH = 26; // Integer public static final int NUM_TRACKS = 27; // Integer public static final int DRM_CRIPPLED = 28; // Boolean - private static final int LAST_SYSTEM = 28; + + // Playback capabilities. + public static final int PAUSE_AVAILABLE = 29; // Boolean + public static final int SEEK_BACKWARD_AVAILABLE = 30; // Boolean + public static final int SEEK_FORWARD_AVAILABLE = 31; // Boolean + + private static final int LAST_SYSTEM = 31; private static final int FIRST_CUSTOM = 8092; // Shorthands to set the MediaPlayer's metadata filter. public static final Set<Integer> MATCH_NONE = Collections.EMPTY_SET; public static final Set<Integer> MATCH_ALL = Collections.singleton(ANY); - public static final int STRING_VAL = 1; - public static final int INTEGER_VAL = 2; - public static final int LONG_VAL = 3; - public static final int DOUBLE_VAL = 4; - public static final int TIMED_TEXT_VAL = 5; - private static final int LAST_TYPE = 5; + public static final int STRING_VAL = 1; + public static final int INTEGER_VAL = 2; + public static final int BOOLEAN_VAL = 3; + public static final int LONG_VAL = 4; + public static final int DOUBLE_VAL = 5; + public static final int TIMED_TEXT_VAL = 6; + public static final int DATE_VAL = 7; + public static final int BYTE_ARRAY_VAL = 8; + // FIXME: misses a type for shared heap is missing (MemoryFile). + // FIXME: misses a type for bitmaps. + private static final int LAST_TYPE = 8; private static final String TAG = "media.Metadata"; - private static final int kMetaHeaderSize = 8; // 8 bytes for the size + the marker + private static final int kMetaHeaderSize = 8; // size + marker private static final int kMetaMarker = 0x4d455441; // 'M' 'E' 'T' 'A' private static final int kRecordHeaderSize = 12; // size + id + type @@ -122,21 +135,28 @@ public class Metadata // Used to look up if a key was present too. // Key: Metadata ID // Value: Offset of the metadata type field in the record. - private final HashMap<Integer, Integer> mKeyToPosMap = new HashMap<Integer, Integer>(); + private final HashMap<Integer, Integer> mKeyToPosMap = + new HashMap<Integer, Integer>(); /** - * Helper class to hold a pair (time, text). Can be used to implement caption. + * Helper class to hold a triple (time, duration, text). Can be used to + * implement caption. */ public class TimedText { private Date mTime; + private int mDuration; // millisec private String mText; - public TimedText(final Date time, final String text) { + + public TimedText(Date time, int duration, String text) { mTime = time; + mDuration = duration; mText = text; } + public String toString() { StringBuilder res = new StringBuilder(80); - res.append(mTime).append(":").append(mText); + res.append(mTime).append("-").append(mDuration) + .append(":").append(mText); return res.toString(); } } @@ -260,8 +280,9 @@ public class Metadata final int pin = parcel.dataPosition(); // to roll back in case of errors. final int size = parcel.readInt(); - if (parcel.dataAvail() < size || size < kMetaHeaderSize) { - Log.e(TAG, "Bad size " + size); + // Magic 4 below is for the int32 'size' just read. + if (parcel.dataAvail() + 4 < size || size < kMetaHeaderSize) { + Log.e(TAG, "Bad size " + size + " avail " + parcel.dataAvail() + " position " + pin); parcel.setDataPosition(pin); return false; } @@ -300,44 +321,64 @@ public class Metadata return mKeyToPosMap.containsKey(metadataId); } - // Accessors + // Accessors. + // Caller must make sure the key is present using the {@code has} + // method otherwise a RuntimeException will occur. + public String getString(final int key) { - // FIXME: Implement. - return new String(); + checkType(key, STRING_VAL); + return mParcel.readString(); } public int getInt(final int key) { - // FIXME: Implement. - return 0; + checkType(key, INTEGER_VAL); + return mParcel.readInt(); + } + + public boolean getBoolean(final int key) { + checkType(key, BOOLEAN_VAL); + return mParcel.readInt() == 1; } public long getLong(final int key) { - // FIXME: Implement. - return 0; + checkType(key, LONG_VAL); + return mParcel.readLong(); } public double getDouble(final int key) { - // FIXME: Implement. - return 0.0; + checkType(key, DOUBLE_VAL); + return mParcel.readDouble(); } public byte[] getByteArray(final int key) { - return new byte[0]; - } - - public Bitmap getBitmap(final int key) { - // FIXME: Implement. - return null; + checkType(key, BYTE_ARRAY_VAL); + return mParcel.createByteArray(); } public Date getDate(final int key) { - // FIXME: Implement. - return new Date(); + checkType(key, DATE_VAL); + final long timeSinceEpoch = mParcel.readLong(); + final String timeZone = mParcel.readString(); + + if (timeZone.length() == 0) { + return new Date(timeSinceEpoch); + } else { + TimeZone tz = TimeZone.getTimeZone(timeZone); + Calendar cal = Calendar.getInstance(tz); + + cal.setTimeInMillis(timeSinceEpoch); + return cal.getTime(); + } } public TimedText getTimedText(final int key) { - // FIXME: Implement. - return new TimedText(new Date(0), "<missing>"); + checkType(key, TIMED_TEXT_VAL); + final Date startTime = new Date(mParcel.readLong()); // epoch + final int duration = mParcel.readInt(); // millisec + + return new TimedText(startTime, + duration, + mParcel.readString()); } // @return the last available system metadata id. Ids are @@ -360,4 +401,16 @@ public class Metadata } return true; } + + // Check the type of the data match what is expected. + private void checkType(final int key, final int expectedType) { + final int pos = mKeyToPosMap.get(key); + + mParcel.setDataPosition(pos); + + final int type = mParcel.readInt(); + if (type != expectedType) { + throw new IllegalStateException("Wrong type " + expectedType + " but got " + type); + } + } } diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 02327d88f050..5e62f9d8f8e5 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -99,6 +99,8 @@ const int kMaxFilterSize = 64; // I pulled that out of thin air. // Keep in sync with ANY in Metadata.java const int32_t kAny = 0; +const int32_t kMetaMarker = 0x4d455441; // 'M' 'E' 'T' 'A' + // Unmarshall a filter from a Parcel. // Filter format in a parcel: @@ -870,10 +872,14 @@ status_t MediaPlayerService::Client::setMetadataFilter(const Parcel& filter) status_t MediaPlayerService::Client::getMetadata( bool update_only, bool apply_filter, Parcel *reply) { + sp<MediaPlayerBase> p = getPlayer(); + if (p == 0) return UNKNOWN_ERROR; + status_t status; - reply->writeInt32(-1); // Placeholder for the return code + // Placeholder for the return code, updated by the caller. + reply->writeInt32(-1); - SortedVector<MetadataType> updates; + SortedVector<MetadataType> ids; // We don't block notifications while we fetch the data. We clear // mMetadataUpdated first so we don't lose notifications happening @@ -881,15 +887,34 @@ status_t MediaPlayerService::Client::getMetadata( { Mutex::Autolock lock(mLock); if (update_only) { - updates = mMetadataUpdated; + ids = mMetadataUpdated; } mMetadataUpdated.clear(); } - // FIXME: Implement, query the native player and do the optional filtering, etc... - status = OK; + const size_t begin = reply->dataPosition(); + reply->writeInt32(-1); // Placeholder for the length of the metadata + reply->writeInt32(kMetaMarker); - return status; + status = p->getMetadata(ids, reply); + + if (status != OK) { + reply->setDataPosition(begin); + LOGE("getMetadata failed %d", status); + return status; + } + + // FIXME: Implement filtering on the result. Not critical since + // filtering takes place on the update notifications already. This + // would be when all the metadata are fetch and a filter is set. + + const size_t end = reply->dataPosition(); + + // Everything is fine, update the metadata length. + reply->setDataPosition(begin); + reply->writeInt32(end - begin); + reply->setDataPosition(end); + return OK; } status_t MediaPlayerService::Client::prepareAsync() diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h index 83d97fee6dfa..30b6a2e0c307 100644 --- a/media/libmediaplayerservice/MidiFile.h +++ b/media/libmediaplayerservice/MidiFile.h @@ -46,7 +46,13 @@ public: virtual status_t reset(); virtual status_t setLooping(int loop); virtual player_type playerType() { return SONIVOX_PLAYER; } - virtual status_t invoke(const Parcel& request, Parcel *reply) {return INVALID_OPERATION;} + virtual status_t invoke(const Parcel& request, Parcel *reply) { + return INVALID_OPERATION; + } + virtual status_t getMetadata(const SortedVector<MetadataType>& ids, + Parcel *records) { + return INVALID_OPERATION; + } private: status_t createOutputTrack(); diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp index ad1afbb05bc3..8597275048e7 100644 --- a/media/libmediaplayerservice/StagefrightPlayer.cpp +++ b/media/libmediaplayerservice/StagefrightPlayer.cpp @@ -205,4 +205,9 @@ void StagefrightPlayer::setAudioSink(const sp<AudioSink> &audioSink) { } } +status_t StagefrightPlayer::getMetadata( + const SortedVector<MetadataType> &ids, Parcel *records) { + return INVALID_OPERATION; +} + } // namespace android diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h index f214872c7c64..f93c1f854999 100644 --- a/media/libmediaplayerservice/StagefrightPlayer.h +++ b/media/libmediaplayerservice/StagefrightPlayer.h @@ -48,6 +48,9 @@ public: virtual status_t invoke(const Parcel &request, Parcel *reply); virtual void setAudioSink(const sp<AudioSink> &audioSink); + virtual status_t getMetadata( + const SortedVector<MetadataType> &ids, Parcel *records); + private: MediaPlayerImpl *mPlayer; diff --git a/media/libmediaplayerservice/TestPlayerStub.h b/media/libmediaplayerservice/TestPlayerStub.h index 80d53a8b95d8..339b10851bc6 100644 --- a/media/libmediaplayerservice/TestPlayerStub.h +++ b/media/libmediaplayerservice/TestPlayerStub.h @@ -94,6 +94,10 @@ class TestPlayerStub : public MediaPlayerInterface { virtual status_t invoke(const android::Parcel& in, android::Parcel *out) { return mPlayer->invoke(in, out); } + virtual status_t getMetadata(const SortedVector<MetadataType>& ids, + Parcel *records) { + return INVALID_OPERATION; + } // @return true if the current build is 'eng' or 'test' and the diff --git a/media/libmediaplayerservice/VorbisPlayer.h b/media/libmediaplayerservice/VorbisPlayer.h index 40246543d4ce..040eb36a7c7d 100644 --- a/media/libmediaplayerservice/VorbisPlayer.h +++ b/media/libmediaplayerservice/VorbisPlayer.h @@ -54,6 +54,10 @@ public: virtual status_t setLooping(int loop); virtual player_type playerType() { return VORBIS_PLAYER; } virtual status_t invoke(const Parcel& request, Parcel *reply) {return INVALID_OPERATION;} + virtual status_t getMetadata(const SortedVector<MetadataType>& ids, + Parcel *records) { + return INVALID_OPERATION; + } private: status_t setdatasource(const char *path, int fd, int64_t offset, int64_t length); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java index f51b29e073ea..637ebb861420 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java @@ -21,6 +21,9 @@ import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; +import java.util.Calendar; +import java.util.Date; + /* * Check the Java layer that parses serialized metadata in Parcel * works as expected. @@ -53,15 +56,10 @@ public class MediaPlayerMetadataParserTest extends AndroidTestCase { assertEquals(0, mParcel.dataPosition()); } - // Check parsing of the parcel is successful. Before the - // invocation of the parser a token is inserted. When the parser - // returns, the parcel should be positioned at the token (check it - // does not read too much data). + // Check parsing of the parcel is successful. private void assertParse() throws Exception { - mParcel.writeInt(kToken); mParcel.setDataPosition(0); assertTrue(mMetadata.parse(mParcel)); - assertEquals(kToken, mParcel.readInt()); } // Write the number of bytes from the start of the parcel to the @@ -89,16 +87,6 @@ public class MediaPlayerMetadataParserTest extends AndroidTestCase { mParcel.writeInt(kMarker); } - // Insert a string record at the current position. - private void writeStringRecord(int metadataId, String val) { - final int start = mParcel.dataPosition(); - mParcel.writeInt(-1); // Placeholder for the length - mParcel.writeInt(metadataId); - mParcel.writeInt(Metadata.STRING_VAL); - mParcel.writeString(val); - adjustSize(start); - } - // ---------------------------------------------------------------------- // START OF THE TESTS @@ -133,7 +121,9 @@ public class MediaPlayerMetadataParserTest extends AndroidTestCase { assertParse(); } + // ---------------------------------------------------------------------- // RECORDS + // ---------------------------------------------------------------------- // A record header should be at least 12 bytes long @SmallTest @@ -223,4 +213,221 @@ public class MediaPlayerMetadataParserTest extends AndroidTestCase { assertFalse(mMetadata.has(Metadata.GENRE)); assertFalse(mMetadata.has(Metadata.firstCustomId())); } + + // ---------------------------------------------------------------------- + // GETTERS + // ---------------------------------------------------------------------- + + // getString + @SmallTest + public void testGetString() throws Exception { + writeStringRecord(Metadata.TITLE, "a title"); + writeStringRecord(Metadata.GENRE, "comedy"); + adjustSize(); + assertParse(); + + assertEquals("a title", mMetadata.getString(Metadata.TITLE)); + assertEquals("comedy", mMetadata.getString(Metadata.GENRE)); + } + + // get an empty string. + @SmallTest + public void testGetEmptyString() throws Exception { + writeStringRecord(Metadata.TITLE, ""); + adjustSize(); + assertParse(); + + assertEquals("", mMetadata.getString(Metadata.TITLE)); + } + + // get a string when a NULL value was in the parcel + @SmallTest + public void testGetNullString() throws Exception { + writeStringRecord(Metadata.TITLE, null); + adjustSize(); + assertParse(); + + assertEquals(null, mMetadata.getString(Metadata.TITLE)); + } + + // get a string when an integer is actually present + @SmallTest + public void testWrongType() throws Exception { + writeIntRecord(Metadata.DURATION, 5); + adjustSize(); + assertParse(); + + try { + mMetadata.getString(Metadata.DURATION); + } catch (IllegalStateException ise) { + return; + } + fail("Exception was not thrown"); + } + + // getInt + @SmallTest + public void testGetInt() throws Exception { + writeIntRecord(Metadata.CD_TRACK_NUM, 1); + adjustSize(); + assertParse(); + + assertEquals(1, mMetadata.getInt(Metadata.CD_TRACK_NUM)); + } + + // getBoolean + @SmallTest + public void testGetBoolean() throws Exception { + writeBooleanRecord(Metadata.DRM_CRIPPLED, true); + adjustSize(); + assertParse(); + + assertEquals(true, mMetadata.getBoolean(Metadata.DRM_CRIPPLED)); + } + + // getLong + @SmallTest + public void testGetLong() throws Exception { + writeLongRecord(Metadata.DURATION, 1L); + adjustSize(); + assertParse(); + + assertEquals(1L, mMetadata.getLong(Metadata.DURATION)); + } + + // getDouble + @SmallTest + public void testGetDouble() throws Exception { + writeDoubleRecord(Metadata.VIDEO_FRAME_RATE, 29.97); + adjustSize(); + assertParse(); + + assertEquals(29.97, mMetadata.getDouble(Metadata.VIDEO_FRAME_RATE)); + } + + // getByteArray + @SmallTest + public void testGetByteArray() throws Exception { + byte data[] = new byte[]{1,2,3,4,5}; + + writeByteArrayRecord(Metadata.ALBUM_ART, data); + adjustSize(); + assertParse(); + + byte res[] = mMetadata.getByteArray(Metadata.ALBUM_ART); + for (int i = 0; i < data.length; ++i) { + assertEquals(data[i], res[i]); + } + } + + // getDate + @SmallTest + public void testGetDate() throws Exception { + writeDateRecord(Metadata.DATE, 0, "PST"); + adjustSize(); + assertParse(); + + assertEquals(new Date(0), mMetadata.getDate(Metadata.DATE)); + } + + // getTimedText + @SmallTest + public void testGetTimedText() throws Exception { + Date now = Calendar.getInstance().getTime(); + writeTimedTextRecord(Metadata.CAPTION, now.getTime(), + 10, "Some caption"); + adjustSize(); + assertParse(); + + Metadata.TimedText caption = mMetadata.getTimedText(Metadata.CAPTION); + assertEquals("" + now + "-" + 10 + ":Some caption", caption.toString()); + } + + // ---------------------------------------------------------------------- + // HELPERS TO APPEND RECORDS + // ---------------------------------------------------------------------- + + // Insert a string record at the current position. + private void writeStringRecord(int metadataId, String val) { + final int start = mParcel.dataPosition(); + mParcel.writeInt(-1); // Placeholder for the length + mParcel.writeInt(metadataId); + mParcel.writeInt(Metadata.STRING_VAL); + mParcel.writeString(val); + adjustSize(start); + } + + // Insert an int record at the current position. + private void writeIntRecord(int metadataId, int val) { + final int start = mParcel.dataPosition(); + mParcel.writeInt(-1); // Placeholder for the length + mParcel.writeInt(metadataId); + mParcel.writeInt(Metadata.INTEGER_VAL); + mParcel.writeInt(val); + adjustSize(start); + } + + // Insert a boolean record at the current position. + private void writeBooleanRecord(int metadataId, boolean val) { + final int start = mParcel.dataPosition(); + mParcel.writeInt(-1); // Placeholder for the length + mParcel.writeInt(metadataId); + mParcel.writeInt(Metadata.BOOLEAN_VAL); + mParcel.writeInt(val ? 1 : 0); + adjustSize(start); + } + + // Insert a Long record at the current position. + private void writeLongRecord(int metadataId, long val) { + final int start = mParcel.dataPosition(); + mParcel.writeInt(-1); // Placeholder for the length + mParcel.writeInt(metadataId); + mParcel.writeInt(Metadata.LONG_VAL); + mParcel.writeLong(val); + adjustSize(start); + } + + // Insert a Double record at the current position. + private void writeDoubleRecord(int metadataId, double val) { + final int start = mParcel.dataPosition(); + mParcel.writeInt(-1); // Placeholder for the length + mParcel.writeInt(metadataId); + mParcel.writeInt(Metadata.DOUBLE_VAL); + mParcel.writeDouble(val); + adjustSize(start); + } + + // Insert a ByteArray record at the current position. + private void writeByteArrayRecord(int metadataId, byte[] val) { + final int start = mParcel.dataPosition(); + mParcel.writeInt(-1); // Placeholder for the length + mParcel.writeInt(metadataId); + mParcel.writeInt(Metadata.BYTE_ARRAY_VAL); + mParcel.writeByteArray(val); + adjustSize(start); + } + + // Insert a Date record at the current position. + private void writeDateRecord(int metadataId, long time, String tz) { + final int start = mParcel.dataPosition(); + mParcel.writeInt(-1); // Placeholder for the length + mParcel.writeInt(metadataId); + mParcel.writeInt(Metadata.DATE_VAL); + mParcel.writeLong(time); + mParcel.writeString(tz); + adjustSize(start); + } + + // Insert a TimedText record at the current position. + private void writeTimedTextRecord(int metadataId, long begin, + int duration, String text) { + final int start = mParcel.dataPosition(); + mParcel.writeInt(-1); // Placeholder for the length + mParcel.writeInt(metadataId); + mParcel.writeInt(Metadata.TIMED_TEXT_VAL); + mParcel.writeLong(begin); + mParcel.writeInt(duration); + mParcel.writeString(text); + adjustSize(start); + } } diff --git a/media/tests/players/invoke_mock_media_player.cpp b/media/tests/players/invoke_mock_media_player.cpp index f02298d9ab83..8d575a35b29c 100644 --- a/media/tests/players/invoke_mock_media_player.cpp +++ b/media/tests/players/invoke_mock_media_player.cpp @@ -20,14 +20,17 @@ #include <string.h> -#include "binder/Parcel.h" -#include "media/MediaPlayerInterface.h" -#include "utils/Errors.h" +#include <binder/Parcel.h> +#include <media/MediaPlayerInterface.h> +#include <utils/Errors.h> +using android::INVALID_OPERATION; using android::ISurface; using android::MediaPlayerBase; +using android::MetadataType; using android::OK; using android::Parcel; +using android::SortedVector; using android::TEST_PLAYER; using android::UNKNOWN_ERROR; using android::player_type; @@ -75,6 +78,9 @@ class Player: public MediaPlayerBase virtual status_t setLooping(int loop) {return OK;} virtual player_type playerType() {return TEST_PLAYER;} virtual status_t invoke(const Parcel& request, Parcel *reply); + virtual status_t getMetadata(const SortedVector<MetadataType>& ids, + Parcel *records) {return INVALID_OPERATION;} + private: // Take a request, copy it to the reply. void ping(const Parcel& request, Parcel *reply); diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp index 64cdb5b80160..68e3fb727a0e 100644 --- a/packages/TtsService/jni/android_tts_SynthProxy.cpp +++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp @@ -194,6 +194,7 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate, if (bufferSize > 0) { prepAudioTrack(pJniData, pForAfter->streamType, rate, format, channel); if (pJniData->mAudioOut) { + pJniData->mAudioOut->start(); pJniData->mAudioOut->write(wav, bufferSize); memset(wav, 0, bufferSize); //LOGV("AudioTrack wrote: %d bytes", bufferSize); @@ -286,6 +287,7 @@ android_tts_SynthProxy_native_finalize(JNIEnv *env, jobject thiz, jint jniData) { if (jniData) { SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; + env->DeleteGlobalRef(pSynthData->tts_ref); delete pSynthData; } } @@ -548,7 +550,6 @@ android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData, if (pSynthData->mAudioOut) { pSynthData->mAudioOut->stop(); - pSynthData->mAudioOut->start(); } afterSynthData_t* pForAfter = new (afterSynthData_t); @@ -607,24 +608,6 @@ android_tts_SynthProxy_shutdown(JNIEnv *env, jobject thiz, jint jniData) } -// TODO add buffer format -static void -android_tts_SynthProxy_playAudioBuffer(JNIEnv *env, jobject thiz, jint jniData, - int bufferPointer, int bufferSize) -{ -LOGI("android_tts_SynthProxy_playAudioBuffer"); - if (jniData == 0) { - LOGE("android_tts_SynthProxy_playAudioBuffer(): invalid JNI data"); - return; - } - - SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; - short* wav = (short*) bufferPointer; - pSynthData->mAudioOut->write(wav, bufferSize); - //LOGI("AudioTrack wrote: %d bytes", bufferSize); -} - - static jobjectArray android_tts_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData) { @@ -710,10 +693,6 @@ static JNINativeMethod gMethods[] = { "(II)I", (void*)android_tts_SynthProxy_setPitch }, - { "native_playAudioBuffer", - "(III)V", - (void*)android_tts_SynthProxy_playAudioBuffer - }, { "native_getLanguage", "(I)[Ljava/lang/String;", (void*)android_tts_SynthProxy_getLanguage diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java index 41ff92a1b9bd..a0814aa912a2 100755 --- a/packages/TtsService/src/android/tts/SynthProxy.java +++ b/packages/TtsService/src/android/tts/SynthProxy.java @@ -109,13 +109,6 @@ public class SynthProxy { } /** - * Plays the given audio buffer. - */ - public void playAudioBuffer(int bufferPointer, int bufferSize) { - native_playAudioBuffer(mJniData, bufferPointer, bufferSize); - } - - /** * Returns the currently set language, country and variant information. */ public String[] getLanguage() { @@ -180,9 +173,6 @@ public class SynthProxy { private native final int native_setPitch(int jniData, int speechRate); - // TODO add buffer format - private native final void native_playAudioBuffer(int jniData, int bufferPointer, int bufferSize); - private native final String[] native_getLanguage(int jniData); private native final int native_getRate(int jniData); diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java index b3b580c55e4a..28801f8b50ad 100755 --- a/packages/TtsService/src/android/tts/TtsService.java +++ b/packages/TtsService/src/android/tts/TtsService.java @@ -551,16 +551,16 @@ public class TtsService extends Service implements OnCompletionListener { return; } int streamType = DEFAULT_STREAM_TYPE; + String language = ""; + String country = ""; + String variant = ""; + String speechRate = ""; if (speechItem.mParams != null){ - String language = ""; - String country = ""; - String variant = ""; for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){ String param = speechItem.mParams.get(i); if (param != null) { if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)) { - setSpeechRate("", - Integer.parseInt(speechItem.mParams.get(i+1))); + speechRate = speechItem.mParams.get(i+1); } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){ language = speechItem.mParams.get(i+1); } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_COUNTRY)){ @@ -579,12 +579,15 @@ public class TtsService extends Service implements OnCompletionListener { } } } + } + // Only do the synthesis if it has not been killed by a subsequent utterance. + if (mKillList.get(speechItem) == null) { if (language.length() > 0){ setLanguage("", language, country, variant); } - } - // Only do the synthesis if it has not been killed by a subsequent utterance. - if (mKillList.get(speechItem) == null){ + if (speechRate.length() > 0){ + setSpeechRate("", Integer.parseInt(speechRate)); + } nativeSynth.speak(speechItem.mText, streamType); } } catch (InterruptedException e) { @@ -624,16 +627,16 @@ public class TtsService extends Service implements OnCompletionListener { synth.start(); return; } + String language = ""; + String country = ""; + String variant = ""; + String speechRate = ""; if (speechItem.mParams != null){ - String language = ""; - String country = ""; - String variant = ""; for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){ String param = speechItem.mParams.get(i); - if (param != null){ - if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)){ - setSpeechRate("", - Integer.parseInt(speechItem.mParams.get(i+1))); + if (param != null) { + if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)) { + speechRate = speechItem.mParams.get(i+1); } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){ language = speechItem.mParams.get(i+1); } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_COUNTRY)){ @@ -645,12 +648,15 @@ public class TtsService extends Service implements OnCompletionListener { } } } - if (language.length() > 0){ - setLanguage("", language, country, variant); - } } // Only do the synthesis if it has not been killed by a subsequent utterance. if (mKillList.get(speechItem) == null){ + if (language.length() > 0){ + setLanguage("", language, country, variant); + } + if (speechRate.length() > 0){ + setSpeechRate("", Integer.parseInt(speechRate)); + } nativeSynth.synthesizeToFile(speechItem.mText, speechItem.mFilename); } } catch (InterruptedException e) { diff --git a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java index c6c94521faf8..e4c070ff8b29 100644 --- a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java +++ b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java @@ -106,6 +106,13 @@ public class AndroidServiceProxy extends ProcessProxy { @Override protected void performTask() throws IOException { String svc = mServiceName; + Log.d(mTag, "----- Stop the daemon just in case: " + mServiceName); + SystemProperties.set(SVC_STOP_CMD, mServiceName); + if (!blockUntil(SVC_STATE_STOPPED, 5)) { + throw new IOException("cannot start service anew: " + svc + + ", it is still running"); + } + Log.d(mTag, "+++++ Start: " + svc); SystemProperties.set(SVC_START_CMD, svc); diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java index cf153e3572e8..32b8e51f278d 100644 --- a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java +++ b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java @@ -57,6 +57,7 @@ public class VpnServiceBinder extends Service { public void onStart (Intent intent, int startId) { super.onStart(intent, startId); setForeground(true); + android.util.Log.d("VpnServiceBinder", "becomes a foreground service"); } public IBinder onBind(Intent intent) { @@ -71,9 +72,8 @@ public class VpnServiceBinder extends Service { } private synchronized void checkStatus(VpnProfile p) { - if (mService == null) broadcastConnectivity(p.getName(), VpnState.IDLE); - - if (!p.getName().equals(mService.mProfile.getName())) { + if ((mService == null) + || (!p.getName().equals(mService.mProfile.getName()))) { broadcastConnectivity(p.getName(), VpnState.IDLE); } else { broadcastConnectivity(p.getName(), mService.getState()); diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 682eaa7c89bb..7025e79776cd 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -4908,6 +4908,22 @@ class PackageManagerService extends IPackageManager.Stub { pw.print(" resourcePath="); pw.println(ps.resourcePathString); if (ps.pkg != null) { pw.print(" dataDir="); pw.println(ps.pkg.applicationInfo.dataDir); + pw.print(" targetSdk="); pw.println(ps.pkg.applicationInfo.targetSdkVersion); + pw.print(" densities="); pw.println(ps.pkg.supportsDensityList); + ArrayList<String> screens = new ArrayList<String>(); + if ((ps.pkg.applicationInfo.flags & + ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS) != 0) { + screens.add("medium"); + } + if ((ps.pkg.applicationInfo.flags & + ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) { + screens.add("large"); + } + if ((ps.pkg.applicationInfo.flags & + ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) { + screens.add("small,"); + } + pw.print(" supportsScreens="); pw.println(screens); } pw.print(" timeStamp="); pw.println(ps.getTimeStampStr()); pw.print(" signatures="); pw.println(ps.signatures); diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index e41524e4aebd..f04cfd6453a6 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -1898,7 +1898,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo break; case WindowManagerPolicy.TRANSIT_TASK_TO_BACK: animAttr = enter - ? com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation + ? com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation : com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation; break; } @@ -7281,17 +7281,27 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public static WindowManager.LayoutParams findAnimations( ArrayList<AppWindowToken> order, - ArrayList<AppWindowToken> tokenList1, - ArrayList<AppWindowToken> tokenList2) { + ArrayList<AppWindowToken> openingTokenList1, + ArrayList<AppWindowToken> closingTokenList2) { // We need to figure out which animation to use... + + // First, check if there is a compatible window in opening/closing + // apps, and use it if exists. WindowManager.LayoutParams animParams = null; int animSrc = 0; - + animParams = findCompatibleWindowParams(openingTokenList1); + if (animParams == null) { + animParams = findCompatibleWindowParams(closingTokenList2); + } + if (animParams != null) { + return animParams; + } + //Log.i(TAG, "Looking for animations..."); for (int i=order.size()-1; i>=0; i--) { AppWindowToken wtoken = order.get(i); //Log.i(TAG, "Token " + wtoken + " with " + wtoken.windows.size() + " windows"); - if (tokenList1.contains(wtoken) || tokenList2.contains(wtoken)) { + if (openingTokenList1.contains(wtoken) || closingTokenList2.contains(wtoken)) { int j = wtoken.windows.size(); while (j > 0) { j--; @@ -7319,6 +7329,21 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return animParams; } + private static LayoutParams findCompatibleWindowParams(ArrayList<AppWindowToken> tokenList) { + for (int appCount = tokenList.size() - 1; appCount >= 0; appCount--) { + AppWindowToken wtoken = tokenList.get(appCount); + // Just checking one window is sufficient as all windows have the compatible flag + // if the application is in compatibility mode. + if (wtoken.windows.size() > 0) { + WindowManager.LayoutParams params = wtoken.windows.get(0).mAttrs; + if ((params.flags & FLAG_COMPATIBLE_WINDOW) != 0) { + return params; + } + } + } + return null; + } + // ------------------------------------------------------------- // DummyAnimation // ------------------------------------------------------------- @@ -9326,16 +9351,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // width is the screen width {@see AppWindowToken#stepAnimatinoLocked} mWidth = width; } - - @Override - public boolean willChangeTransformationMatrix() { - return true; - } @Override - public boolean willChangeBounds() { - return true; + public int getZAdjustment() { + return Animation.ZORDER_TOP; } } } - diff --git a/tests/AndroidTests/res/raw/v21_simple_1.vcf b/tests/AndroidTests/res/raw/v21_simple_1.vcf new file mode 100644 index 000000000000..6aabb4c023e0 --- /dev/null +++ b/tests/AndroidTests/res/raw/v21_simple_1.vcf @@ -0,0 +1,3 @@ +BEGIN:VCARD
+N:Ando;Roid;
+END:VCARD
diff --git a/tests/AndroidTests/res/raw/v21_simple_2.vcf b/tests/AndroidTests/res/raw/v21_simple_2.vcf new file mode 100644 index 000000000000..f0d5ab506ae6 --- /dev/null +++ b/tests/AndroidTests/res/raw/v21_simple_2.vcf @@ -0,0 +1,3 @@ +BEGIN:VCARD
+FN:Ando Roid
+END:VCARD
diff --git a/tests/AndroidTests/res/raw/v21_simple.vcf b/tests/AndroidTests/res/raw/v21_simple_3.vcf index beddabb96086..beddabb96086 100644 --- a/tests/AndroidTests/res/raw/v21_simple.vcf +++ b/tests/AndroidTests/res/raw/v21_simple_3.vcf diff --git a/tests/AndroidTests/src/com/android/unit_tests/content/ConfigTest.java b/tests/AndroidTests/src/com/android/unit_tests/content/ConfigTest.java index e6639d3720cd..a065d7016af0 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/content/ConfigTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/content/ConfigTest.java @@ -133,7 +133,7 @@ public class ConfigTest extends AndroidTestCase { case DENSITY: // this is the ratio from the standard - mMetrics.density = (((float)value)/((float)DisplayMetrics.DEFAULT_DENSITY)); + mMetrics.density = (((float)value)/((float)DisplayMetrics.DENSITY_DEFAULT)); break; default: assert(false); diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java new file mode 100644 index 000000000000..0ee74dfc16f0 --- /dev/null +++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java @@ -0,0 +1,322 @@ +/* + * 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.android.unit_tests.vcard; + +import android.content.ContentValues; + +import org.apache.commons.codec.binary.Base64; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.Map.Entry; +import java.util.regex.Pattern; + +/** + * @hide old class just for test + */ +public class PropertyNode { + public String propName; + public String propValue; + public List<String> propValue_vector; + + /** Store value as byte[],after decode. + * Used when propValue is encoded by something like BASE64, QUOTED-PRINTABLE, etc. + */ + public byte[] propValue_bytes; + + /** param store: key=paramType, value=paramValue + * Note that currently PropertyNode class does not support multiple param-values + * defined in vCard 3.0 (See also RFC 2426). multiple-values are stored as + * one String value like "A,B", not ["A", "B"]... + * TODO: fix this. + */ + public ContentValues paramMap; + + /** Only for TYPE=??? param store. */ + public Set<String> paramMap_TYPE; + + /** Store group values. Used only in VCard. */ + public Set<String> propGroupSet; + + public PropertyNode() { + propName = ""; + propValue = ""; + propValue_vector = new ArrayList<String>(); + paramMap = new ContentValues(); + paramMap_TYPE = new HashSet<String>(); + propGroupSet = new HashSet<String>(); + } + + public PropertyNode( + String propName, String propValue, List<String> propValue_vector, + byte[] propValue_bytes, ContentValues paramMap, Set<String> paramMap_TYPE, + Set<String> propGroupSet) { + if (propName != null) { + this.propName = propName; + } else { + this.propName = ""; + } + if (propValue != null) { + this.propValue = propValue; + } else { + this.propValue = ""; + } + if (propValue_vector != null) { + this.propValue_vector = propValue_vector; + } else { + this.propValue_vector = new ArrayList<String>(); + } + this.propValue_bytes = propValue_bytes; + if (paramMap != null) { + this.paramMap = paramMap; + } else { + this.paramMap = new ContentValues(); + } + if (paramMap_TYPE != null) { + this.paramMap_TYPE = paramMap_TYPE; + } else { + this.paramMap_TYPE = new HashSet<String>(); + } + if (propGroupSet != null) { + this.propGroupSet = propGroupSet; + } else { + this.propGroupSet = new HashSet<String>(); + } + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PropertyNode)) { + return false; + } + + PropertyNode node = (PropertyNode)obj; + + if (propName == null || !propName.equals(node.propName)) { + return false; + } else if (!paramMap.equals(node.paramMap)) { + return false; + } else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) { + return false; + } else if (!propGroupSet.equals(node.propGroupSet)) { + return false; + } + + if (propValue_bytes != null && Arrays.equals(propValue_bytes, node.propValue_bytes)) { + return true; + } else { + // Log.d("@@@", propValue + ", " + node.propValue); + if (!propValue.equals(node.propValue)) { + return false; + } + + // The value in propValue_vector is not decoded even if it should be + // decoded by BASE64 or QUOTED-PRINTABLE. When the size of propValue_vector + // is 1, the encoded value is stored in propValue, so we do not have to + // check it. + return (propValue_vector.equals(node.propValue_vector) || + propValue_vector.size() == 1 || + node.propValue_vector.size() == 1); + } + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("propName: "); + builder.append(propName); + builder.append(", paramMap: "); + builder.append(paramMap.toString()); + builder.append(", propmMap_TYPE: "); + builder.append(paramMap_TYPE.toString()); + builder.append(", propGroupSet: "); + builder.append(propGroupSet.toString()); + if (propValue_vector != null && propValue_vector.size() > 1) { + builder.append(", propValue_vector size: "); + builder.append(propValue_vector.size()); + } + if (propValue_bytes != null) { + builder.append(", propValue_bytes size: "); + builder.append(propValue_bytes.length); + } + builder.append(", propValue: "); + builder.append(propValue); + return builder.toString(); + } + + /** + * Encode this object into a string which can be decoded. + */ + public String encode() { + // PropertyNode#toString() is for reading, not for parsing in the future. + // We construct appropriate String here. + StringBuilder builder = new StringBuilder(); + if (propName.length() > 0) { + builder.append("propName:["); + builder.append(propName); + builder.append("],"); + } + int size = propGroupSet.size(); + if (size > 0) { + Set<String> set = propGroupSet; + builder.append("propGroup:["); + int i = 0; + for (String group : set) { + // We do not need to double quote groups. + // group = 1*(ALPHA / DIGIT / "-") + builder.append(group); + if (i < size - 1) { + builder.append(","); + } + i++; + } + builder.append("],"); + } + + if (paramMap.size() > 0 || paramMap_TYPE.size() > 0) { + ContentValues values = paramMap; + builder.append("paramMap:["); + size = paramMap.size(); + int i = 0; + for (Entry<String, Object> entry : values.valueSet()) { + // Assuming param-key does not contain NON-ASCII nor symbols. + // + // According to vCard 3.0: + // param-name = iana-token / x-name + builder.append(entry.getKey()); + + // param-value may contain any value including NON-ASCIIs. + // We use the following replacing rule. + // \ -> \\ + // , -> \, + // In String#replaceAll(), "\\\\" means a single backslash. + builder.append("="); + builder.append(entry.getValue().toString() + .replaceAll("\\\\", "\\\\\\\\") + .replaceAll(",", "\\\\,")); + if (i < size -1) { + builder.append(","); + } + i++; + } + + Set<String> set = paramMap_TYPE; + size = paramMap_TYPE.size(); + if (i > 0 && size > 0) { + builder.append(","); + } + i = 0; + for (String type : set) { + builder.append("TYPE="); + builder.append(type + .replaceAll("\\\\", "\\\\\\\\") + .replaceAll(",", "\\\\,")); + if (i < size - 1) { + builder.append(","); + } + i++; + } + builder.append("],"); + } + + size = propValue_vector.size(); + if (size > 0) { + builder.append("propValue:["); + List<String> list = propValue_vector; + for (int i = 0; i < size; i++) { + builder.append(list.get(i) + .replaceAll("\\\\", "\\\\\\\\") + .replaceAll(",", "\\\\,")); + if (i < size -1) { + builder.append(","); + } + } + builder.append("],"); + } + + return builder.toString(); + } + + public static PropertyNode decode(String encodedString) { + PropertyNode propertyNode = new PropertyNode(); + String trimed = encodedString.trim(); + if (trimed.length() == 0) { + return propertyNode; + } + String[] elems = trimed.split("],"); + + for (String elem : elems) { + int index = elem.indexOf('['); + String name = elem.substring(0, index - 1); + Pattern pattern = Pattern.compile("(?<!\\\\),"); + String[] values = pattern.split(elem.substring(index + 1), -1); + if (name.equals("propName")) { + propertyNode.propName = values[0]; + } else if (name.equals("propGroupSet")) { + for (String value : values) { + propertyNode.propGroupSet.add(value); + } + } else if (name.equals("paramMap")) { + ContentValues paramMap = propertyNode.paramMap; + Set<String> paramMap_TYPE = propertyNode.paramMap_TYPE; + for (String value : values) { + String[] tmp = value.split("=", 2); + String mapKey = tmp[0]; + // \, -> , + // \\ -> \ + // In String#replaceAll(), "\\\\" means a single backslash. + String mapValue = + tmp[1].replaceAll("\\\\,", ",").replaceAll("\\\\\\\\", "\\\\"); + if (mapKey.equalsIgnoreCase("TYPE")) { + paramMap_TYPE.add(mapValue); + } else { + paramMap.put(mapKey, mapValue); + } + } + } else if (name.equals("propValue")) { + StringBuilder builder = new StringBuilder(); + List<String> list = propertyNode.propValue_vector; + int length = values.length; + for (int i = 0; i < length; i++) { + String normValue = values[i] + .replaceAll("\\\\,", ",") + .replaceAll("\\\\\\\\", "\\\\"); + list.add(normValue); + builder.append(normValue); + if (i < length - 1) { + builder.append(";"); + } + } + propertyNode.propValue = builder.toString(); + } + } + + // At this time, QUOTED-PRINTABLE is already decoded to Java String. + // We just need to decode BASE64 String to binary. + String encoding = propertyNode.paramMap.getAsString("ENCODING"); + if (encoding != null && + (encoding.equalsIgnoreCase("BASE64") || + encoding.equalsIgnoreCase("B"))) { + propertyNode.propValue_bytes = + Base64.decodeBase64(propertyNode.propValue_vector.get(0).getBytes()); + } + + return propertyNode; + } +} diff --git a/tests/AndroidTests/src/com/android/unit_tests/VCardTests.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTests.java index b7f562ddc947..af5562ad4ba4 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/VCardTests.java +++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTests.java @@ -14,36 +14,132 @@ * limitations under the License. */ -package com.android.unit_tests; +package com.android.unit_tests.vcard; import android.content.ContentValues; -import android.syncml.pim.PropertyNode; -import android.syncml.pim.VDataBuilder; -import android.syncml.pim.VNode; -import android.syncml.pim.vcard.VCardException; -import android.syncml.pim.vcard.VCardParser_V21; -import android.syncml.pim.vcard.VCardParser_V30; +import android.pim.vcard.ContactStruct; +import android.pim.vcard.EntryHandler; +import android.pim.vcard.VCardBuilder; +import android.pim.vcard.VCardBuilderCollection; +import android.pim.vcard.VCardConfig; +import android.pim.vcard.VCardDataBuilder; +import android.pim.vcard.VCardParser; +import android.pim.vcard.VCardParser_V21; +import android.pim.vcard.VCardParser_V30; +import android.pim.vcard.exception.VCardException; import android.test.AndroidTestCase; +import com.android.unit_tests.R; + import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; -import java.util.Vector; +import java.util.List; +import java.util.Map; public class VCardTests extends AndroidTestCase { + // TODO: Use EntityIterator, which is added in Eclair. + private static class EntryHolder implements EntryHandler { + public List<ContactStruct> contacts = new ArrayList<ContactStruct>(); + public void onEntryCreated(ContactStruct contactStruct) { + contacts.add(contactStruct); + } + public void onFinal() { + } + } + + static void verify(ContactStruct expected, ContactStruct actual) { + if (!equalsString(expected.getName(), actual.getName())) { + fail(String.format("Names do not equal: \"%s\" != \"%s\"", + expected.getName(), actual.getName())); + } + if (!equalsString( + expected.getPhoneticName(), actual.getPhoneticName())) { + fail(String.format("Phonetic names do not equal: \"%s\" != \"%s\"", + expected.getPhoneticName(), actual.getPhoneticName())); + } + { + final byte[] expectedPhotoBytes = expected.getPhotoBytes(); + final byte[] actualPhotoBytes = actual.getPhotoBytes(); + if (!((expectedPhotoBytes == null && actualPhotoBytes == null) || + Arrays.equals(expectedPhotoBytes, actualPhotoBytes))) { + fail("photoBytes is not equal."); + } + } + verifyInternal(expected.getNotes(), actual.getNotes(), "notes"); + verifyInternal(expected.getPhoneList(), actual.getPhoneList(), "phones"); + verifyInternal(expected.getContactMethodList(), actual.getContactMethodList(), + "contact lists"); + verifyInternal(expected.getOrganizationList(), actual.getOrganizationList(), + "organizations"); + { + final Map<String, List<String>> expectedMap = + expected.getExtensionMap(); + final Map<String, List<String>> actualMap = + actual.getExtensionMap(); + if (verifySize((expectedMap == null ? 0 : expectedMap.size()), + (actualMap == null ? 0 : actualMap.size()), "extensions") > 0) { + for (String key : expectedMap.keySet()) { + if (!actualMap.containsKey(key)) { + fail(String.format( + "Actual does not have %s extension while expected has", + key)); + } + final List<String> expectedList = expectedMap.get(key); + final List<String> actualList = actualMap.get(key); + verifyInternal(expectedList, actualList, + String.format("extension \"%s\"", key)); + } + } + } + } + + private static boolean equalsString(String a, String b) { + if (a == null || a.length() == 0) { + return b == null || b.length() == 0; + } else { + return a.equals(b); + } + } + + private static int verifySize(int expectedSize, int actualSize, String name) { + if (expectedSize != actualSize) { + fail(String.format("Size of %s is different: %d != %d", + name, expectedSize, actualSize)); + } + return expectedSize; + } + + private static <T> void verifyInternal(final List<T> expected, final List<T> actual, + String name) { + if(verifySize((expected == null ? 0 : expected.size()), + (actual == null ? 0 : actual.size()), name) > 0) { + int size = expected.size(); + for (int i = 0; i < size; i++) { + final T expectedObj = expected.get(i); + final T actualObj = actual.get(i); + if (!expected.equals(actual)) { + fail(String.format("The %i %s are different: %s != %s", + i, name, expectedObj, actualObj)); + } + } + } + } + private class PropertyNodesVerifier { - private HashMap<String, Vector<PropertyNode>> mPropertyNodeMap; + private HashMap<String, ArrayList<PropertyNode>> mPropertyNodeMap; public PropertyNodesVerifier(PropertyNode... nodes) { - mPropertyNodeMap = new HashMap<String, Vector<PropertyNode>>(); + mPropertyNodeMap = new HashMap<String, ArrayList<PropertyNode>>(); for (PropertyNode propertyNode : nodes) { String propName = propertyNode.propName; - Vector<PropertyNode> expectedNodes = + ArrayList<PropertyNode> expectedNodes = mPropertyNodeMap.get(propName); if (expectedNodes == null) { - expectedNodes = new Vector<PropertyNode>(); + expectedNodes = new ArrayList<PropertyNode>(); mPropertyNodeMap.put(propName, expectedNodes); } expectedNodes.add(propertyNode); @@ -53,7 +149,7 @@ public class VCardTests extends AndroidTestCase { public void verify(VNode vnode) { for (PropertyNode propertyNode : vnode.propList) { String propName = propertyNode.propName; - Vector<PropertyNode> nodes = mPropertyNodeMap.get(propName); + ArrayList<PropertyNode> nodes = mPropertyNodeMap.get(propName); if (nodes == null) { fail("Unexpected propName \"" + propName + "\" exists."); } @@ -81,8 +177,8 @@ public class VCardTests extends AndroidTestCase { } } if (mPropertyNodeMap.size() != 0) { - Vector<String> expectedProps = new Vector<String>(); - for (Vector<PropertyNode> nodes : mPropertyNodeMap.values()) { + ArrayList<String> expectedProps = new ArrayList<String>(); + for (ArrayList<PropertyNode> nodes : mPropertyNodeMap.values()) { for (PropertyNode node : nodes) { expectedProps.add(node.propName); } @@ -93,25 +189,82 @@ public class VCardTests extends AndroidTestCase { } } - public void testV21SimpleCase() throws IOException, VCardException { - VCardParser_V21 parser = new VCardParser_V21(); - VDataBuilder builder = new VDataBuilder(); - InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple); + public void testV21SimpleCase1_1() throws IOException, VCardException { + VCardParser parser = new VCardParser_V21(); + VCardDataBuilder builder = new VCardDataBuilder(VCardConfig.NAME_ORDER_TYPE_ENGLISH); + EntryHolder holder = new EntryHolder(); + builder.addEntryHandler(holder); + InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple_1); assertEquals(true, parser.parse(is,"ISO-8859-1", builder)); is.close(); - assertEquals(1, builder.vNodeList.size()); + assertEquals(1, holder.contacts.size()); + verify(new ContactStruct("Roid Ando", null, + null, null, null, null, null, null), + holder.contacts.get(0)); + } + + public void testV21SimpleCase1_2() throws IOException, VCardException { + VCardParser parser = new VCardParser_V21(); + VCardDataBuilder builder = new VCardDataBuilder(VCardConfig.NAME_ORDER_TYPE_JAPANESE); + EntryHolder holder = new EntryHolder(); + builder.addEntryHandler(holder); + InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple_1); + assertEquals(true, parser.parse(is,"ISO-8859-1", builder)); + is.close(); + assertEquals(1, holder.contacts.size()); + verify(new ContactStruct("Ando Roid", null, + null, null, null, null, null, null), + holder.contacts.get(0)); + } + + public void testV21SimpleCase2() throws IOException, VCardException { + VCardParser parser = new VCardParser_V21(); + VCardDataBuilder builder = new VCardDataBuilder(VCardConfig.NAME_ORDER_TYPE_ENGLISH); + EntryHolder holder = new EntryHolder(); + builder.addEntryHandler(holder); + InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple_2); + assertEquals(true, parser.parse(is,"ISO-8859-1", builder)); + is.close(); + assertEquals(1, holder.contacts.size()); + verify(new ContactStruct("Ando Roid", null, + null, null, null, null, null, null), + holder.contacts.get(0)); + } + + public void testV21SimpleCase3() throws IOException, VCardException { + VCardParser parser = new VCardParser_V21(); + VCardDataBuilder builder1 = new VCardDataBuilder(VCardConfig.NAME_ORDER_TYPE_ENGLISH); + EntryHolder holder = new EntryHolder(); + builder1.addEntryHandler(holder); + VNodeBuilder builder2 = new VNodeBuilder(); + VCardBuilderCollection collection = + new VCardBuilderCollection( + new ArrayList<VCardBuilder>(Arrays.asList(builder1, builder2))); + InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple_3); + assertEquals(true, parser.parse(is,"ISO-8859-1", collection)); + is.close(); + + assertEquals(1, builder2.vNodeList.size()); + VNode vnode = builder2.vNodeList.get(0); PropertyNodesVerifier verifier = new PropertyNodesVerifier( new PropertyNode("N", "Ando;Roid;", Arrays.asList("Ando", "Roid", ""), null, null, null, null), new PropertyNode("FN", "Ando Roid", null, null, null, null, null)); - verifier.verify(builder.vNodeList.get(0)); + verifier.verify(vnode); + + // FN is prefered. + assertEquals(1, holder.contacts.size()); + ContactStruct actual = holder.contacts.get(0); + verify(new ContactStruct("Ando Roid", null, + null, null, null, null, null, null), + actual); } - + public void testV21BackslashCase() throws IOException, VCardException { VCardParser_V21 parser = new VCardParser_V21(); - VDataBuilder builder = new VDataBuilder(); + VNodeBuilder builder = new VNodeBuilder(); InputStream is = getContext().getResources().openRawResource(R.raw.v21_backslash); assertEquals(true, parser.parse(is,"ISO-8859-1", builder)); is.close(); @@ -129,7 +282,7 @@ public class VCardTests extends AndroidTestCase { public void testV21ComplicatedCase() throws IOException, VCardException { VCardParser_V21 parser = new VCardParser_V21(); - VDataBuilder builder = new VDataBuilder(); + VNodeBuilder builder = new VNodeBuilder(); InputStream is = getContext().getResources().openRawResource(R.raw.v21_complicated); assertEquals(true, parser.parse(is,"ISO-8859-1", builder)); is.close(); @@ -585,7 +738,7 @@ public class VCardTests extends AndroidTestCase { public void testV21Japanese1() throws IOException, VCardException { VCardParser_V21 parser = new VCardParser_V21(); - VDataBuilder builder = new VDataBuilder(); + VNodeBuilder builder = new VNodeBuilder(); InputStream is = getContext().getResources().openRawResource(R.raw.v21_japanese_1); assertEquals(true, parser.parse(is,"ISO-8859-1", builder)); is.close(); @@ -616,7 +769,7 @@ public class VCardTests extends AndroidTestCase { public void testV21Japanese2() throws IOException, VCardException { VCardParser_V21 parser = new VCardParser_V21(); - VDataBuilder builder = new VDataBuilder(); + VNodeBuilder builder = new VNodeBuilder(); InputStream is = getContext().getResources().openRawResource(R.raw.v21_japanese_2); assertEquals(true, parser.parse(is,"ISO-8859-1", builder)); is.close(); @@ -660,7 +813,7 @@ public class VCardTests extends AndroidTestCase { public void testV21MultipleEntryCase() throws IOException, VCardException { VCardParser_V21 parser = new VCardParser_V21(); - VDataBuilder builder = new VDataBuilder(); + VNodeBuilder builder = new VNodeBuilder(); InputStream is = getContext().getResources().openRawResource(R.raw.v21_multiple_entry); assertEquals(true, parser.parse(is,"ISO-8859-1", builder)); is.close(); @@ -741,7 +894,7 @@ public class VCardTests extends AndroidTestCase { public void testV30SimpleCase() throws IOException, VCardException { VCardParser_V21 parser = new VCardParser_V30(); - VDataBuilder builder = new VDataBuilder(); + VNodeBuilder builder = new VNodeBuilder(); InputStream is = getContext().getResources().openRawResource(R.raw.v30_simple); assertEquals(true, parser.parse(is,"ISO-8859-1", builder)); is.close(); diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java new file mode 100644 index 000000000000..3eb827b60710 --- /dev/null +++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java @@ -0,0 +1,30 @@ +/* + * 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.android.unit_tests.vcard; + +import java.util.ArrayList; + +/** + * @hide old class. Just for testing + */ +public class VNode { + public String VName; + + public ArrayList<PropertyNode> propList = new ArrayList<PropertyNode>(); + + /** 0:parse over. 1:parsing. */ + public int parseStatus = 1; +} diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java new file mode 100644 index 000000000000..6d692237489a --- /dev/null +++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java @@ -0,0 +1,313 @@ +/* + * 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.android.unit_tests.vcard; + +import android.content.ContentValues; +import android.pim.vcard.VCardBuilder; +import android.pim.vcard.VCardConfig; +import android.util.CharsetUtils; +import android.util.Log; + +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.net.QuotedPrintableCodec; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +/** + * Store the parse result to custom datastruct: VNode, PropertyNode + * Maybe several vcard instance, so use vNodeList to store. + * VNode: standy by a vcard instance. + * PropertyNode: standy by a property line of a card. + * @hide old class, just for testing use + */ +public class VNodeBuilder implements VCardBuilder { + static private String LOG_TAG = "VDATABuilder"; + + /** + * If there's no other information available, this class uses this charset for encoding + * byte arrays. + */ + static public String TARGET_CHARSET = "UTF-8"; + + /** type=VNode */ + public List<VNode> vNodeList = new ArrayList<VNode>(); + private int mNodeListPos = 0; + private VNode mCurrentVNode; + private PropertyNode mCurrentPropNode; + private String mCurrentParamType; + + /** + * The charset using which VParser parses the text. + */ + private String mSourceCharset; + + /** + * The charset with which byte array is encoded to String. + */ + private String mTargetCharset; + + private boolean mStrictLineBreakParsing; + + public VNodeBuilder() { + this(VCardConfig.DEFAULT_CHARSET, TARGET_CHARSET, false); + } + + public VNodeBuilder(String charset, boolean strictLineBreakParsing) { + this(null, charset, strictLineBreakParsing); + } + + /** + * @hide sourceCharset is temporal. + */ + public VNodeBuilder(String sourceCharset, String targetCharset, + boolean strictLineBreakParsing) { + if (sourceCharset != null) { + mSourceCharset = sourceCharset; + } else { + mSourceCharset = VCardConfig.DEFAULT_CHARSET; + } + if (targetCharset != null) { + mTargetCharset = targetCharset; + } else { + mTargetCharset = TARGET_CHARSET; + } + mStrictLineBreakParsing = strictLineBreakParsing; + } + + public void start() { + } + + public void end() { + } + + // Note: I guess that this code assumes the Record may nest like this: + // START:VPOS + // ... + // START:VPOS2 + // ... + // END:VPOS2 + // ... + // END:VPOS + // + // However the following code has a bug. + // When error occurs after calling startRecord(), the entry which is probably + // the cause of the error remains to be in vNodeList, while endRecord() is not called. + // + // I leave this code as is since I'm not familiar with vcalendar specification. + // But I believe we should refactor this code in the future. + // Until this, the last entry has to be removed when some error occurs. + public void startRecord(String type) { + + VNode vnode = new VNode(); + vnode.parseStatus = 1; + vnode.VName = type; + // I feel this should be done in endRecord(), but it cannot be done because of + // the reason above. + vNodeList.add(vnode); + mNodeListPos = vNodeList.size() - 1; + mCurrentVNode = vNodeList.get(mNodeListPos); + } + + public void endRecord() { + VNode endNode = vNodeList.get(mNodeListPos); + endNode.parseStatus = 0; + while(mNodeListPos > 0){ + mNodeListPos--; + if((vNodeList.get(mNodeListPos)).parseStatus == 1) + break; + } + mCurrentVNode = vNodeList.get(mNodeListPos); + } + + public void startProperty() { + mCurrentPropNode = new PropertyNode(); + } + + public void endProperty() { + mCurrentVNode.propList.add(mCurrentPropNode); + } + + public void propertyName(String name) { + mCurrentPropNode.propName = name; + } + + // Used only in VCard. + public void propertyGroup(String group) { + mCurrentPropNode.propGroupSet.add(group); + } + + public void propertyParamType(String type) { + mCurrentParamType = type; + } + + public void propertyParamValue(String value) { + if (mCurrentParamType == null || + mCurrentParamType.equalsIgnoreCase("TYPE")) { + mCurrentPropNode.paramMap_TYPE.add(value); + } else { + mCurrentPropNode.paramMap.put(mCurrentParamType, value); + } + + mCurrentParamType = null; + } + + private String encodeString(String originalString, String targetCharset) { + if (mSourceCharset.equalsIgnoreCase(targetCharset)) { + return originalString; + } + Charset charset = Charset.forName(mSourceCharset); + ByteBuffer byteBuffer = charset.encode(originalString); + // byteBuffer.array() "may" return byte array which is larger than + // byteBuffer.remaining(). Here, we keep on the safe side. + byte[] bytes = new byte[byteBuffer.remaining()]; + byteBuffer.get(bytes); + try { + return new String(bytes, targetCharset); + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); + return null; + } + } + + private String handleOneValue(String value, String targetCharset, String encoding) { + if (encoding != null) { + if (encoding.equals("BASE64") || encoding.equals("B")) { + // Assume BASE64 is used only when the number of values is 1. + mCurrentPropNode.propValue_bytes = + Base64.decodeBase64(value.getBytes()); + return value; + } else if (encoding.equals("QUOTED-PRINTABLE")) { + String quotedPrintable = value + .replaceAll("= ", " ").replaceAll("=\t", "\t"); + String[] lines; + if (mStrictLineBreakParsing) { + lines = quotedPrintable.split("\r\n"); + } else { + StringBuilder builder = new StringBuilder(); + int length = quotedPrintable.length(); + ArrayList<String> list = new ArrayList<String>(); + for (int i = 0; i < length; i++) { + char ch = quotedPrintable.charAt(i); + if (ch == '\n') { + list.add(builder.toString()); + builder = new StringBuilder(); + } else if (ch == '\r') { + list.add(builder.toString()); + builder = new StringBuilder(); + if (i < length - 1) { + char nextCh = quotedPrintable.charAt(i + 1); + if (nextCh == '\n') { + i++; + } + } + } else { + builder.append(ch); + } + } + String finalLine = builder.toString(); + if (finalLine.length() > 0) { + list.add(finalLine); + } + lines = list.toArray(new String[0]); + } + StringBuilder builder = new StringBuilder(); + for (String line : lines) { + if (line.endsWith("=")) { + line = line.substring(0, line.length() - 1); + } + builder.append(line); + } + byte[] bytes; + try { + bytes = builder.toString().getBytes(mSourceCharset); + } catch (UnsupportedEncodingException e1) { + Log.e(LOG_TAG, "Failed to encode: charset=" + mSourceCharset); + bytes = builder.toString().getBytes(); + } + + try { + bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes); + } catch (DecoderException e) { + Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e); + return ""; + } + + try { + return new String(bytes, targetCharset); + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); + return new String(bytes); + } + } + // Unknown encoding. Fall back to default. + } + return encodeString(value, targetCharset); + } + + public void propertyValues(List<String> values) { + if (values == null || values.size() == 0) { + mCurrentPropNode.propValue_bytes = null; + mCurrentPropNode.propValue_vector.clear(); + mCurrentPropNode.propValue_vector.add(""); + mCurrentPropNode.propValue = ""; + return; + } + + ContentValues paramMap = mCurrentPropNode.paramMap; + + String targetCharset = CharsetUtils.nameForDefaultVendor(paramMap.getAsString("CHARSET")); + String encoding = paramMap.getAsString("ENCODING"); + + if (targetCharset == null || targetCharset.length() == 0) { + targetCharset = mTargetCharset; + } + + for (String value : values) { + mCurrentPropNode.propValue_vector.add( + handleOneValue(value, targetCharset, encoding)); + } + + mCurrentPropNode.propValue = listToString(mCurrentPropNode.propValue_vector); + } + + private String listToString(List<String> list){ + int size = list.size(); + if (size > 1) { + StringBuilder typeListB = new StringBuilder(); + for (String type : list) { + typeListB.append(type).append(";"); + } + int len = typeListB.length(); + if (len > 0 && typeListB.charAt(len - 1) == ';') { + return typeListB.substring(0, len - 1); + } + return typeListB.toString(); + } else if (size == 1) { + return list.get(0); + } else { + return ""; + } + } + + public String getResult(){ + return null; + } +} diff --git a/tests/DpiTest/AndroidManifest.xml b/tests/DpiTest/AndroidManifest.xml index 64ad7beeda34..68ecc6e89934 100644 --- a/tests/DpiTest/AndroidManifest.xml +++ b/tests/DpiTest/AndroidManifest.xml @@ -19,10 +19,15 @@ <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="3" /> <supports-screens android:smallScreens="true" /> <application android:label="DpiTest"> - <activity android:name="DpiTestActivity" android:label="DpiTest"> + <activity android:name="DpiTestActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity android:name="DpiTestNoCompatActivity" android:label="DpiTestNoCompat"> <intent-filter> <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> diff --git a/tests/DpiTest/res/drawable-nodpi/logonodpi120.png b/tests/DpiTest/res/drawable-nodpi/logonodpi120.png Binary files differnew file mode 100644 index 000000000000..46bbd5bd3d48 --- /dev/null +++ b/tests/DpiTest/res/drawable-nodpi/logonodpi120.png diff --git a/tests/DpiTest/res/drawable-nodpi/logonodpi160.png b/tests/DpiTest/res/drawable-nodpi/logonodpi160.png Binary files differnew file mode 100644 index 000000000000..c23b2ce83b43 --- /dev/null +++ b/tests/DpiTest/res/drawable-nodpi/logonodpi160.png diff --git a/tests/DpiTest/res/drawable-nodpi/logonodpi240.png b/tests/DpiTest/res/drawable-nodpi/logonodpi240.png Binary files differnew file mode 100644 index 000000000000..4d717a86143d --- /dev/null +++ b/tests/DpiTest/res/drawable-nodpi/logonodpi240.png diff --git a/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java index 5a9f3f548c7d..8c69305d5760 100644 --- a/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java +++ b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java @@ -17,6 +17,8 @@ package com.google.android.test.dpi; import android.app.Activity; +import android.app.ActivityThread; +import android.app.Application; import android.os.Bundle; import android.graphics.BitmapFactory; import android.graphics.Bitmap; @@ -28,8 +30,42 @@ import android.widget.TextView; import android.widget.ScrollView; import android.view.View; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.CompatibilityInfo; +import android.util.DisplayMetrics; public class DpiTestActivity extends Activity { + public DpiTestActivity() { + super(); + init(false); + } + + public DpiTestActivity(boolean noCompat) { + super(); + init(noCompat); + } + + public void init(boolean noCompat) { + try { + // This is all a dirty hack. Don't think a real application should + // be doing it. + Application app = ActivityThread.currentActivityThread().getApplication(); + ApplicationInfo ai = app.getPackageManager().getApplicationInfo( + "com.google.android.test.dpi", + PackageManager.GET_SUPPORTS_DENSITIES); + if (noCompat) { + ai.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS + | ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS + | ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS; + ai.supportsDensities = new int[] { ApplicationInfo.ANY_DENSITY }; + app.getResources().setCompatibilityInfo(new CompatibilityInfo(ai)); + } + } catch (PackageManager.NameNotFoundException e) { + throw new RuntimeException("ouch", e); + } + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -73,6 +109,13 @@ public class DpiTestActivity extends Activity { addLabelToRoot(root, "Autoscaled bitmap"); addChildToRoot(root, layout); + layout = new LinearLayout(this); + addResourceDrawable(layout, R.drawable.logonodpi120); + addResourceDrawable(layout, R.drawable.logonodpi160); + addResourceDrawable(layout, R.drawable.logonodpi240); + addLabelToRoot(root, "No-dpi resource drawable"); + addChildToRoot(root, layout); + setContentView(scrollWrap(root)); } @@ -155,7 +198,10 @@ public class DpiTestActivity extends Activity { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - setMeasuredDimension(mBitmap.getScaledWidth(), mBitmap.getScaledHeight()); + final DisplayMetrics metrics = getResources().getDisplayMetrics(); + setMeasuredDimension( + mBitmap.getScaledWidth(metrics), + mBitmap.getScaledHeight(metrics)); } @Override diff --git a/tests/DpiTest/src/com/google/android/test/dpi/DpiTestNoCompatActivity.java b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestNoCompatActivity.java new file mode 100644 index 000000000000..4d25e083e039 --- /dev/null +++ b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestNoCompatActivity.java @@ -0,0 +1,23 @@ +/* + * 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.google.android.test.dpi; + +public class DpiTestNoCompatActivity extends DpiTestActivity { + public DpiTestNoCompatActivity() { + super(true); + } +} diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java index 39bbf16b73b2..ba461973fbfb 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java @@ -73,6 +73,10 @@ public class LoadTestsAutoTest extends ActivityInstrumentationTestCase2<TestShel runTestAndWaitUntilDone(activity, runner.mTestPath, runner.mTimeoutInMillis); activity.clearCache(); + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + } dumpMemoryInfo(); // Kill activity diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java index 90f03dd586b9..48c1e5dba4f4 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java @@ -91,7 +91,7 @@ public class TestShellActivity extends Activity implements LayoutTestController } public void clearCache() { - mWebView.clearCache(true); + mWebView.freeMemory(); } @Override diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp index 67af116d372d..14cad2f0b8b9 100644 --- a/tools/aapt/AaptAssets.cpp +++ b/tools/aapt/AaptAssets.cpp @@ -654,9 +654,15 @@ bool AaptGroupEntry::getDensityName(const char* name, ResTable_config* out) { if (strcmp(name, kWildcardName) == 0) { - if (out) out->density = 0; + if (out) out->density = ResTable_config::DENSITY_DEFAULT; return true; } + + if (strcmp(name, "nodpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_NONE; + return true; + } + char* c = (char*)name; while (*c >= '0' && *c <= '9') { c++; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index d0a1c46bf644..fd77d5143d9e 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -286,8 +286,8 @@ public final class Bridge implements ILayoutBridge { } return computeLayout(layoutDescription, projectKey, - screenWidth, screenHeight, DisplayMetrics.DEFAULT_DENSITY, - DisplayMetrics.DEFAULT_DENSITY, DisplayMetrics.DEFAULT_DENSITY, + screenWidth, screenHeight, DisplayMetrics.DENSITY_DEFAULT, + DisplayMetrics.DENSITY_DEFAULT, DisplayMetrics.DENSITY_DEFAULT, themeName, isProjectTheme, projectResources, frameworkResources, customViewLoader, logger); } @@ -304,8 +304,8 @@ public final class Bridge implements ILayoutBridge { Map<String, Map<String, IResourceValue>> frameworkResources, IProjectCallback customViewLoader, ILayoutLog logger) { return computeLayout(layoutDescription, projectKey, - screenWidth, screenHeight, DisplayMetrics.DEFAULT_DENSITY, - DisplayMetrics.DEFAULT_DENSITY, DisplayMetrics.DEFAULT_DENSITY, + screenWidth, screenHeight, DisplayMetrics.DENSITY_DEFAULT, + DisplayMetrics.DENSITY_DEFAULT, DisplayMetrics.DENSITY_DEFAULT, themeName, isProjectTheme, projectResources, frameworkResources, customViewLoader, logger); } @@ -340,7 +340,7 @@ public final class Bridge implements ILayoutBridge { try { // setup the display Metrics. DisplayMetrics metrics = new DisplayMetrics(); - metrics.density = density / (float) DisplayMetrics.DEFAULT_DENSITY; + metrics.density = density / (float) DisplayMetrics.DENSITY_DEFAULT; metrics.scaledDensity = metrics.density; metrics.widthPixels = screenWidth; metrics.heightPixels = screenHeight; |