am b34d5d52: Use the TLS register if HAVE_ARM_TLS_REGISTER is defined
Merge commit 'b34d5d527596102ae7a040d787e4d8c336bf192b' into eclair-mr2
* commit 'b34d5d527596102ae7a040d787e4d8c336bf192b':
Use the TLS register if HAVE_ARM_TLS_REGISTER is defined
diff --git a/Android.mk b/Android.mk
index 49ee63d..d153ce6 100644
--- a/Android.mk
+++ b/Android.mk
@@ -108,6 +108,7 @@
core/java/android/hardware/ISensorService.aidl \
core/java/android/net/IConnectivityManager.aidl \
core/java/android/os/ICheckinService.aidl \
+ core/java/android/os/IDropBox.aidl \
core/java/android/os/IHardwareService.aidl \
core/java/android/os/IMessenger.aidl \
core/java/android/os/IMountService.aidl \
@@ -216,6 +217,7 @@
frameworks/base/core/java/android/appwidget/AppWidgetProviderInfo.aidl \
frameworks/base/core/java/android/net/Uri.aidl \
frameworks/base/core/java/android/os/Bundle.aidl \
+ frameworks/base/core/java/android/os/DropBoxEntry.aidl \
frameworks/base/core/java/android/os/ParcelFileDescriptor.aidl \
frameworks/base/core/java/android/os/ParcelUuid.aidl \
frameworks/base/core/java/android/view/KeyEvent.aidl \
diff --git a/api/current.xml b/api/current.xml
index 6148047..290bd80 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -118824,6 +118824,23 @@
<parameter name="origId" type="long">
</parameter>
</method>
+<method name="cancelThumbnailRequest"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cr" type="android.content.ContentResolver">
+</parameter>
+<parameter name="origId" type="long">
+</parameter>
+<parameter name="groupId" type="long">
+</parameter>
+</method>
<method name="getContentUri"
return="android.net.Uri"
abstract="false"
@@ -118856,6 +118873,27 @@
<parameter name="options" type="android.graphics.BitmapFactory.Options">
</parameter>
</method>
+<method name="getThumbnail"
+ return="android.graphics.Bitmap"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cr" type="android.content.ContentResolver">
+</parameter>
+<parameter name="origId" type="long">
+</parameter>
+<parameter name="groupId" type="long">
+</parameter>
+<parameter name="kind" type="int">
+</parameter>
+<parameter name="options" type="android.graphics.BitmapFactory.Options">
+</parameter>
+</method>
<method name="query"
return="android.database.Cursor"
abstract="false"
@@ -119281,6 +119319,23 @@
<parameter name="origId" type="long">
</parameter>
</method>
+<method name="cancelThumbnailRequest"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cr" type="android.content.ContentResolver">
+</parameter>
+<parameter name="origId" type="long">
+</parameter>
+<parameter name="groupId" type="long">
+</parameter>
+</method>
<method name="getContentUri"
return="android.net.Uri"
abstract="false"
@@ -119313,6 +119368,27 @@
<parameter name="options" type="android.graphics.BitmapFactory.Options">
</parameter>
</method>
+<method name="getThumbnail"
+ return="android.graphics.Bitmap"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cr" type="android.content.ContentResolver">
+</parameter>
+<parameter name="origId" type="long">
+</parameter>
+<parameter name="groupId" type="long">
+</parameter>
+<parameter name="kind" type="int">
+</parameter>
+<parameter name="options" type="android.graphics.BitmapFactory.Options">
+</parameter>
+</method>
<field name="DATA"
type="java.lang.String"
transient="false"
@@ -143705,6 +143781,29 @@
</parameter>
</method>
</interface>
+<interface name="LeadingMarginSpan.LeadingMarginSpan2"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.text.style.LeadingMarginSpan">
+</implements>
+<implements name="android.text.style.WrapTogetherSpan">
+</implements>
+<method name="getLeadingMarginLineCount"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</interface>
<class name="LeadingMarginSpan.Standard"
extends="java.lang.Object"
abstract="false"
diff --git a/cmds/stagefright/SineSource.cpp b/cmds/stagefright/SineSource.cpp
index e5a6ccb..e272a65 100644
--- a/cmds/stagefright/SineSource.cpp
+++ b/cmds/stagefright/SineSource.cpp
@@ -86,8 +86,8 @@
x += k;
}
- buffer->meta_data()->setInt32(kKeyTimeUnits, mPhase);
- buffer->meta_data()->setInt32(kKeyTimeScale, mSampleRate);
+ buffer->meta_data()->setInt64(
+ kKeyTime, ((int64_t)mPhase * 1000000) / mSampleRate);
mPhase += numFramesPerBuffer;
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index f8bb3c8..3b7cece 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -52,12 +52,8 @@
static void playSource(OMXClient *client, const sp<MediaSource> &source) {
sp<MetaData> meta = source->getFormat();
- int32_t durationUnits;
- int32_t timeScale;
- CHECK(meta->findInt32(kKeyDuration, &durationUnits));
- CHECK(meta->findInt32(kKeyTimeScale, &timeScale));
-
- int64_t durationUs = ((int64_t)durationUnits * 1000000) / timeScale;
+ int64_t durationUs;
+ CHECK(meta->findInt64(kKeyDuration, &durationUs));
sp<OMXCodec> decoder = OMXCodec::Create(
client->interface(), meta, false /* createEncoder */, source);
@@ -78,20 +74,24 @@
options.clearSeekTo();
bool shouldSeek = false;
- if (err != OK) {
+ if (err == INFO_FORMAT_CHANGED) {
+ CHECK_EQ(buffer, NULL);
+
+ printf("format changed.\n");
+ continue;
+ } else if (err != OK) {
printf("reached EOF.\n");
shouldSeek = true;
} else {
- int32_t timestampUnits;
- CHECK(buffer->meta_data()->findInt32(kKeyTimeUnits, ×tampUnits));
-
- int64_t timestampUs = ((int64_t)timestampUnits * 1000000) / timeScale;
+ int64_t timestampUs;
+ CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs));
bool failed = false;
if (seekTimeUs >= 0) {
int64_t diff = timestampUs - seekTimeUs;
+
if (diff < 0) {
diff = -diff;
}
@@ -157,6 +157,11 @@
if (err != OK) {
CHECK_EQ(buffer, NULL);
+ if (err == INFO_FORMAT_CHANGED) {
+ printf("format changed.\n");
+ continue;
+ }
+
break;
}
@@ -283,7 +288,7 @@
CHECK(service.get() != NULL);
- sp<IOMX> omx = service->createOMX();
+ sp<IOMX> omx = service->getOMX();
CHECK(omx.get() != NULL);
const char *kMimeTypes[] = {
@@ -329,11 +334,11 @@
CHECK(service.get() != NULL);
- sp<IOMX> omx = service->createOMX();
+ sp<IOMX> omx = service->getOMX();
CHECK(omx.get() != NULL);
List<String8> list;
- omx->list_nodes(&list);
+ omx->listNodes(&list);
for (List<String8>::iterator it = list.begin();
it != list.end(); ++it) {
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 3a11cb3..be74073 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -21,6 +21,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.RegisteredServicesCache;
import android.content.pm.PackageInfo;
@@ -90,11 +92,8 @@
// Messages that can be sent on mHandler
private static final int MESSAGE_TIMED_OUT = 3;
- private static final int MESSAGE_CONNECTED = 7;
- private static final int MESSAGE_DISCONNECTED = 8;
private final AccountAuthenticatorCache mAuthenticatorCache;
- private final AuthenticatorBindHelper mBindHelper;
private final DatabaseHelper mOpenHelper;
private final SimWatcher mSimWatcher;
@@ -220,10 +219,12 @@
mAuthenticatorCache = new AccountAuthenticatorCache(mContext);
mAuthenticatorCache.setListener(this);
- mBindHelper = new AuthenticatorBindHelper(mContext, mAuthenticatorCache, mMessageHandler,
- MESSAGE_CONNECTED, MESSAGE_DISCONNECTED);
- mSimWatcher = new SimWatcher(mContext);
+ if (SystemProperties.getBoolean("ro.config.sim_password_clear", false)) {
+ mSimWatcher = new SimWatcher(mContext);
+ } else {
+ mSimWatcher = null;
+ }
sThis.set(this);
onRegisteredServicesCacheChanged();
@@ -1068,7 +1069,7 @@
}
private abstract class Session extends IAccountAuthenticatorResponse.Stub
- implements AuthenticatorBindHelper.Callback, IBinder.DeathRecipient {
+ implements IBinder.DeathRecipient, ServiceConnection {
IAccountManagerResponse mResponse;
final String mAccountType;
final boolean mExpectActivityLaunch;
@@ -1150,7 +1151,7 @@
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
}
- if (!mBindHelper.bind(mAccountType, this)) {
+ if (!bindToAuthenticator(mAccountType)) {
Log.d(TAG, "bind attempt failed for " + toDebugString());
onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
}
@@ -1159,7 +1160,7 @@
private void unbind() {
if (mAuthenticator != null) {
mAuthenticator = null;
- mBindHelper.unbind(this);
+ mContext.unbindService(this);
}
}
@@ -1172,7 +1173,7 @@
mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this);
}
- public void onConnected(IBinder service) {
+ public void onServiceConnected(ComponentName name, IBinder service) {
mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
try {
run();
@@ -1182,9 +1183,7 @@
}
}
- public abstract void run() throws RemoteException;
-
- public void onDisconnected() {
+ public void onServiceDisconnected(ComponentName name) {
mAuthenticator = null;
IAccountManagerResponse response = getResponseAndClose();
if (response != null) {
@@ -1193,6 +1192,8 @@
}
}
+ public abstract void run() throws RemoteException;
+
public void onTimedOut() {
IAccountManagerResponse response = getResponseAndClose();
if (response != null) {
@@ -1262,6 +1263,39 @@
}
}
}
+
+ /**
+ * find the component name for the authenticator and initiate a bind
+ * if no authenticator or the bind fails then return false, otherwise return true
+ */
+ private boolean bindToAuthenticator(String authenticatorType) {
+ AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
+ mAuthenticatorCache.getServiceInfo(
+ AuthenticatorDescription.newKey(authenticatorType));
+ if (authenticatorInfo == null) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "there is no authenticator for " + authenticatorType
+ + ", bailing out");
+ }
+ return false;
+ }
+
+ Intent intent = new Intent();
+ intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
+ intent.setComponent(authenticatorInfo.componentName);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
+ }
+ if (!mContext.bindService(intent, this, Context.BIND_AUTO_CREATE)) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
+ }
+ return false;
+ }
+
+
+ return true;
+ }
}
private class MessageHandler extends Handler {
@@ -1270,9 +1304,6 @@
}
public void handleMessage(Message msg) {
- if (mBindHelper.handleMessage(msg)) {
- return;
- }
switch (msg.what) {
case MESSAGE_TIMED_OUT:
Session session = (Session)msg.obj;
@@ -1567,13 +1598,14 @@
private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) {
final boolean fromAuthenticator = hasAuthenticatorUid(account.type, callerUid);
final boolean hasExplicitGrants = hasExplicitlyGrantedPermission(account, authTokenType);
+ final boolean inSystemImage = inSystemImage(callerUid);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
+ callerUid + ", account " + account
+ ": is authenticator? " + fromAuthenticator
+ ", has explicit permission? " + hasExplicitGrants);
}
- return fromAuthenticator || hasExplicitGrants || inSystemImage(callerUid);
+ return fromAuthenticator || hasExplicitGrants || inSystemImage;
}
private boolean hasAuthenticatorUid(String accountType, int callingUid) {
diff --git a/core/java/android/accounts/AuthenticatorBindHelper.java b/core/java/android/accounts/AuthenticatorBindHelper.java
deleted file mode 100644
index 2ca1f0e..0000000
--- a/core/java/android/accounts/AuthenticatorBindHelper.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.accounts;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Map;
-
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-
-/**
- * A helper object that simplifies binding to Account Authenticators. It uses the
- * {@link AccountAuthenticatorCache} to find the component name of the authenticators,
- * allowing the user to bind by account name. It also allows multiple, simultaneous binds
- * to the same authenticator, with each bind call guaranteed to return either
- * {@link Callback#onConnected} or {@link Callback#onDisconnected} if the bind() call
- * itself succeeds, even if the authenticator is already bound internally.
- * @hide
- */
-public class AuthenticatorBindHelper {
- private static final String TAG = "Accounts";
- private final Handler mHandler;
- private final Context mContext;
- private final int mMessageWhatConnected;
- private final int mMessageWhatDisconnected;
- private final Map<String, MyServiceConnection> mServiceConnections = Maps.newHashMap();
- private final Map<String, ArrayList<Callback>> mServiceUsers = Maps.newHashMap();
- private final AccountAuthenticatorCache mAuthenticatorCache;
-
- public AuthenticatorBindHelper(Context context,
- AccountAuthenticatorCache authenticatorCache, Handler handler,
- int messageWhatConnected, int messageWhatDisconnected) {
- mContext = context;
- mHandler = handler;
- mAuthenticatorCache = authenticatorCache;
- mMessageWhatConnected = messageWhatConnected;
- mMessageWhatDisconnected = messageWhatDisconnected;
- }
-
- public interface Callback {
- void onConnected(IBinder service);
- void onDisconnected();
- }
-
- public boolean bind(String authenticatorType, Callback callback) {
- // if the authenticator is connecting or connected then return true
- synchronized (mServiceConnections) {
- if (mServiceConnections.containsKey(authenticatorType)) {
- MyServiceConnection connection = mServiceConnections.get(authenticatorType);
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "service connection already exists for " + authenticatorType);
- }
- mServiceUsers.get(authenticatorType).add(callback);
- if (connection.mService != null) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "the service is connected, scheduling a connected message for "
- + authenticatorType);
- }
- connection.scheduleCallbackConnectedMessage(callback);
- } else {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "the service is *not* connected, waiting for for "
- + authenticatorType);
- }
- }
- return true;
- }
-
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "there is no service connection for " + authenticatorType);
- }
-
- // otherwise find the component name for the authenticator and initiate a bind
- // if no authenticator or the bind fails then return false, otherwise return true
- AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
- mAuthenticatorCache.getServiceInfo(
- AuthenticatorDescription.newKey(authenticatorType));
- if (authenticatorInfo == null) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "there is no authenticator for " + authenticatorType
- + ", bailing out");
- }
- return false;
- }
-
- MyServiceConnection connection = new MyServiceConnection(authenticatorType);
-
- Intent intent = new Intent();
- intent.setAction("android.accounts.AccountAuthenticator");
- intent.setComponent(authenticatorInfo.componentName);
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
- }
- if (!mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
- }
- return false;
- }
-
- mServiceConnections.put(authenticatorType, connection);
- mServiceUsers.put(authenticatorType, Lists.newArrayList(callback));
- return true;
- }
- }
-
- public void unbind(Callback callbackToUnbind) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "unbinding callback " + callbackToUnbind);
- }
- synchronized (mServiceConnections) {
- for (Map.Entry<String, ArrayList<Callback>> entry : mServiceUsers.entrySet()) {
- final String authenticatorType = entry.getKey();
- final ArrayList<Callback> serviceUsers = entry.getValue();
- for (Callback callback : serviceUsers) {
- if (callback == callbackToUnbind) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "found callback in service" + authenticatorType);
- }
- serviceUsers.remove(callbackToUnbind);
- if (serviceUsers.isEmpty()) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "there are no more callbacks for service "
- + authenticatorType + ", unbinding service");
- }
- unbindFromServiceLocked(authenticatorType);
- } else {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "leaving service " + authenticatorType
- + " around since there are still callbacks using it");
- }
- }
- return;
- }
- }
- }
- Log.e(TAG, "did not find callback " + callbackToUnbind + " in any of the services");
- }
- }
-
- /**
- * You must synchronized on mServiceConnections before calling this
- */
- private void unbindFromServiceLocked(String authenticatorType) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "unbindService from " + authenticatorType);
- }
- mContext.unbindService(mServiceConnections.get(authenticatorType));
- mServiceUsers.remove(authenticatorType);
- mServiceConnections.remove(authenticatorType);
- }
-
- private class ConnectedMessagePayload {
- public final IBinder mService;
- public final Callback mCallback;
- public ConnectedMessagePayload(IBinder service, Callback callback) {
- mService = service;
- mCallback = callback;
- }
- }
-
- private class MyServiceConnection implements ServiceConnection {
- private final String mAuthenticatorType;
- private IBinder mService = null;
-
- public MyServiceConnection(String authenticatorType) {
- mAuthenticatorType = authenticatorType;
- }
-
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "onServiceConnected for account type " + mAuthenticatorType);
- }
- // post a message for each service user to tell them that the service is connected
- synchronized (mServiceConnections) {
- mService = service;
- for (Callback callback : mServiceUsers.get(mAuthenticatorType)) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "the service became connected, scheduling a connected "
- + "message for " + mAuthenticatorType);
- }
- scheduleCallbackConnectedMessage(callback);
- }
- }
- }
-
- private void scheduleCallbackConnectedMessage(Callback callback) {
- final ConnectedMessagePayload payload =
- new ConnectedMessagePayload(mService, callback);
- mHandler.obtainMessage(mMessageWhatConnected, payload).sendToTarget();
- }
-
- public void onServiceDisconnected(ComponentName name) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "onServiceDisconnected for account type " + mAuthenticatorType);
- }
- // post a message for each service user to tell them that the service is disconnected,
- // and unbind from the service.
- synchronized (mServiceConnections) {
- final ArrayList<Callback> callbackList = mServiceUsers.get(mAuthenticatorType);
- if (callbackList != null) {
- for (Callback callback : callbackList) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "the service became disconnected, scheduling a "
- + "disconnected message for "
- + mAuthenticatorType);
- }
- mHandler.obtainMessage(mMessageWhatDisconnected, callback).sendToTarget();
- }
- unbindFromServiceLocked(mAuthenticatorType);
- }
- }
- }
- }
-
- boolean handleMessage(Message message) {
- if (message.what == mMessageWhatConnected) {
- ConnectedMessagePayload payload = (ConnectedMessagePayload)message.obj;
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "notifying callback " + payload.mCallback + " that it is connected");
- }
- payload.mCallback.onConnected(payload.mService);
- return true;
- } else if (message.what == mMessageWhatDisconnected) {
- Callback callback = (Callback)message.obj;
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "notifying callback " + callback + " that it is disconnected");
- }
- callback.onDisconnected();
- return true;
- } else {
- return false;
- }
- }
-}
diff --git a/core/java/android/accounts/GrantCredentialsPermissionActivity.java b/core/java/android/accounts/GrantCredentialsPermissionActivity.java
index e3ed2e9..f4b7258 100644
--- a/core/java/android/accounts/GrantCredentialsPermissionActivity.java
+++ b/core/java/android/accounts/GrantCredentialsPermissionActivity.java
@@ -18,14 +18,16 @@
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
+import android.widget.LinearLayout;
+import android.widget.ImageView;
import android.view.View;
import android.view.LayoutInflater;
-import android.view.ViewGroup;
+import android.view.Window;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.text.TextUtils;
+import android.graphics.drawable.Drawable;
import com.android.internal.R;
/**
@@ -43,62 +45,68 @@
private String mAuthTokenType;
private int mUid;
private Bundle mResultBundle = null;
+ protected LayoutInflater mInflater;
protected void onCreate(Bundle savedInstanceState) {
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
- getWindow().setContentView(R.layout.grant_credentials_permission);
- mAccount = getIntent().getExtras().getParcelable(EXTRAS_ACCOUNT);
- mAuthTokenType = getIntent().getExtras().getString(EXTRAS_AUTH_TOKEN_TYPE);
- mUid = getIntent().getExtras().getInt(EXTRAS_REQUESTING_UID);
- final String accountTypeLabel =
- getIntent().getExtras().getString(EXTRAS_ACCOUNT_TYPE_LABEL);
- final String[] packages = getIntent().getExtras().getStringArray(EXTRAS_PACKAGES);
+ setContentView(R.layout.grant_credentials_permission);
- findViewById(R.id.allow).setOnClickListener(this);
- findViewById(R.id.deny).setOnClickListener(this);
+ mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- TextView messageView = (TextView) getWindow().findViewById(R.id.message);
- String authTokenLabel = getIntent().getExtras().getString(EXTRAS_AUTH_TOKEN_LABEL);
- if (authTokenLabel.length() == 0) {
- CharSequence grantCredentialsPermissionFormat = getResources().getText(
- R.string.grant_credentials_permission_message_desc);
- messageView.setText(String.format(grantCredentialsPermissionFormat.toString(),
- mAccount.name, accountTypeLabel));
- } else {
- CharSequence grantCredentialsPermissionFormat = getResources().getText(
- R.string.grant_credentials_permission_message_with_authtokenlabel_desc);
- messageView.setText(String.format(grantCredentialsPermissionFormat.toString(),
- authTokenLabel, mAccount.name, accountTypeLabel));
- }
+ final Bundle extras = getIntent().getExtras();
+ mAccount = extras.getParcelable(EXTRAS_ACCOUNT);
+ mAuthTokenType = extras.getString(EXTRAS_AUTH_TOKEN_TYPE);
+ mUid = extras.getInt(EXTRAS_REQUESTING_UID);
+ final String accountTypeLabel = extras.getString(EXTRAS_ACCOUNT_TYPE_LABEL);
+ final String[] packages = extras.getStringArray(EXTRAS_PACKAGES);
+ final String authTokenLabel = extras.getString(EXTRAS_AUTH_TOKEN_LABEL);
- String[] packageLabels = new String[packages.length];
+ findViewById(R.id.allow_button).setOnClickListener(this);
+ findViewById(R.id.deny_button).setOnClickListener(this);
+
+ LinearLayout packagesListView = (LinearLayout) findViewById(R.id.packages_list);
+
final PackageManager pm = getPackageManager();
- for (int i = 0; i < packages.length; i++) {
+ for (String pkg : packages) {
+ String packageLabel;
try {
- packageLabels[i] =
- pm.getApplicationLabel(pm.getApplicationInfo(packages[i], 0)).toString();
+ packageLabel = pm.getApplicationLabel(pm.getApplicationInfo(pkg, 0)).toString();
} catch (PackageManager.NameNotFoundException e) {
- packageLabels[i] = packages[i];
+ packageLabel = pkg;
}
+ packagesListView.addView(newPackageView(packageLabel));
}
- ((ListView) findViewById(R.id.packages_list)).setAdapter(
- new PackagesArrayAdapter(this, packageLabels));
+
+ ((TextView) findViewById(R.id.account_name)).setText(mAccount.name);
+ ((TextView) findViewById(R.id.account_type)).setText(accountTypeLabel);
+ TextView authTokenTypeView = (TextView) findViewById(R.id.authtoken_type);
+ if (TextUtils.isEmpty(authTokenLabel)) {
+ authTokenTypeView.setVisibility(View.GONE);
+ } else {
+ authTokenTypeView.setText(authTokenLabel);
+ }
+ }
+
+ private View newPackageView(String packageLabel) {
+ View view = mInflater.inflate(R.layout.permissions_package_list_item, null);
+ ((TextView) view.findViewById(R.id.package_label)).setText(packageLabel);
+ return view;
}
public void onClick(View v) {
+ final AccountManagerService accountManagerService = AccountManagerService.getSingleton();
switch (v.getId()) {
- case R.id.allow:
- AccountManagerService.getSingleton().grantAppPermission(mAccount, mAuthTokenType,
- mUid);
+ case R.id.allow_button:
+ accountManagerService.grantAppPermission(mAccount, mAuthTokenType, mUid);
Intent result = new Intent();
result.putExtra("retry", true);
setResult(RESULT_OK, result);
setAccountAuthenticatorResult(result.getExtras());
break;
- case R.id.deny:
- AccountManagerService.getSingleton().revokeAppPermission(mAccount, mAuthTokenType,
- mUid);
+ case R.id.deny_button:
+ accountManagerService.revokeAppPermission(mAccount, mAuthTokenType, mUid);
setResult(RESULT_CANCELED);
break;
}
@@ -110,63 +118,20 @@
}
/**
- * Sends the result or a Constants.ERROR_CODE_CANCELED error if a result isn't present.
+ * Sends the result or a {@link AccountManager#ERROR_CODE_CANCELED} error if a
+ * result isn't present.
*/
public void finish() {
Intent intent = getIntent();
- AccountAuthenticatorResponse accountAuthenticatorResponse =
- intent.getParcelableExtra(EXTRAS_RESPONSE);
- if (accountAuthenticatorResponse != null) {
+ AccountAuthenticatorResponse response = intent.getParcelableExtra(EXTRAS_RESPONSE);
+ if (response != null) {
// send the result bundle back if set, otherwise send an error.
if (mResultBundle != null) {
- accountAuthenticatorResponse.onResult(mResultBundle);
+ response.onResult(mResultBundle);
} else {
- accountAuthenticatorResponse.onError(AccountManager.ERROR_CODE_CANCELED, "canceled");
+ response.onError(AccountManager.ERROR_CODE_CANCELED, "canceled");
}
}
super.finish();
}
-
- private static class PackagesArrayAdapter extends ArrayAdapter<String> {
- protected LayoutInflater mInflater;
- private static final int mResource = R.layout.simple_list_item_1;
-
- public PackagesArrayAdapter(Context context, String[] items) {
- super(context, mResource, items);
- mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- }
-
- static class ViewHolder {
- TextView label;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // A ViewHolder keeps references to children views to avoid unneccessary calls
- // to findViewById() on each row.
- ViewHolder holder;
-
- // When convertView is not null, we can reuse it directly, there is no need
- // to reinflate it. We only inflate a new View when the convertView supplied
- // by ListView is null.
- if (convertView == null) {
- convertView = mInflater.inflate(mResource, null);
-
- // Creates a ViewHolder and store references to the two children views
- // we want to bind data to.
- holder = new ViewHolder();
- holder.label = (TextView) convertView.findViewById(R.id.text1);
-
- convertView.setTag(holder);
- } else {
- // Get the ViewHolder back to get fast access to the TextView
- // and the ImageView.
- holder = (ViewHolder) convertView.getTag();
- }
-
- holder.label.setText(getItem(position));
-
- return convertView;
- }
- }
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index ff48583..5b34ef9 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -46,7 +46,7 @@
*/
public final class BluetoothAdapter {
private static final String TAG = "BluetoothAdapter";
- private static final boolean DBG = false;
+ private static final boolean DBG = true; //STOPSHIP: Remove excess logging
/**
* Sentinel error value for this class. Guaranteed to not equal any other
diff --git a/core/java/android/content/AbstractSyncableContentProvider.java b/core/java/android/content/AbstractSyncableContentProvider.java
index eba8715..9866862 100644
--- a/core/java/android/content/AbstractSyncableContentProvider.java
+++ b/core/java/android/content/AbstractSyncableContentProvider.java
@@ -747,4 +747,8 @@
public void writeSyncDataBytes(Account account, byte[] data) {
mSyncState.writeSyncDataBytes(mOpenHelper.getWritableDatabase(), account, data);
}
+
+ protected ContentProvider getSyncStateProvider() {
+ return mSyncState.asContentProvider();
+ }
}
diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java
index fb6091a..0db6155 100644
--- a/core/java/android/content/AbstractThreadedSyncAdapter.java
+++ b/core/java/android/content/AbstractThreadedSyncAdapter.java
@@ -21,6 +21,7 @@
import android.os.Process;
import android.os.NetStat;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.EventLog;
import java.util.concurrent.atomic.AtomicInteger;
@@ -117,6 +118,12 @@
}
}
}
+
+ public void initialize(Account account, String authority) throws RemoteException {
+ Bundle extras = new Bundle();
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
+ startSync(null, authority, account, extras);
+ }
}
/**
diff --git a/core/java/android/content/ISyncAdapter.aidl b/core/java/android/content/ISyncAdapter.aidl
index 4660527..dd9d14e 100644
--- a/core/java/android/content/ISyncAdapter.aidl
+++ b/core/java/android/content/ISyncAdapter.aidl
@@ -44,4 +44,12 @@
* @param syncContext the ISyncContext that was passed to {@link #startSync}
*/
void cancelSync(ISyncContext syncContext);
+
+ /**
+ * Initialize the SyncAdapter for this account and authority.
+ *
+ * @param account the account that should be synced
+ * @param authority the authority that should be synced
+ */
+ void initialize(in Account account, String authority);
}
diff --git a/core/java/android/content/SyncAdapter.java b/core/java/android/content/SyncAdapter.java
index 88dc332..af1634e 100644
--- a/core/java/android/content/SyncAdapter.java
+++ b/core/java/android/content/SyncAdapter.java
@@ -38,6 +38,12 @@
public void cancelSync(ISyncContext syncContext) throws RemoteException {
SyncAdapter.this.cancelSync();
}
+
+ public void initialize(Account account, String authority) throws RemoteException {
+ Bundle extras = new Bundle();
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
+ startSync(null, authority, account, extras);
+ }
}
Transport mTransport = new Transport();
diff --git a/core/java/android/content/SyncContext.java b/core/java/android/content/SyncContext.java
index 587586d..cc914c0 100644
--- a/core/java/android/content/SyncContext.java
+++ b/core/java/android/content/SyncContext.java
@@ -56,7 +56,9 @@
if (now < mLastHeartbeatSendTime + HEARTBEAT_SEND_INTERVAL_IN_MS) return;
try {
mLastHeartbeatSendTime = now;
- mSyncContext.sendHeartbeat();
+ if (mSyncContext != null) {
+ mSyncContext.sendHeartbeat();
+ }
} catch (RemoteException e) {
// this should never happen
}
@@ -64,13 +66,15 @@
public void onFinished(SyncResult result) {
try {
- mSyncContext.onFinished(result);
+ if (mSyncContext != null) {
+ mSyncContext.onFinished(result);
+ }
} catch (RemoteException e) {
// this should never happen
}
}
public IBinder getSyncContextBinder() {
- return mSyncContext.asBinder();
+ return (mSyncContext == null) ? null : mSyncContext.asBinder();
}
}
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 9757ef6..8a529e9 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -544,6 +544,46 @@
return (activeSyncContext != null) ? activeSyncContext.mSyncOperation.account : null;
}
+ private void initializeSyncAdapter(Account account, String authority) {
+ SyncAdapterType syncAdapterType = SyncAdapterType.newKey(authority, account.type);
+ RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
+ mSyncAdapters.getServiceInfo(syncAdapterType);
+ if (syncAdapterInfo == null) {
+ Log.w(TAG, "can't find a sync adapter for " + syncAdapterType);
+ return;
+ }
+
+ Intent intent = new Intent();
+ intent.setAction("android.content.SyncAdapter");
+ intent.setComponent(syncAdapterInfo.componentName);
+ mContext.bindService(intent, new InitializerServiceConnection(account, authority),
+ Context.BIND_AUTO_CREATE);
+ }
+
+ private class InitializerServiceConnection implements ServiceConnection {
+ private final Account mAccount;
+ private final String mAuthority;
+
+ public InitializerServiceConnection(Account account, String authority) {
+ mAccount = account;
+ mAuthority = authority;
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ try {
+ ISyncAdapter.Stub.asInterface(service).initialize(mAccount, mAuthority);
+ } catch (RemoteException e) {
+ // doesn't matter, we will retry again later
+ } finally {
+ mContext.unbindService(this);
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ mContext.unbindService(this);
+ }
+ }
+
/**
* Returns whether or not sync is enabled. Sync can be enabled by
* setting the system property "ro.config.sync" to the value "yes".
@@ -686,36 +726,34 @@
continue;
}
- // make this an initialization sync if the isSyncable state is unknown
- Bundle extrasCopy = extras;
- long delayCopy = delay;
+ // initialize the SyncAdapter if the isSyncable state is unknown
if (isSyncable < 0) {
- extrasCopy = new Bundle(extras);
- extrasCopy.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
- delayCopy = -1; // expedite this
- } else {
- final boolean syncAutomatically = masterSyncAutomatically
- && mSyncStorageEngine.getSyncAutomatically(account, authority);
- boolean syncAllowed =
- manualSync || (backgroundDataUsageAllowed && syncAutomatically);
- if (!syncAllowed) {
- if (isLoggable) {
- Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority
- + " is not allowed, dropping request");
- }
- continue;
- }
+ initializeSyncAdapter(account, authority);
+ continue;
}
+
+ final boolean syncAutomatically = masterSyncAutomatically
+ && mSyncStorageEngine.getSyncAutomatically(account, authority);
+ boolean syncAllowed =
+ manualSync || (backgroundDataUsageAllowed && syncAutomatically);
+ if (!syncAllowed) {
+ if (isLoggable) {
+ Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority
+ + " is not allowed, dropping request");
+ }
+ continue;
+ }
+
if (isLoggable) {
Log.v(TAG, "scheduleSync:"
- + " delay " + delayCopy
+ + " delay " + delay
+ ", source " + source
+ ", account " + account
+ ", authority " + authority
- + ", extras " + extrasCopy);
+ + ", extras " + extras);
}
scheduleSyncOperation(
- new SyncOperation(account, source, authority, extrasCopy, delayCopy));
+ new SyncOperation(account, source, authority, extras, delay));
}
}
}
diff --git a/core/java/android/net/http/Connection.java b/core/java/android/net/http/Connection.java
index 2d39e39..b8e17da 100644
--- a/core/java/android/net/http/Connection.java
+++ b/core/java/android/net/http/Connection.java
@@ -94,7 +94,6 @@
*/
private static final String HTTP_CONNECTION = "http.connection";
- RequestQueue.ConnectionManager mConnectionManager;
RequestFeeder mRequestFeeder;
/**
@@ -104,11 +103,9 @@
private byte[] mBuf;
protected Connection(Context context, HttpHost host,
- RequestQueue.ConnectionManager connectionManager,
RequestFeeder requestFeeder) {
mContext = context;
mHost = host;
- mConnectionManager = connectionManager;
mRequestFeeder = requestFeeder;
mCanPersist = false;
@@ -124,18 +121,15 @@
* necessary
*/
static Connection getConnection(
- Context context, HttpHost host,
- RequestQueue.ConnectionManager connectionManager,
+ Context context, HttpHost host, HttpHost proxy,
RequestFeeder requestFeeder) {
if (host.getSchemeName().equals("http")) {
- return new HttpConnection(context, host, connectionManager,
- requestFeeder);
+ return new HttpConnection(context, host, requestFeeder);
}
// Otherwise, default to https
- return new HttpsConnection(context, host, connectionManager,
- requestFeeder);
+ return new HttpsConnection(context, host, proxy, requestFeeder);
}
/**
@@ -338,7 +332,7 @@
mRequestFeeder.requeueRequest(tReq);
empty = false;
}
- if (empty) empty = mRequestFeeder.haveRequest(mHost);
+ if (empty) empty = !mRequestFeeder.haveRequest(mHost);
}
return empty;
}
diff --git a/core/java/android/net/http/ConnectionThread.java b/core/java/android/net/http/ConnectionThread.java
index 0b30e58..32191d2 100644
--- a/core/java/android/net/http/ConnectionThread.java
+++ b/core/java/android/net/http/ConnectionThread.java
@@ -108,24 +108,11 @@
if (HttpLog.LOGV) HttpLog.v("ConnectionThread: new request " +
request.mHost + " " + request );
- HttpHost proxy = mConnectionManager.getProxyHost();
-
- HttpHost host;
- if (false) {
- // Allow https proxy
- host = proxy == null ? request.mHost : proxy;
- } else {
- // Disallow https proxy -- tmob proxy server
- // serves a request loop for https reqs
- host = (proxy == null ||
- request.mHost.getSchemeName().equals("https")) ?
- request.mHost : proxy;
- }
- mConnection = mConnectionManager.getConnection(mContext, host);
+ mConnection = mConnectionManager.getConnection(mContext,
+ request.mHost);
mConnection.processRequests(request);
if (mConnection.getCanPersist()) {
- if (!mConnectionManager.recycleConnection(host,
- mConnection)) {
+ if (!mConnectionManager.recycleConnection(mConnection)) {
mConnection.closeConnection();
}
} else {
diff --git a/core/java/android/net/http/HttpConnection.java b/core/java/android/net/http/HttpConnection.java
index 8b12d0b..6df86bf 100644
--- a/core/java/android/net/http/HttpConnection.java
+++ b/core/java/android/net/http/HttpConnection.java
@@ -35,9 +35,8 @@
class HttpConnection extends Connection {
HttpConnection(Context context, HttpHost host,
- RequestQueue.ConnectionManager connectionManager,
RequestFeeder requestFeeder) {
- super(context, host, connectionManager, requestFeeder);
+ super(context, host, requestFeeder);
}
/**
diff --git a/core/java/android/net/http/HttpsConnection.java b/core/java/android/net/http/HttpsConnection.java
index 8a69d0d..f735f3d 100644
--- a/core/java/android/net/http/HttpsConnection.java
+++ b/core/java/android/net/http/HttpsConnection.java
@@ -131,13 +131,16 @@
*/
private boolean mAborted = false;
+ // Used when connecting through a proxy.
+ private HttpHost mProxyHost;
+
/**
* Contructor for a https connection.
*/
- HttpsConnection(Context context, HttpHost host,
- RequestQueue.ConnectionManager connectionManager,
+ HttpsConnection(Context context, HttpHost host, HttpHost proxy,
RequestFeeder requestFeeder) {
- super(context, host, connectionManager, requestFeeder);
+ super(context, host, requestFeeder);
+ mProxyHost = proxy;
}
/**
@@ -159,8 +162,7 @@
AndroidHttpClientConnection openConnection(Request req) throws IOException {
SSLSocket sslSock = null;
- HttpHost proxyHost = mConnectionManager.getProxyHost();
- if (proxyHost != null) {
+ if (mProxyHost != null) {
// If we have a proxy set, we first send a CONNECT request
// to the proxy; if the proxy returns 200 OK, we negotiate
// a secure connection to the target server via the proxy.
@@ -172,7 +174,7 @@
Socket proxySock = null;
try {
proxySock = new Socket
- (proxyHost.getHostName(), proxyHost.getPort());
+ (mProxyHost.getHostName(), mProxyHost.getPort());
proxySock.setSoTimeout(60 * 1000);
diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java
index 190ae7a..77cd544 100644
--- a/core/java/android/net/http/RequestHandle.java
+++ b/core/java/android/net/http/RequestHandle.java
@@ -42,15 +42,13 @@
private WebAddress mUri;
private String mMethod;
private Map<String, String> mHeaders;
-
private RequestQueue mRequestQueue;
-
private Request mRequest;
-
private InputStream mBodyProvider;
private int mBodyLength;
-
private int mRedirectCount = 0;
+ // Used only with synchronous requests.
+ private Connection mConnection;
private final static String AUTHORIZATION_HEADER = "Authorization";
private final static String PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization";
@@ -81,6 +79,19 @@
}
/**
+ * Creates a new request session with a given Connection. This connection
+ * is used during a synchronous load to handle this request.
+ */
+ public RequestHandle(RequestQueue requestQueue, String url, WebAddress uri,
+ String method, Map<String, String> headers,
+ InputStream bodyProvider, int bodyLength, Request request,
+ Connection conn) {
+ this(requestQueue, url, uri, method, headers, bodyProvider, bodyLength,
+ request);
+ mConnection = conn;
+ }
+
+ /**
* Cancels this request
*/
public void cancel() {
@@ -262,6 +273,12 @@
mRequest.waitUntilComplete();
}
+ public void processRequest() {
+ if (mConnection != null) {
+ mConnection.processRequests(mRequest);
+ }
+ }
+
/**
* @return Digest-scheme authentication response.
*/
diff --git a/core/java/android/net/http/RequestQueue.java b/core/java/android/net/http/RequestQueue.java
index 875caa0..84b6487 100644
--- a/core/java/android/net/http/RequestQueue.java
+++ b/core/java/android/net/http/RequestQueue.java
@@ -171,16 +171,17 @@
}
public Connection getConnection(Context context, HttpHost host) {
+ host = RequestQueue.this.determineHost(host);
Connection con = mIdleCache.getConnection(host);
if (con == null) {
mTotalConnection++;
- con = Connection.getConnection(
- mContext, host, this, RequestQueue.this);
+ con = Connection.getConnection(mContext, host, mProxyHost,
+ RequestQueue.this);
}
return con;
}
- public boolean recycleConnection(HttpHost host, Connection connection) {
- return mIdleCache.cacheConnection(host, connection);
+ public boolean recycleConnection(Connection connection) {
+ return mIdleCache.cacheConnection(connection.getHost(), connection);
}
}
@@ -342,6 +343,66 @@
req);
}
+ private static class SyncFeeder implements RequestFeeder {
+ // This is used in the case where the request fails and needs to be
+ // requeued into the RequestFeeder.
+ private Request mRequest;
+ SyncFeeder() {
+ }
+ public Request getRequest() {
+ Request r = mRequest;
+ mRequest = null;
+ return r;
+ }
+ public Request getRequest(HttpHost host) {
+ return getRequest();
+ }
+ public boolean haveRequest(HttpHost host) {
+ return mRequest != null;
+ }
+ public void requeueRequest(Request r) {
+ mRequest = r;
+ }
+ }
+
+ public RequestHandle queueSynchronousRequest(String url, WebAddress uri,
+ String method, Map<String, String> headers,
+ EventHandler eventHandler, InputStream bodyProvider,
+ int bodyLength) {
+ if (HttpLog.LOGV) {
+ HttpLog.v("RequestQueue.dispatchSynchronousRequest " + uri);
+ }
+
+ HttpHost host = new HttpHost(uri.mHost, uri.mPort, uri.mScheme);
+
+ Request req = new Request(method, host, mProxyHost, uri.mPath,
+ bodyProvider, bodyLength, eventHandler, headers);
+
+ // Open a new connection that uses our special RequestFeeder
+ // implementation.
+ host = determineHost(host);
+ Connection conn = Connection.getConnection(mContext, host, mProxyHost,
+ new SyncFeeder());
+
+ // TODO: I would like to process the request here but LoadListener
+ // needs a RequestHandle to process some messages.
+ return new RequestHandle(this, url, uri, method, headers, bodyProvider,
+ bodyLength, req, conn);
+
+ }
+
+ // Chooses between the proxy and the request's host.
+ private HttpHost determineHost(HttpHost host) {
+ // There used to be a comment in ConnectionThread about t-mob's proxy
+ // being really bad about https. But, HttpsConnection actually looks
+ // for a proxy and connects through it anyway. I think that this check
+ // is still valid because if a site is https, we will use
+ // HttpsConnection rather than HttpConnection if the proxy address is
+ // not secure.
+ return (mProxyHost == null || "https".equals(host.getSchemeName()))
+ ? host : mProxyHost;
+ }
+
/**
* @return true iff there are any non-active requests pending
*/
@@ -478,6 +539,6 @@
interface ConnectionManager {
HttpHost getProxyHost();
Connection getConnection(Context context, HttpHost host);
- boolean recycleConnection(HttpHost host, Connection connection);
+ boolean recycleConnection(Connection connection);
}
}
diff --git a/core/java/android/os/DropBoxEntry.aidl b/core/java/android/os/DropBoxEntry.aidl
new file mode 100644
index 0000000..225eee1
--- /dev/null
+++ b/core/java/android/os/DropBoxEntry.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.os;
+
+parcelable DropBoxEntry;
diff --git a/core/java/android/os/DropBoxEntry.java b/core/java/android/os/DropBoxEntry.java
new file mode 100644
index 0000000..e3816a8
--- /dev/null
+++ b/core/java/android/os/DropBoxEntry.java
@@ -0,0 +1,163 @@
+/*
+ * 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.os;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * A single entry retrieved from an {@link IDropBox} implementation.
+ * This may include a reference to a stream, so you must call
+ * {@link #close()} when you are done using it.
+ *
+ * {@pending}
+ */
+public class DropBoxEntry implements Parcelable {
+ private final String mTag;
+ private final long mTimeMillis;
+
+ private final String mText;
+ private final ParcelFileDescriptor mFileDescriptor;
+ private final int mFlags;
+
+ /** Flag value: Entry's content was deleted to save space. */
+ public static final int IS_EMPTY = 1;
+
+ /** Flag value: Content is human-readable UTF-8 text (possibly compressed). */
+ public static final int IS_TEXT = 2;
+
+ /** Flag value: Content can been decompressed with {@link GZIPOutputStream}. */
+ public static final int IS_GZIPPED = 4;
+
+ /** Create a new DropBoxEntry with the specified contents. */
+ public DropBoxEntry(String tag, long timeMillis, String text) {
+ if (tag == null || text == null) throw new NullPointerException();
+ mTag = tag;
+ mTimeMillis = timeMillis;
+ mText = text;
+ mFileDescriptor = null;
+ mFlags = IS_TEXT;
+ }
+
+ /** Create a new DropBoxEntry with the specified contents. */
+ public DropBoxEntry(String tag, long millis, File data, int flags) throws IOException {
+ if (tag == null) throw new NullPointerException();
+ if (((flags & IS_EMPTY) != 0) != (data == null)) throw new IllegalArgumentException();
+
+ mTag = tag;
+ mTimeMillis = millis;
+ mText = null;
+ mFlags = flags;
+ mFileDescriptor = data == null ? null :
+ ParcelFileDescriptor.open(data, ParcelFileDescriptor.MODE_READ_ONLY);
+ }
+
+ /** Internal constructor for CREATOR.createFromParcel(). */
+ private DropBoxEntry(String tag, long millis, Object value, int flags) {
+ if (tag == null) throw new NullPointerException();
+ if (((flags & IS_EMPTY) != 0) != (value == null)) throw new IllegalArgumentException();
+
+ mTag = tag;
+ mTimeMillis = millis;
+ mFlags = flags;
+
+ if (value == null) {
+ mText = null;
+ mFileDescriptor = null;
+ } else if (value instanceof String) {
+ if ((flags & IS_TEXT) == 0) throw new IllegalArgumentException();
+ mText = (String) value;
+ mFileDescriptor = null;
+ } else if (value instanceof ParcelFileDescriptor) {
+ mText = null;
+ mFileDescriptor = (ParcelFileDescriptor) value;
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /** Close the input stream associated with this entry. */
+ public synchronized void close() {
+ try { if (mFileDescriptor != null) mFileDescriptor.close(); } catch (IOException e) { }
+ }
+
+ /** @return the tag originally attached to the entry. */
+ public String getTag() { return mTag; }
+
+ /** @return time when the entry was originally created. */
+ public long getTimeMillis() { return mTimeMillis; }
+
+ /** @return flags describing the content returned by @{link #getInputStream()}. */
+ public int getFlags() { return mFlags & ~IS_GZIPPED; } // getInputStream() decompresses.
+
+ /**
+ * @param maxLength of string to return (will truncate at this length).
+ * @return the uncompressed text contents of the entry, null if the entry is not text.
+ */
+ public String getText(int maxLength) {
+ if (mText != null) return mText.substring(0, Math.min(maxLength, mText.length()));
+ if ((mFlags & IS_TEXT) == 0) return null;
+
+ try {
+ InputStream stream = getInputStream();
+ if (stream == null) return null;
+ char[] buf = new char[maxLength];
+ InputStreamReader reader = new InputStreamReader(stream);
+ return new String(buf, 0, Math.max(0, reader.read(buf)));
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /** @return the uncompressed contents of the entry, or null if the contents were lost */
+ public InputStream getInputStream() throws IOException {
+ if (mText != null) return new ByteArrayInputStream(mText.getBytes("UTF8"));
+ if (mFileDescriptor == null) return null;
+ InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(mFileDescriptor);
+ return (mFlags & IS_GZIPPED) != 0 ? new GZIPInputStream(is) : is;
+ }
+
+ public static final Parcelable.Creator<DropBoxEntry> CREATOR = new Parcelable.Creator() {
+ public DropBoxEntry[] newArray(int size) { return new DropBoxEntry[size]; }
+ public DropBoxEntry createFromParcel(Parcel in) {
+ return new DropBoxEntry(
+ in.readString(), in.readLong(), in.readValue(null), in.readInt());
+ }
+ };
+
+ public int describeContents() {
+ return mFileDescriptor != null ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mTag);
+ out.writeLong(mTimeMillis);
+ if (mFileDescriptor != null) {
+ out.writeValue(mFileDescriptor);
+ } else {
+ out.writeValue(mText);
+ }
+ out.writeInt(mFlags);
+ }
+}
diff --git a/core/java/android/os/IDropBox.aidl b/core/java/android/os/IDropBox.aidl
new file mode 100644
index 0000000..f951e52
--- /dev/null
+++ b/core/java/android/os/IDropBox.aidl
@@ -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.os;
+
+import android.os.DropBoxEntry;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Enqueues chunks of data (from various sources -- application crashes, kernel
+ * log records, etc.). The queue is size bounded and will drop old data if the
+ * enqueued data exceeds the maximum size.
+ *
+ * <p>This interface is implemented by a system service you can access:
+ *
+ * <pre>IDropBox.Stub.asInterface(ServiceManager.getService("dropbox"));</pre>
+ *
+ * <p>Other system services and debugging tools may scan the drop box to upload
+ * entries for processing.
+ *
+ * {@pending}
+ */
+interface IDropBox {
+ /**
+ * Stores human-readable text. The data may be discarded eventually (or even
+ * immediately) if space is limited, or ignored entirely if the tag has been
+ * blocked (see {@link #isTagEnabled}).
+ *
+ * @param tag describing the type of entry being stored
+ * @param data value to store
+ */
+ void addText(String tag, String data);
+
+ /**
+ * Stores binary data. The data may be ignored or discarded as with
+ * {@link #addText}.
+ *
+ * @param tag describing the type of entry being stored
+ * @param data value to store
+ * @param flags describing the data, defined in {@link DropBoxEntry}
+ */
+ void addData(String tag, in byte[] data, int flags);
+
+ /**
+ * Stores data read from a file descriptor. The data may be ignored or
+ * discarded as with {@link #addText}. You must close your
+ * ParcelFileDescriptor object after calling this method!
+ *
+ * @param tag describing the type of entry being stored
+ * @param data file descriptor to read from
+ * @param flags describing the data, defined in {@link DropBoxEntry}
+ */
+ void addFile(String tag, in ParcelFileDescriptor data, int flags);
+
+ /**
+ * Checks any blacklists (set in system settings) to see whether a certain
+ * tag is allowed. Entries with disabled tags will be dropped immediately,
+ * so you can save the work of actually constructing and sending the data.
+ *
+ * @param tag that would be used in {@link #addText} or {@link #addFile}
+ * @return whether events with that tag would be accepted
+ */
+ boolean isTagEnabled(String tag);
+
+ /**
+ * Gets the next entry from the drop box *after* the specified time.
+ * Requires android.permission.READ_LOGS.
+ *
+ * @param millis time of the last entry seen
+ * @return the next entry, or null if there are no more entries
+ */
+ DropBoxEntry getNextEntry(long millis);
+
+ // TODO: It may be useful to have some sort of notification mechanism
+ // when data is added to the dropbox, for demand-driven readers --
+ // for now readers need to poll the dropbox to find new data.
+}
diff --git a/core/java/android/pim/vcard/Constants.java b/core/java/android/pim/vcard/Constants.java
index ca41ce5..a1c7e10 100644
--- a/core/java/android/pim/vcard/Constants.java
+++ b/core/java/android/pim/vcard/Constants.java
@@ -16,16 +16,47 @@
package android.pim.vcard;
/**
- * Constants used in both composer and parser.
+ * Constants used in both exporter and importer code.
*/
/* package */ class Constants {
- public static final String ATTR_TYPE = "TYPE";
-
public static final String VERSION_V21 = "2.1";
public static final String VERSION_V30 = "3.0";
+
+ // The property names valid both in vCard 2.1 and 3.0.
+ public static final String PROPERTY_BEGIN = "BEGIN";
+ public static final String PROPERTY_VERSION = "VERSION";
+ public static final String PROPERTY_N = "N";
+ public static final String PROPERTY_FN = "FN";
+ public static final String PROPERTY_ADR = "ADR";
+ public static final String PROPERTY_EMAIL = "EMAIL";
+ public static final String PROPERTY_NOTE = "NOTE";
+ public static final String PROPERTY_ORG = "ORG";
+ public static final String PROPERTY_SOUND = "SOUND"; // Not fully supported.
+ public static final String PROPERTY_TEL = "TEL";
+ public static final String PROPERTY_TITLE = "TITLE";
+ public static final String PROPERTY_ROLE = "ROLE";
+ public static final String PROPERTY_PHOTO = "PHOTO";
+ public static final String PROPERTY_LOGO = "LOGO";
+ public static final String PROPERTY_URL = "URL";
+ public static final String PROPERTY_BDAY = "BDAY"; // Birthday
+ public static final String PROPERTY_END = "END";
+
+ // Valid property names not supported (not appropriately handled) by our vCard importer now.
+ public static final String PROPERTY_REV = "REV";
+ public static final String PROPERTY_AGENT = "AGENT";
+
+ // Available in vCard 3.0. Shoud not use when composing vCard 2.1 file.
+ public static final String PROPERTY_NAME = "NAME";
+ public static final String PROPERTY_NICKNAME = "NICKNAME";
+ public static final String PROPERTY_SORT_STRING = "SORT-STRING";
- // Properties both the current (as of 2009-08-17) ContactsStruct and de-fact vCard extensions
+ // De-fact property values expressing phonetic names.
+ public static final String PROPERTY_X_PHONETIC_FIRST_NAME = "X-PHONETIC-FIRST-NAME";
+ public static final String PROPERTY_X_PHONETIC_MIDDLE_NAME = "X-PHONETIC-MIDDLE-NAME";
+ public static final String PROPERTY_X_PHONETIC_LAST_NAME = "X-PHONETIC-LAST-NAME";
+
+ // Properties both ContactsStruct in Eclair and de-fact vCard extensions
// shown in http://en.wikipedia.org/wiki/VCard support are defined here.
public static final String PROPERTY_X_AIM = "X-AIM";
public static final String PROPERTY_X_MSN = "X-MSN";
@@ -34,12 +65,26 @@
public static final String PROPERTY_X_JABBER = "X-JABBER";
public static final String PROPERTY_X_GOOGLE_TALK = "X-GOOGLE-TALK";
public static final String PROPERTY_X_SKYPE_USERNAME = "X-SKYPE-USERNAME";
+ // Properties only ContactsStruct has. We alse use this.
+ public static final String PROPERTY_X_QQ = "X-QQ";
+ public static final String PROPERTY_X_NETMEETING = "X-NETMEETING";
+
// Phone number for Skype, available as usual phone.
public static final String PROPERTY_X_SKYPE_PSTNNUMBER = "X-SKYPE-PSTNNUMBER";
- // Some device emits this "X-" attribute, which is specifically invalid but should be
- // always properly accepted, and emitted in some special case (for that device/application).
- public static final String PROPERTY_X_GOOGLE_TALK_WITH_SPACE = "X-GOOGLE TALK";
-
+
+ // Properties for DoCoMo vCard.
+ public static final String PROPERTY_X_CLASS = "X-CLASS";
+ public static final String PROPERTY_X_REDUCTION = "X-REDUCTION";
+ public static final String PROPERTY_X_NO = "X-NO";
+ public static final String PROPERTY_X_DCM_HMN_MODE = "X-DCM-HMN-MODE";
+
+ // For some historical reason, we often use the term "ATTR"/"attribute" especially toward
+ // what is called "param" in both vCard specs, while vCard, while vCard importer side uses
+ // "param".
+ //
+ // TODO: Confusing. Fix it.
+ public static final String ATTR_TYPE = "TYPE";
+
// How more than one TYPE fields are expressed is different between vCard 2.1 and vCard 3.0
//
// e.g.
@@ -59,12 +104,19 @@
public static final String ATTR_TYPE_VOICE = "VOICE";
public static final String ATTR_TYPE_INTERNET = "INTERNET";
+ // Abbreviation of "prefered" according to vCard 2.1 specification.
+ // We interpret this value as "primary" property during import/export.
+ //
+ // Note: Both vCard specs does anything about the requirement about this attribute,
+ // but there may be some vCard importer which will get confused with more than
+ // one "PREF"s in one property name, while Android accepts them.
public static final String ATTR_TYPE_PREF = "PREF";
// Phone types valid in vCard and known to ContactsContract, but not so common.
public static final String ATTR_TYPE_CAR = "CAR";
public static final String ATTR_TYPE_ISDN = "ISDN";
public static final String ATTR_TYPE_PAGER = "PAGER";
+ public static final String ATTR_TYPE_TLX = "TLX"; // Telex
// Phone types existing in vCard 2.1 but not known to ContactsContract.
// TODO: should make parser make these TYPE_CUSTOM.
@@ -73,22 +125,39 @@
public static final String ATTR_TYPE_BBS = "BBS";
public static final String ATTR_TYPE_VIDEO = "VIDEO";
- // Phone types existing in the current Contacts structure but not valid in vCard (at least 2.1)
- // These types are encoded to "X-" attributes when composing vCard for now.
- // Parser passes these even if "X-" is added to the attribute.
- public static final String ATTR_TYPE_PHONE_EXTRA_OTHER = "OTHER";
- public static final String ATTR_TYPE_PHONE_EXTRA_CALLBACK = "CALLBACK";
- // TODO: may be "TYPE=COMPANY,PREF", not "COMPANY-MAIN".
- public static final String ATTR_TYPE_PHONE_EXTRA_COMPANY_MAIN = "COMPANY-MAIN";
- public static final String ATTR_TYPE_PHONE_EXTRA_RADIO = "RADIO";
- public static final String ATTR_TYPE_PHONE_EXTRA_TELEX = "TELEX";
- public static final String ATTR_TYPE_PHONE_EXTRA_TTY_TDD = "TTY-TDD";
- public static final String ATTR_TYPE_PHONE_EXTRA_ASSISTANT = "ASSISTANT";
+ // Attribute for Phones, which are not formally valid in vCard (at least 2.1).
+ // These types are basically encoded to "X-" attributes when composing vCard.
+ // Parser passes these when "X-" is added to the attribute or not.
+ public static final String ATTR_PHONE_EXTRA_TYPE_CALLBACK = "CALLBACK";
+ public static final String ATTR_PHONE_EXTRA_TYPE_RADIO = "RADIO";
+ public static final String ATTR_PHONE_EXTRA_TYPE_TTY_TDD = "TTY-TDD";
+ public static final String ATTR_PHONE_EXTRA_TYPE_ASSISTANT = "ASSISTANT";
+ // vCard composer translates this type to "WORK" + "PREF". Just for parsing.
+ public static final String ATTR_PHONE_EXTRA_TYPE_COMPANY_MAIN = "COMPANY-MAIN";
+ // vCard composer translates this type to "VOICE" Just for parsing.
+ public static final String ATTR_PHONE_EXTRA_TYPE_OTHER = "OTHER";
+
+ // Attribute for addresses.
+ public static final String ATTR_ADR_TYPE_PARCEL = "PARCEL";
+ public static final String ATTR_ADR_TYPE_DOM = "DOM";
+ public static final String ATTR_ADR_TYPE_INTL = "INTL";
+
+ // Attribute types not officially valid but used in some vCard exporter.
+ // Do not use in composer side.
+ public static final String ATTR_EXTRA_TYPE_COMPANY = "COMPANY";
// DoCoMo specific attribute. Used with "SOUND" property, which is alternate of SORT-STRING in
// vCard 3.0.
public static final String ATTR_TYPE_X_IRMC_N = "X-IRMC-N";
+ public interface ImportOnly {
+ public static final String PROPERTY_X_NICKNAME = "X-NICKNAME";
+ // Some device emits this "X-" attribute for expressing Google Talk,
+ // which is specifically invalid but should be always properly accepted, and emitted
+ // in some special case (for that device/application).
+ public static final String PROPERTY_X_GOOGLE_TALK_WITH_SPACE = "X-GOOGLE TALK";
+ }
+
private Constants() {
}
}
\ No newline at end of file
diff --git a/core/java/android/pim/vcard/ContactStruct.java b/core/java/android/pim/vcard/ContactStruct.java
index 36e5e23..eb9c48a 100644
--- a/core/java/android/pim/vcard/ContactStruct.java
+++ b/core/java/android/pim/vcard/ContactStruct.java
@@ -38,6 +38,7 @@
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.CommonDataKinds.Website;
import android.telephony.PhoneNumberUtils;
+import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.Log;
@@ -46,7 +47,6 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -55,11 +55,11 @@
*/
public class ContactStruct {
private static final String LOG_TAG = "vcard.ContactStruct";
-
+
// Key: the name shown in VCard. e.g. "X-AIM", "X-ICQ"
// Value: the result of {@link Contacts.ContactMethods#encodePredefinedImProtocol}
private static final Map<String, Integer> sImMap = new HashMap<String, Integer>();
-
+
static {
sImMap.put(Constants.PROPERTY_X_AIM, Im.PROTOCOL_AIM);
sImMap.put(Constants.PROPERTY_X_MSN, Im.PROTOCOL_MSN);
@@ -68,12 +68,10 @@
sImMap.put(Constants.PROPERTY_X_JABBER, Im.PROTOCOL_JABBER);
sImMap.put(Constants.PROPERTY_X_SKYPE_USERNAME, Im.PROTOCOL_SKYPE);
sImMap.put(Constants.PROPERTY_X_GOOGLE_TALK, Im.PROTOCOL_GOOGLE_TALK);
- sImMap.put(Constants.PROPERTY_X_GOOGLE_TALK_WITH_SPACE, Im.PROTOCOL_GOOGLE_TALK);
+ sImMap.put(Constants.ImportOnly.PROPERTY_X_GOOGLE_TALK_WITH_SPACE,
+ Im.PROTOCOL_GOOGLE_TALK);
}
- /**
- * @hide only for testing
- */
static public class PhoneData {
public final int type;
public final String data;
@@ -90,7 +88,7 @@
@Override
public boolean equals(Object obj) {
- if (obj instanceof PhoneData) {
+ if (!(obj instanceof PhoneData)) {
return false;
}
PhoneData phoneData = (PhoneData)obj;
@@ -125,7 +123,7 @@
@Override
public boolean equals(Object obj) {
- if (obj instanceof EmailData) {
+ if (!(obj instanceof EmailData)) {
return false;
}
EmailData emailData = (EmailData)obj;
@@ -202,7 +200,7 @@
@Override
public boolean equals(Object obj) {
- if (obj instanceof PostalData) {
+ if (!(obj instanceof PostalData)) {
return false;
}
PostalData postalData = (PostalData)obj;
@@ -251,87 +249,117 @@
}
}
- /**
- * @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;
+ // non-final is Intended: we may change the values since this info is separated into
+ // two parts in vCard: "ORG" + "TITLE".
+ public String companyName;
+ public String departmentName;
+ public String titleName;
// 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,
+ public OrganizationData(int type,
+ String companyName,
+ String departmentName,
+ String titleName,
boolean isPrimary) {
this.type = type;
this.companyName = companyName;
- this.positionName = positionName;
+ this.departmentName = departmentName;
+ this.titleName = titleName;
this.isPrimary = isPrimary;
}
@Override
public boolean equals(Object obj) {
- if (obj instanceof OrganizationData) {
+ if (!(obj instanceof OrganizationData)) {
return false;
}
OrganizationData organization = (OrganizationData)obj;
- return (type == organization.type && companyName.equals(organization.companyName) &&
- positionName.equals(organization.positionName) &&
+ return (type == organization.type &&
+ TextUtils.equals(companyName, organization.companyName) &&
+ TextUtils.equals(departmentName, organization.departmentName) &&
+ TextUtils.equals(titleName, organization.titleName) &&
isPrimary == organization.isPrimary);
}
-
+
@Override
public String toString() {
- return String.format("type: %d, company: %s, position: %s, isPrimary: %s",
- type, companyName, positionName, isPrimary);
+ return String.format(
+ "type: %d, company: %s, department: %s, title: %s, isPrimary: %s",
+ type, companyName, departmentName, titleName, isPrimary);
}
}
static public class ImData {
+ public final int protocol;
+ public final String customProtocol;
public final int type;
public final String data;
- public final String label;
public final boolean isPrimary;
- // TODO: ContactsConstant#PROTOCOL, ContactsConstant#CUSTOM_PROTOCOL should be used?
- public ImData(int type, String data, String label, boolean isPrimary) {
+ public ImData(int protocol, String customProtocol, int type,
+ String data, boolean isPrimary) {
+ this.protocol = protocol;
+ this.customProtocol = customProtocol;
this.type = type;
this.data = data;
- this.label = label;
this.isPrimary = isPrimary;
}
@Override
public boolean equals(Object obj) {
- if (obj instanceof ImData) {
+ if (!(obj instanceof ImData)) {
return false;
}
ImData imData = (ImData)obj;
- return (type == imData.type && data.equals(imData.data) &&
- label.equals(imData.label) && isPrimary == imData.isPrimary);
+ return (type == imData.type && protocol == imData.protocol
+ && (customProtocol != null ? customProtocol.equals(imData.customProtocol) :
+ (imData.customProtocol == null))
+ && (data != null ? data.equals(imData.data) : (imData.data == null))
+ && isPrimary == imData.isPrimary);
}
@Override
public String toString() {
- return String.format("type: %d, data: %s, label: %s, isPrimary: %s",
- type, data, label, isPrimary);
+ return String.format(
+ "type: %d, protocol: %d, custom_protcol: %s, data: %s, isPrimary: %s",
+ type, protocol, customProtocol, data, isPrimary);
}
}
- /**
- * @hide only for testing.
- */
static public class PhotoData {
public static final String FORMAT_FLASH = "SWF";
public final int type;
public final String formatName; // used when type is not defined in ContactsContract.
public final byte[] photoBytes;
+ public final boolean isPrimary;
- public PhotoData(int type, String formatName, byte[] photoBytes) {
+ public PhotoData(int type, String formatName, byte[] photoBytes, boolean isPrimary) {
this.type = type;
this.formatName = formatName;
this.photoBytes = photoBytes;
+ this.isPrimary = isPrimary;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof PhotoData)) {
+ return false;
+ }
+ PhotoData photoData = (PhotoData)obj;
+ return (type == photoData.type &&
+ (formatName == null ? (photoData.formatName == null) :
+ formatName.equals(photoData.formatName)) &&
+ (Arrays.equals(photoBytes, photoData.photoBytes)) &&
+ (isPrimary == photoData.isPrimary));
+ }
+
+ @Override
+ public String toString() {
+ return String.format("type: %d, format: %s: size: %d, isPrimary: %s",
+ type, formatName, photoBytes.length, isPrimary);
}
}
@@ -342,10 +370,6 @@
private List<String> mPropertyValueList = new ArrayList<String>();
private byte[] mPropertyBytes;
- public Property() {
- clear();
- }
-
public void setPropertyName(final String propertyName) {
mPropertyName = propertyName;
}
@@ -385,6 +409,7 @@
mPropertyName = null;
mParameterMap.clear();
mPropertyValueList.clear();
+ mPropertyBytes = null;
}
}
@@ -421,17 +446,8 @@
private final int mVCardType;
private final Account mAccount;
- // Each Column of four properties has ISPRIMARY field
- // (See android.provider.Contacts)
- // If false even after the parsing 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() {
- this(VCardConfig.VCARD_TYPE_V21_GENERIC);
+ this(VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
}
public ContactStruct(int vcardType) {
@@ -444,186 +460,6 @@
}
/**
- * @hide only for testing.
- */
- public ContactStruct(String givenName,
- String familyName,
- String middleName,
- String prefix,
- String suffix,
- String phoneticGivenName,
- String pheneticFamilyName,
- String phoneticMiddleName,
- List<byte[]> photoBytesList,
- List<String> notes,
- List<PhoneData> phoneList,
- List<EmailData> emailList,
- List<PostalData> postalList,
- List<OrganizationData> organizationList,
- List<PhotoData> photoList,
- List<String> websiteList) {
- this(VCardConfig.VCARD_TYPE_DEFAULT);
- mGivenName = givenName;
- mFamilyName = familyName;
- mPrefix = prefix;
- mSuffix = suffix;
- mPhoneticGivenName = givenName;
- mPhoneticFamilyName = familyName;
- mPhoneticMiddleName = middleName;
- mEmailList = emailList;
- mPostalList = postalList;
- mOrganizationList = organizationList;
- mPhotoList = photoList;
- mWebsiteList = websiteList;
- }
-
- // All getter methods should be used carefully, since they may change
- // in the future as of 2009-09-24, on which I cannot be sure this structure
- // is completely consolidated.
- // When we are sure we will no longer change them, we'll be happy to
- // make it complete public (withouth @hide tag)
- //
- // Also note that these getter methods should be used only after
- // all properties being pushed into this object. If not, incorrect
- // value will "be stored in the local cache and" be returned to you.
-
- /**
- * @hide
- */
- public String getFamilyName() {
- return mFamilyName;
- }
-
- /**
- * @hide
- */
- public String getGivenName() {
- return mGivenName;
- }
-
- /**
- * @hide
- */
- public String getMiddleName() {
- return mMiddleName;
- }
-
- /**
- * @hide
- */
- public String getPrefix() {
- return mPrefix;
- }
-
- /**
- * @hide
- */
- public String getSuffix() {
- return mSuffix;
- }
-
- /**
- * @hide
- */
- public String getFullName() {
- return mFullName;
- }
-
- /**
- * @hide
- */
- public String getPhoneticFamilyName() {
- return mPhoneticFamilyName;
- }
-
- /**
- * @hide
- */
- public String getPhoneticGivenName() {
- return mPhoneticGivenName;
- }
-
- /**
- * @hide
- */
- public String getPhoneticMiddleName() {
- return mPhoneticMiddleName;
- }
-
- /**
- * @hide
- */
- public String getPhoneticFullName() {
- return mPhoneticFullName;
- }
-
- /**
- * @hide
- */
- public final List<String> getNickNameList() {
- return mNickNameList;
- }
-
- /**
- * @hide
- */
- public String getDisplayName() {
- if (mDisplayName == null) {
- constructDisplayName();
- }
- return mDisplayName;
- }
-
- /**
- * @hide
- */
- public String getBirthday() {
- return mBirthday;
- }
-
- /**
- * @hide
- */
- public final List<PhotoData> getPhotoList() {
- return mPhotoList;
- }
-
- /**
- * @hide
- */
- public final List<String> getNotes() {
- return mNoteList;
- }
-
- /**
- * @hide
- */
- public final List<PhoneData> getPhoneList() {
- return mPhoneList;
- }
-
- /**
- * @hide
- */
- public final List<EmailData> getEmailList() {
- return mEmailList;
- }
-
- /**
- * @hide
- */
- public final List<PostalData> getPostalList() {
- return mPostalList;
- }
-
- /**
- * @hide
- */
- public final List<OrganizationData> getOrganizationList() {
- return mOrganizationList;
- }
-
- /**
* Add a phone info to phoneList.
* @param data phone number
* @param type type col of content://contacts/phones
@@ -643,10 +479,24 @@
}
}
- PhoneData phoneData = new PhoneData(type,
- PhoneNumberUtils.formatNumber(builder.toString()),
- label, isPrimary);
-
+ final String formattedPhoneNumber;
+ {
+ final String rawPhoneNumber = builder.toString();
+ if (VCardConfig.isJapaneseDevice(mVCardType)) {
+ // As of 2009-10-07, there's no formatNumber() which accepts
+ // the second argument and returns String directly.
+ final SpannableStringBuilder tmpBuilder =
+ new SpannableStringBuilder(rawPhoneNumber);
+ PhoneNumberUtils.formatNumber(tmpBuilder, PhoneNumberUtils.FORMAT_JAPAN);
+ formattedPhoneNumber = tmpBuilder.toString();
+ } else {
+ // There's no information available on vCard side. Depend on the default
+ // behavior, which may cause problem in the future when the additional format
+ // rule is supported (e.g. PhoneNumberUtils.FORMAT_KLINGON)
+ formattedPhoneNumber = PhoneNumberUtils.formatNumber(rawPhoneNumber);
+ }
+ }
+ PhoneData phoneData = new PhoneData(type, formattedPhoneNumber, label, isPrimary);
mPhoneList.add(phoneData);
}
@@ -666,24 +516,122 @@
private void addPostal(int type, List<String> propValueList, String label, boolean isPrimary){
if (mPostalList == null) {
- mPostalList = new ArrayList<PostalData>();
+ mPostalList = new ArrayList<PostalData>(0);
}
mPostalList.add(new PostalData(type, propValueList, label, isPrimary));
}
- private void addOrganization(int type, final String companyName,
- final String positionName, boolean isPrimary) {
+ /**
+ * Should be called via {@link #handleOrgValue(int, List, boolean)} or
+ * {@link #handleTitleValue(String)}.
+ */
+ private void addNewOrganization(int type, final String companyName,
+ final String departmentName,
+ final String titleName, boolean isPrimary) {
if (mOrganizationList == null) {
mOrganizationList = new ArrayList<OrganizationData>();
}
- mOrganizationList.add(new OrganizationData(type, companyName, positionName, isPrimary));
+ mOrganizationList.add(new OrganizationData(type, companyName,
+ departmentName, titleName, isPrimary));
}
+
+ private static final List<String> sEmptyList = new ArrayList<String>(0);
- private void addIm(int type, String data, String label, boolean isPrimary) {
+ /**
+ * Set "ORG" related values to the appropriate data. If there's more than one
+ * OrganizationData objects, this input data are attached to the last one which does not
+ * have valid values (not including empty but only null). If there's no
+ * OrganizationData object, a new OrganizationData is created, whose title is set to null.
+ */
+ private void handleOrgValue(final int type, List<String> orgList, boolean isPrimary) {
+ if (orgList == null) {
+ orgList = sEmptyList;
+ }
+ final String companyName;
+ final String departmentName;
+ final int size = orgList.size();
+ switch (size) {
+ case 0: {
+ companyName = "";
+ departmentName = null;
+ break;
+ }
+ case 1: {
+ companyName = orgList.get(0);
+ departmentName = null;
+ break;
+ }
+ default: { // More than 1.
+ companyName = orgList.get(0);
+ // We're not sure which is the correct string for department.
+ // In order to keep all the data, concatinate the rest of elements.
+ StringBuilder builder = new StringBuilder();
+ for (int i = 1; i < size; i++) {
+ if (i > 1) {
+ builder.append(' ');
+ }
+ builder.append(orgList.get(i));
+ }
+ departmentName = builder.toString();
+ }
+ }
+ if (mOrganizationList == null) {
+ // Create new first organization entry, with "null" title which may be
+ // added via handleTitleValue().
+ addNewOrganization(type, companyName, departmentName, null, isPrimary);
+ return;
+ }
+ for (OrganizationData organizationData : mOrganizationList) {
+ // Not use TextUtils.isEmpty() since ORG was set but the elements might be empty.
+ // e.g. "ORG;PREF:;" -> Both companyName and departmentName become empty but not null.
+ if (organizationData.companyName == null &&
+ organizationData.departmentName == null) {
+ // Probably the "TITLE" property comes before the "ORG" property via
+ // handleTitleLine().
+ organizationData.companyName = companyName;
+ organizationData.departmentName = departmentName;
+ organizationData.isPrimary = isPrimary;
+ return;
+ }
+ }
+ // No OrganizatioData is available. Create another one, with "null" title, which may be
+ // added via handleTitleValue().
+ addNewOrganization(type, companyName, departmentName, null, isPrimary);
+ }
+
+ private final static int DEFAULT_ORGANIZATION_TYPE = Organization.TYPE_WORK;
+
+ /**
+ * Set "title" value to the appropriate data. If there's more than one
+ * OrganizationData objects, this input is attached to the last one which does not
+ * have valid title value (not including empty but only null). If there's no
+ * OrganizationData object, a new OrganizationData is created, whose company name is
+ * set to null.
+ */
+ private void handleTitleValue(final String title) {
+ if (mOrganizationList == null) {
+ // Create new first organization entry, with "null" other info, which may be
+ // added via handleOrgValue().
+ addNewOrganization(DEFAULT_ORGANIZATION_TYPE, null, null, title, false);
+ return;
+ }
+ for (OrganizationData organizationData : mOrganizationList) {
+ if (organizationData.titleName == null) {
+ organizationData.titleName = title;
+ return;
+ }
+ }
+ // No Organization is available. Create another one, with "null" other info, which may be
+ // added via handleOrgValue().
+ addNewOrganization(DEFAULT_ORGANIZATION_TYPE, null, null, title, false);
+ }
+
+ private void addIm(int protocol, String customProtocol, int type,
+ String propValue, boolean isPrimary) {
if (mImList == null) {
mImList = new ArrayList<ImData>();
}
- mImList.add(new ImData(type, data, label, isPrimary));
+ mImList.add(new ImData(protocol, customProtocol, type, propValue, isPrimary));
}
private void addNote(final String note) {
@@ -693,43 +641,14 @@
mNoteList.add(note);
}
- private void addPhotoBytes(String formatName, byte[] photoBytes) {
+ private void addPhotoBytes(String formatName, byte[] photoBytes, boolean isPrimary) {
if (mPhotoList == null) {
mPhotoList = new ArrayList<PhotoData>(1);
}
- final PhotoData photoData = new PhotoData(0, null, photoBytes);
+ final PhotoData photoData = new PhotoData(0, null, photoBytes, isPrimary);
mPhotoList.add(photoData);
}
- /**
- * 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(ContactsContract.CommonDataKinds.Organization.TYPE_OTHER,
- "", null, false);
- size = 1;
- }
- OrganizationData lastData = mOrganizationList.get(size - 1);
- lastData.positionName = positionValue;
- }
-
@SuppressWarnings("fallthrough")
private void handleNProperty(List<String> elems) {
// Family, Given, Middle, Prefix, Suffix. (1 - 5)
@@ -755,7 +674,7 @@
mFamilyName = elems.get(0);
}
}
-
+
/**
* Some Japanese mobile phones use this field for phonetic name,
* since vCard 2.1 does not have "SORT-STRING" type.
@@ -796,28 +715,36 @@
}
final String propValue = listToString(propValueList).trim();
- if (propName.equals("VERSION")) {
+ if (propName.equals(Constants.PROPERTY_VERSION)) {
// vCard version. Ignore this.
- } else if (propName.equals("FN")) {
+ } else if (propName.equals(Constants.PROPERTY_FN)) {
mFullName = propValue;
- } else if (propName.equals("NAME") && mFullName == null) {
+ } else if (propName.equals(Constants.PROPERTY_NAME) && mFullName == null) {
// Only in vCard 3.0. Use this if FN, which must exist in vCard 3.0 but may not
// actually exist in the real vCard data, does not exist.
mFullName = propValue;
- } else if (propName.equals("N")) {
+ } else if (propName.equals(Constants.PROPERTY_N)) {
handleNProperty(propValueList);
- } else if (propName.equals("SORT-STRING")) {
+ } else if (propName.equals(Constants.PROPERTY_NICKNAME)) {
mPhoneticFullName = propValue;
- } else if (propName.equals("NICKNAME") || propName.equals("X-NICKNAME")) {
+ } else if (propName.equals(Constants.PROPERTY_NICKNAME) ||
+ propName.equals(Constants.ImportOnly.PROPERTY_X_NICKNAME)) {
addNickName(propValue);
- } else if (propName.equals("SOUND")) {
+ } else if (propName.equals(Constants.PROPERTY_SOUND)) {
Collection<String> typeCollection = paramMap.get(Constants.ATTR_TYPE);
if (typeCollection != null && typeCollection.contains(Constants.ATTR_TYPE_X_IRMC_N)) {
- handlePhoneticNameFromSound(propValueList);
+ // As of 2009-10-08, Parser side does not split a property value into separated
+ // values using ';' (in other words, propValueList.size() == 1),
+ // which is correct behavior from the view of vCard 2.1.
+ // But we want it to be separated, so do the separation here.
+ final List<String> phoneticNameList =
+ VCardUtils.constructListFromValue(propValue,
+ VCardConfig.isV30(mVCardType));
+ handlePhoneticNameFromSound(phoneticNameList);
} else {
// Ignore this field since Android cannot understand what it is.
}
- } else if (propName.equals("ADR")) {
+ } else if (propName.equals(Constants.PROPERTY_ADR)) {
boolean valuesAreAllEmpty = true;
for (String value : propValueList) {
if (value.length() > 0) {
@@ -836,23 +763,21 @@
if (typeCollection != null) {
for (String typeString : typeCollection) {
typeString = typeString.toUpperCase();
- if (typeString.equals(Constants.ATTR_TYPE_PREF) && !mPrefIsSet_Address) {
- // Only first "PREF" is considered.
- mPrefIsSet_Address = true;
+ if (typeString.equals(Constants.ATTR_TYPE_PREF)) {
isPrimary = true;
} else if (typeString.equals(Constants.ATTR_TYPE_HOME)) {
type = StructuredPostal.TYPE_HOME;
label = "";
} else if (typeString.equals(Constants.ATTR_TYPE_WORK) ||
- typeString.equalsIgnoreCase("COMPANY")) {
+ typeString.equalsIgnoreCase(Constants.ATTR_EXTRA_TYPE_COMPANY)) {
// "COMPANY" seems emitted by Windows Mobile, which is not
// specifically supported by vCard 2.1. We assume this is same
// as "WORK".
type = StructuredPostal.TYPE_WORK;
label = "";
- } else if (typeString.equals("PARCEL") ||
- typeString.equals("DOM") ||
- typeString.equals("INTL")) {
+ } else if (typeString.equals(Constants.ATTR_ADR_TYPE_PARCEL) ||
+ typeString.equals(Constants.ATTR_ADR_TYPE_DOM) ||
+ typeString.equals(Constants.ATTR_ADR_TYPE_INTL)) {
// We do not have any appropriate way to store this information.
} else {
if (typeString.startsWith("X-") && type < 0) {
@@ -871,7 +796,7 @@
}
addPostal(type, propValueList, label, isPrimary);
- } else if (propName.equals("EMAIL")) {
+ } else if (propName.equals(Constants.PROPERTY_EMAIL)) {
int type = -1;
String label = null;
boolean isPrimary = false;
@@ -879,9 +804,7 @@
if (typeCollection != null) {
for (String typeString : typeCollection) {
typeString = typeString.toUpperCase();
- if (typeString.equals(Constants.ATTR_TYPE_PREF) && !mPrefIsSet_Email) {
- // Only first "PREF" is considered.
- mPrefIsSet_Email = true;
+ if (typeString.equals(Constants.ATTR_TYPE_PREF)) {
isPrimary = true;
} else if (typeString.equals(Constants.ATTR_TYPE_HOME)) {
type = Email.TYPE_HOME;
@@ -905,50 +828,47 @@
type = Email.TYPE_OTHER;
}
addEmail(type, propValue, label, isPrimary);
- } else if (propName.equals("ORG")) {
+ } else if (propName.equals(Constants.PROPERTY_ORG)) {
// vCard specification does not specify other types.
- int type = Organization.TYPE_WORK;
+ final int type = Organization.TYPE_WORK;
boolean isPrimary = false;
-
Collection<String> typeCollection = paramMap.get(Constants.ATTR_TYPE);
if (typeCollection != null) {
for (String typeString : typeCollection) {
- if (typeString.equals(Constants.ATTR_TYPE_PREF) && !mPrefIsSet_Organization) {
- // vCard specification officially does not have PREF in ORG.
- // This is just for safety.
- mPrefIsSet_Organization = true;
+ if (typeString.equals(Constants.ATTR_TYPE_PREF)) {
isPrimary = true;
}
}
}
-
- 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")) {
- String formatName = null;
- Collection<String> typeCollection = paramMap.get("TYPE");
- if (typeCollection != null) {
- formatName = typeCollection.iterator().next();
- }
+ handleOrgValue(type, propValueList, isPrimary);
+ } else if (propName.equals(Constants.PROPERTY_TITLE)) {
+ handleTitleValue(propValue);
+ } else if (propName.equals(Constants.PROPERTY_ROLE)) {
+ // This conflicts with TITLE. Ignore for now...
+ // handleTitleValue(propValue);
+ } else if (propName.equals(Constants.PROPERTY_PHOTO) ||
+ propName.equals(Constants.PROPERTY_LOGO)) {
Collection<String> paramMapValue = paramMap.get("VALUE");
if (paramMapValue != null && paramMapValue.contains("URL")) {
// Currently we do not have appropriate example for testing this case.
} else {
- addPhotoBytes(formatName, propBytes);
+ final Collection<String> typeCollection = paramMap.get("TYPE");
+ String formatName = null;
+ boolean isPrimary = false;
+ if (typeCollection != null) {
+ for (String typeValue : typeCollection) {
+ if (Constants.ATTR_TYPE_PREF.equals(typeValue)) {
+ isPrimary = true;
+ } else if (formatName == null){
+ formatName = typeValue;
+ }
+ }
+ }
+ addPhotoBytes(formatName, propBytes, isPrimary);
}
- } else if (propName.equals("TEL")) {
- Collection<String> typeCollection = paramMap.get(Constants.ATTR_TYPE);
- Object typeObject = VCardUtils.getPhoneTypeFromStrings(typeCollection);
+ } else if (propName.equals(Constants.PROPERTY_TEL)) {
+ final Collection<String> typeCollection = paramMap.get(Constants.ATTR_TYPE);
+ final Object typeObject = VCardUtils.getPhoneTypeFromStrings(typeCollection);
final int type;
final String label;
if (typeObject instanceof Integer) {
@@ -960,9 +880,7 @@
}
final boolean isPrimary;
- if (!mPrefIsSet_Phone && typeCollection != null &&
- typeCollection.contains(Constants.ATTR_TYPE_PREF)) {
- mPrefIsSet_Phone = true;
+ if (typeCollection != null && typeCollection.contains(Constants.ATTR_TYPE_PREF)) {
isPrimary = true;
} else {
isPrimary = false;
@@ -975,47 +893,48 @@
int type = Phone.TYPE_OTHER;
final String label = null;
final boolean isPrimary;
- if (!mPrefIsSet_Phone && typeCollection != null &&
- typeCollection.contains(Constants.ATTR_TYPE_PREF)) {
- mPrefIsSet_Phone = true;
+ if (typeCollection != null && typeCollection.contains(Constants.ATTR_TYPE_PREF)) {
isPrimary = true;
} else {
isPrimary = false;
}
addPhone(type, propValue, label, isPrimary);
- } else if (sImMap.containsKey(propName)){
- int type = sImMap.get(propName);
+ } else if (sImMap.containsKey(propName)) {
+ final int protocol = sImMap.get(propName);
boolean isPrimary = false;
+ int type = -1;
final Collection<String> typeCollection = paramMap.get(Constants.ATTR_TYPE);
if (typeCollection != null) {
for (String typeString : typeCollection) {
if (typeString.equals(Constants.ATTR_TYPE_PREF)) {
isPrimary = true;
- } else if (typeString.equalsIgnoreCase(Constants.ATTR_TYPE_HOME)) {
- type = Phone.TYPE_HOME;
- } else if (typeString.equalsIgnoreCase(Constants.ATTR_TYPE_WORK)) {
- type = Phone.TYPE_WORK;
+ } else if (type < 0) {
+ if (typeString.equalsIgnoreCase(Constants.ATTR_TYPE_HOME)) {
+ type = Im.TYPE_HOME;
+ } else if (typeString.equalsIgnoreCase(Constants.ATTR_TYPE_WORK)) {
+ type = Im.TYPE_WORK;
+ }
}
}
}
if (type < 0) {
type = Phone.TYPE_HOME;
}
- addIm(type, propValue, null, isPrimary);
- } else if (propName.equals("NOTE")) {
+ addIm(protocol, null, type, propValue, isPrimary);
+ } else if (propName.equals(Constants.PROPERTY_NOTE)) {
addNote(propValue);
- } else if (propName.equals("URL")) {
+ } else if (propName.equals(Constants.PROPERTY_URL)) {
if (mWebsiteList == null) {
mWebsiteList = new ArrayList<String>(1);
}
mWebsiteList.add(propValue);
- } else if (propName.equals("X-PHONETIC-FIRST-NAME")) {
+ } else if (propName.equals(Constants.PROPERTY_X_PHONETIC_FIRST_NAME)) {
mPhoneticGivenName = propValue;
- } else if (propName.equals("X-PHONETIC-MIDDLE-NAME")) {
+ } else if (propName.equals(Constants.PROPERTY_X_PHONETIC_MIDDLE_NAME)) {
mPhoneticMiddleName = propValue;
- } else if (propName.equals("X-PHONETIC-LAST-NAME")) {
+ } else if (propName.equals(Constants.PROPERTY_X_PHONETIC_LAST_NAME)) {
mPhoneticFamilyName = propValue;
- } else if (propName.equals("BDAY")) {
+ } else if (propName.equals(Constants.PROPERTY_BDAY)) {
mBirthday = propValue;
/*} else if (propName.equals("REV")) {
// Revision of this VCard entry. I think we can ignore this.
@@ -1048,7 +967,10 @@
* Construct the display name. The constructed data must not be null.
*/
private void constructDisplayName() {
- if (!(TextUtils.isEmpty(mFamilyName) && TextUtils.isEmpty(mGivenName))) {
+ // FullName (created via "FN" or "NAME" field) is prefered.
+ if (!TextUtils.isEmpty(mFullName)) {
+ mDisplayName = mFullName;
+ } else if (!(TextUtils.isEmpty(mFamilyName) && TextUtils.isEmpty(mGivenName))) {
StringBuilder builder = new StringBuilder();
List<String> nameList;
switch (VCardConfig.getNameOrderType(mVCardType)) {
@@ -1079,8 +1001,6 @@
}
}
mDisplayName = builder.toString();
- } else if (!TextUtils.isEmpty(mFullName)) {
- mDisplayName = mFullName;
} else if (!(TextUtils.isEmpty(mPhoneticFamilyName) &&
TextUtils.isEmpty(mPhoneticGivenName))) {
mDisplayName = VCardUtils.constructNameFromElements(mVCardType,
@@ -1103,25 +1023,10 @@
*/
public void consolidateFields() {
constructDisplayName();
-
+
if (mPhoneticFullName != null) {
mPhoneticFullName = mPhoneticFullName.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 && mPostalList != null && mPostalList.size() > 0) {
- mPostalList.get(0).isPrimary = true;
- }
- if (!mPrefIsSet_Email && mEmailList != null && mEmailList.size() > 0) {
- mEmailList.get(0).isPrimary = true;
- }
- if (!mPrefIsSet_Organization && mOrganizationList != null && mOrganizationList.size() > 0) {
- mOrganizationList.get(0).isPrimary = true;
- }
}
// From GoogleSource.java in Contacts app.
@@ -1181,22 +1086,16 @@
}
if (mNickNameList != null && mNickNameList.size() > 0) {
- boolean first = true;
for (String nickName : mNickNameList) {
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
builder.withValueBackReference(Nickname.RAW_CONTACT_ID, 0);
builder.withValue(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE);
-
builder.withValue(Nickname.TYPE, Nickname.TYPE_DEFAULT);
builder.withValue(Nickname.NAME, nickName);
- if (first) {
- builder.withValue(Data.IS_PRIMARY, 1);
- first = false;
- }
operationList.add(builder.build());
}
}
-
+
if (mPhoneList != null) {
for (PhoneData phoneData : mPhoneList) {
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
@@ -1209,30 +1108,34 @@
}
builder.withValue(Phone.NUMBER, phoneData.data);
if (phoneData.isPrimary) {
- builder.withValue(Data.IS_PRIMARY, 1);
+ builder.withValue(Phone.IS_PRIMARY, 1);
}
operationList.add(builder.build());
}
}
-
+
if (mOrganizationList != null) {
- boolean first = true;
for (OrganizationData organizationData : mOrganizationList) {
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
builder.withValueBackReference(Organization.RAW_CONTACT_ID, 0);
builder.withValue(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
-
- // Currently, we do not use TYPE_CUSTOM.
builder.withValue(Organization.TYPE, organizationData.type);
- builder.withValue(Organization.COMPANY, organizationData.companyName);
- builder.withValue(Organization.TITLE, organizationData.positionName);
- if (first) {
- builder.withValue(Data.IS_PRIMARY, 1);
+ if (organizationData.companyName != null) {
+ builder.withValue(Organization.COMPANY, organizationData.companyName);
+ }
+ if (organizationData.departmentName != null) {
+ builder.withValue(Organization.DEPARTMENT, organizationData.departmentName);
+ }
+ if (organizationData.titleName != null) {
+ builder.withValue(Organization.TITLE, organizationData.titleName);
+ }
+ if (organizationData.isPrimary) {
+ builder.withValue(Organization.IS_PRIMARY, 1);
}
operationList.add(builder.build());
}
}
-
+
if (mEmailList != null) {
for (EmailData emailData : mEmailList) {
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
@@ -1265,12 +1168,11 @@
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
builder.withValueBackReference(Im.RAW_CONTACT_ID, 0);
builder.withValue(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
-
builder.withValue(Im.TYPE, imData.type);
- if (imData.type == Im.TYPE_CUSTOM) {
- builder.withValue(Im.LABEL, imData.label);
+ builder.withValue(Im.PROTOCOL, imData.protocol);
+ if (imData.protocol == Im.PROTOCOL_CUSTOM) {
+ builder.withValue(Im.CUSTOM_PROTOCOL, imData.customProtocol);
}
- builder.withValue(Im.DATA, imData.data);
if (imData.isPrimary) {
builder.withValue(Data.IS_PRIMARY, 1);
}
@@ -1282,22 +1184,19 @@
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
builder.withValueBackReference(Note.RAW_CONTACT_ID, 0);
builder.withValue(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE);
-
builder.withValue(Note.NOTE, note);
operationList.add(builder.build());
}
}
-
+
if (mPhotoList != null) {
- boolean first = true;
for (PhotoData photoData : mPhotoList) {
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
builder.withValueBackReference(Photo.RAW_CONTACT_ID, 0);
builder.withValue(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
builder.withValue(Photo.PHOTO, photoData.photoBytes);
- if (first) {
- builder.withValue(Data.IS_PRIMARY, 1);
- first = false;
+ if (photoData.isPrimary) {
+ builder.withValue(Photo.IS_PRIMARY, 1);
}
operationList.add(builder.build());
}
@@ -1310,12 +1209,12 @@
builder.withValue(Data.MIMETYPE, Website.CONTENT_ITEM_TYPE);
builder.withValue(Website.URL, website);
// There's no information about the type of URL in vCard.
- // We use TYPE_HOME for safety.
- builder.withValue(Website.TYPE, Website.TYPE_HOME);
+ // We use TYPE_HOMEPAGE for safety.
+ builder.withValue(Website.TYPE, Website.TYPE_HOMEPAGE);
operationList.add(builder.build());
}
}
-
+
if (!TextUtils.isEmpty(mBirthday)) {
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
builder.withValueBackReference(Event.RAW_CONTACT_ID, 0);
@@ -1364,4 +1263,99 @@
return "";
}
}
+
+ // All getter methods should be used carefully, since they may change
+ // in the future as of 2009-10-05, on which I cannot be sure this structure
+ // is completely consolidated.
+ //
+ // Also note that these getter methods should be used only after
+ // all properties being pushed into this object. If not, incorrect
+ // value will "be stored in the local cache and" be returned to you.
+
+ public String getFamilyName() {
+ return mFamilyName;
+ }
+
+ public String getGivenName() {
+ return mGivenName;
+ }
+
+ public String getMiddleName() {
+ return mMiddleName;
+ }
+
+ public String getPrefix() {
+ return mPrefix;
+ }
+
+ public String getSuffix() {
+ return mSuffix;
+ }
+
+ public String getFullName() {
+ return mFullName;
+ }
+
+ public String getPhoneticFamilyName() {
+ return mPhoneticFamilyName;
+ }
+
+ public String getPhoneticGivenName() {
+ return mPhoneticGivenName;
+ }
+
+ public String getPhoneticMiddleName() {
+ return mPhoneticMiddleName;
+ }
+
+ public String getPhoneticFullName() {
+ return mPhoneticFullName;
+ }
+
+ public final List<String> getNickNameList() {
+ return mNickNameList;
+ }
+
+ public String getBirthday() {
+ return mBirthday;
+ }
+
+ public final List<String> getNotes() {
+ return mNoteList;
+ }
+
+ public final List<PhoneData> getPhoneList() {
+ return mPhoneList;
+ }
+
+ public final List<EmailData> getEmailList() {
+ return mEmailList;
+ }
+
+ public final List<PostalData> getPostalList() {
+ return mPostalList;
+ }
+
+ public final List<OrganizationData> getOrganizationList() {
+ return mOrganizationList;
+ }
+
+ public final List<ImData> getImList() {
+ return mImList;
+ }
+
+ public final List<PhotoData> getPhotoList() {
+ return mPhotoList;
+ }
+
+ public final List<String> getWebsiteList() {
+ return mWebsiteList;
+ }
+
+ public String getDisplayName() {
+ if (mDisplayName == null) {
+ constructDisplayName();
+ }
+ return mDisplayName;
+ }
}
diff --git a/core/java/android/pim/vcard/VCardComposer.java b/core/java/android/pim/vcard/VCardComposer.java
index c4711f8..c5afb73 100644
--- a/core/java/android/pim/vcard/VCardComposer.java
+++ b/core/java/android/pim/vcard/VCardComposer.java
@@ -72,19 +72,38 @@
* Usually, this class should be used like this.
* </p>
*
- * <pre class="prettyprint"> VCardComposer composer = null; try { composer = new
- * VCardComposer(context); composer.addHandler(composer.new
- * HandlerForOutputStream(outputStream)); if (!composer.init()) { // Do
- * something handling the situation. return; } while (!composer.isAfterLast()) {
- * if (mCanceled) { // Assume a user may cancel this operation during the
- * export. return; } if (!composer.createOneEntry()) { // Do something handling
- * the error situation. return; } } } finally { if (composer != null) {
- * composer.terminate(); } } </pre>
+ * <pre class="prettyprint">VCardComposer composer = null;
+ * try {
+ * composer = new VCardComposer(context);
+ * composer.addHandler(
+ * composer.new HandlerForOutputStream(outputStream));
+ * if (!composer.init()) {
+ * // Do something handling the situation.
+ * return;
+ * }
+ * while (!composer.isAfterLast()) {
+ * if (mCanceled) {
+ * // Assume a user may cancel this operation during the export.
+ * return;
+ * }
+ * if (!composer.createOneEntry()) {
+ * // Do something handling the error situation.
+ * return;
+ * }
+ * }
+ * } finally {
+ * if (composer != null) {
+ * composer.terminate();
+ * }
+ * } </pre>
*/
public class VCardComposer {
private static final String LOG_TAG = "vcard.VCardComposer";
- private static final String DEFAULT_EMAIL_TYPE = Constants.ATTR_TYPE_INTERNET;
+ // TODO: Should be configurable?
+ public static final int DEFAULT_PHONE_TYPE = Phone.TYPE_HOME;
+ public static final int DEFAULT_POSTAL_TYPE = StructuredPostal.TYPE_HOME;
+ public static final int DEFAULT_EMAIL_TYPE = Email.TYPE_OTHER;
public static final String FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO =
"Failed to get database information";
@@ -95,28 +114,83 @@
public static final String FAILURE_REASON_NOT_INITIALIZED =
"The vCard composer object is not correctly initialized";
+ /** Should be visible only from developers... (no need to translate, hopefully) */
+ public static final String FAILURE_REASON_UNSUPPORTED_URI =
+ "The Uri vCard composer received is not supported by the composer.";
+
public static final String NO_ERROR = "No error";
+ public static final String VCARD_TYPE_STRING_DOCOMO = "docomo";
+
+ // Property for call log entry
+ private static final String VCARD_PROPERTY_X_TIMESTAMP = "X-IRMC-CALL-DATETIME";
+ private static final String VCARD_PROPERTY_CALLTYPE_INCOMING = "INCOMING";
+ private static final String VCARD_PROPERTY_CALLTYPE_OUTGOING = "OUTGOING";
+ private static final String VCARD_PROPERTY_CALLTYPE_MISSED = "MISSED";
+
+ private static final String VCARD_DATA_VCARD = "VCARD";
+ private static final String VCARD_DATA_PUBLIC = "PUBLIC";
+
+ private static final String VCARD_ATTR_SEPARATOR = ";";
+ private static final String VCARD_COL_SEPARATOR = "\r\n";
+ private static final String VCARD_DATA_SEPARATOR = ":";
+ private static final String VCARD_ITEM_SEPARATOR = ";";
+ private static final String VCARD_WS = " ";
+ private static final String VCARD_ATTR_EQUAL = "=";
+
+ private static final String VCARD_ATTR_ENCODING_QP = "ENCODING=QUOTED-PRINTABLE";
+
+ private static final String VCARD_ATTR_ENCODING_BASE64_V21 = "ENCODING=BASE64";
+ private static final String VCARD_ATTR_ENCODING_BASE64_V30 = "ENCODING=b";
+
+ private static final String SHIFT_JIS = "SHIFT_JIS";
+
+ /**
+ * Special URI for testing.
+ */
+ public static final String VCARD_TEST_AUTHORITY = "com.android.unit_tests.vcard";
+ public static final Uri VCARD_TEST_AUTHORITY_URI =
+ Uri.parse("content://" + VCARD_TEST_AUTHORITY);
+ public static final Uri CONTACTS_TEST_CONTENT_URI =
+ Uri.withAppendedPath(VCARD_TEST_AUTHORITY_URI, "contacts");
+
private static final Uri sDataRequestUri;
+ private static final Map<Integer, String> sImMap;
+
+ /**
+ * See the comment in {@link VCardConfig#FLAG_REFRAIN_QP_TO_PRIMARY_PROPERTIES}.
+ */
+ private static final Set<String> sPrimaryPropertyNameSet;
static {
Uri.Builder builder = RawContacts.CONTENT_URI.buildUpon();
builder.appendQueryParameter(Data.FOR_EXPORT_ONLY, "1");
sDataRequestUri = builder.build();
+ sImMap = new HashMap<Integer, String>();
+ sImMap.put(Im.PROTOCOL_AIM, Constants.PROPERTY_X_AIM);
+ sImMap.put(Im.PROTOCOL_MSN, Constants.PROPERTY_X_MSN);
+ sImMap.put(Im.PROTOCOL_YAHOO, Constants.PROPERTY_X_YAHOO);
+ sImMap.put(Im.PROTOCOL_ICQ, Constants.PROPERTY_X_ICQ);
+ sImMap.put(Im.PROTOCOL_JABBER, Constants.PROPERTY_X_JABBER);
+ sImMap.put(Im.PROTOCOL_SKYPE, Constants.PROPERTY_X_SKYPE_USERNAME);
+ // Google talk is a special case.
+
+ // TODO: incomplete. Implement properly
+ sPrimaryPropertyNameSet = new HashSet<String>();
+ sPrimaryPropertyNameSet.add(Constants.PROPERTY_N);
+ sPrimaryPropertyNameSet.add(Constants.PROPERTY_FN);
+ sPrimaryPropertyNameSet.add(Constants.PROPERTY_SOUND);
}
public static interface OneEntryHandler {
public boolean onInit(Context context);
-
public boolean onEntryCreated(String vcard);
-
public void onTerminate();
}
/**
* <p>
- * An useful example handler, which emits VCard String to outputstream one
- * by one.
+ * An useful example handler, which emits VCard String to outputstream one by one.
* </p>
* <p>
* The input OutputStream object is closed() on {{@link #onTerminate()}.
@@ -211,65 +285,6 @@
}
}
- public static final String VCARD_TYPE_STRING_DOCOMO = "docomo";
-
- private static final String VCARD_PROPERTY_ADR = "ADR";
- private static final String VCARD_PROPERTY_BEGIN = "BEGIN";
- private static final String VCARD_PROPERTY_EMAIL = "EMAIL";
- private static final String VCARD_PROPERTY_END = "END";
- private static final String VCARD_PROPERTY_NAME = "N";
- private static final String VCARD_PROPERTY_FULL_NAME = "FN";
- private static final String VCARD_PROPERTY_NOTE = "NOTE";
- private static final String VCARD_PROPERTY_ORG = "ORG";
- private static final String VCARD_PROPERTY_SOUND = "SOUND";
- private static final String VCARD_PROPERTY_SORT_STRING = "SORT-STRING";
- private static final String VCARD_PROPERTY_NICKNAME = "NICKNAME";
- private static final String VCARD_PROPERTY_TEL = "TEL";
- private static final String VCARD_PROPERTY_TITLE = "TITLE";
- private static final String VCARD_PROPERTY_PHOTO = "PHOTO";
- private static final String VCARD_PROPERTY_VERSION = "VERSION";
- private static final String VCARD_PROPERTY_URL = "URL";
- private static final String VCARD_PROPERTY_BIRTHDAY = "BDAY";
-
- private static final String VCARD_PROPERTY_X_PHONETIC_FIRST_NAME = "X-PHONETIC-FIRST-NAME";
- private static final String VCARD_PROPERTY_X_PHONETIC_MIDDLE_NAME = "X-PHONETIC-MIDDLE-NAME";
- private static final String VCARD_PROPERTY_X_PHONETIC_LAST_NAME = "X-PHONETIC-LAST-NAME";
-
- // Android specific properties
- // TODO: ues extra MIME-TYPE instead of adding this kind of inflexible fields
- private static final String VCARD_PROPERTY_X_NICKNAME = "X-NICKNAME";
-
- // Property for call log entry
- private static final String VCARD_PROPERTY_X_TIMESTAMP = "X-IRMC-CALL-DATETIME";
- private static final String VCARD_PROPERTY_CALLTYPE_INCOMING = "INCOMING";
- private static final String VCARD_PROPERTY_CALLTYPE_OUTGOING = "OUTGOING";
- private static final String VCARD_PROPERTY_CALLTYPE_MISSED = "MISSED";
-
- // Properties for DoCoMo vCard.
- private static final String VCARD_PROPERTY_X_CLASS = "X-CLASS";
- private static final String VCARD_PROPERTY_X_REDUCTION = "X-REDUCTION";
- private static final String VCARD_PROPERTY_X_NO = "X-NO";
- private static final String VCARD_PROPERTY_X_DCM_HMN_MODE = "X-DCM-HMN-MODE";
-
- private static final String VCARD_DATA_VCARD = "VCARD";
- private static final String VCARD_DATA_PUBLIC = "PUBLIC";
-
- private static final String VCARD_ATTR_SEPARATOR = ";";
- private static final String VCARD_COL_SEPARATOR = "\r\n";
- private static final String VCARD_DATA_SEPARATOR = ":";
- private static final String VCARD_ITEM_SEPARATOR = ";";
- private static final String VCARD_WS = " ";
- private static final String VCARD_ATTR_EQUAL = "=";
-
- // Type strings are now in VCardConstants.java.
-
- private static final String VCARD_ATTR_ENCODING_QP = "ENCODING=QUOTED-PRINTABLE";
-
- private static final String VCARD_ATTR_ENCODING_BASE64_V21 = "ENCODING=BASE64";
- private static final String VCARD_ATTR_ENCODING_BASE64_V30 = "ENCODING=b";
-
- private static final String SHIFT_JIS = "SHIFT_JIS";
-
private final Context mContext;
private final int mVCardType;
private final boolean mCareHandlerErrors;
@@ -286,7 +301,9 @@
private final boolean mUsesDefactProperty;
private final boolean mUsesUtf8;
private final boolean mUsesShiftJis;
- private final boolean mUsesQPToPrimaryProperties;
+ private final boolean mAppendTypeParamName;
+ private final boolean mRefrainsQPToPrimaryProperties;
+ private final boolean mNeedsToConvertPhoneticString;
private Cursor mCursor;
private int mIdColumn;
@@ -298,20 +315,7 @@
private String mErrorReason = NO_ERROR;
- private static final Map<Integer, String> sImMap;
-
- static {
- sImMap = new HashMap<Integer, String>();
- sImMap.put(Im.PROTOCOL_AIM, Constants.PROPERTY_X_AIM);
- sImMap.put(Im.PROTOCOL_MSN, Constants.PROPERTY_X_MSN);
- sImMap.put(Im.PROTOCOL_YAHOO, Constants.PROPERTY_X_YAHOO);
- sImMap.put(Im.PROTOCOL_ICQ, Constants.PROPERTY_X_ICQ);
- sImMap.put(Im.PROTOCOL_JABBER, Constants.PROPERTY_X_JABBER);
- sImMap.put(Im.PROTOCOL_SKYPE, Constants.PROPERTY_X_SKYPE_USERNAME);
- // Google talk is a special case.
- }
-
- private boolean mIsCallLogComposer = false;
+ private boolean mIsCallLogComposer;
private static final String[] sContactsProjection = new String[] {
Contacts._ID,
@@ -332,30 +336,24 @@
private static final String FLAG_TIMEZONE_UTC = "Z";
public VCardComposer(Context context) {
- this(context, VCardConfig.VCARD_TYPE_DEFAULT, true, false);
+ this(context, VCardConfig.VCARD_TYPE_DEFAULT, true);
}
- public VCardComposer(Context context, String vcardTypeStr,
- boolean careHandlerErrors) {
- this(context, VCardConfig.getVCardTypeFromString(vcardTypeStr),
- careHandlerErrors, false);
+ public VCardComposer(Context context, int vcardType) {
+ this(context, vcardType, true);
}
- public VCardComposer(Context context, int vcardType, boolean careHandlerErrors) {
- this(context, vcardType, careHandlerErrors, false);
+ public VCardComposer(Context context, String vcardTypeStr, boolean careHandlerErrors) {
+ this(context, VCardConfig.getVCardTypeFromString(vcardTypeStr), careHandlerErrors);
}
/**
* Construct for supporting call log entry vCard composing.
- *
- * @param isCallLogComposer true if this composer is for creating Call Log vCard.
*/
- public VCardComposer(Context context, int vcardType, boolean careHandlerErrors,
- boolean isCallLogComposer) {
+ public VCardComposer(Context context, int vcardType, boolean careHandlerErrors) {
mContext = context;
mVCardType = vcardType;
mCareHandlerErrors = careHandlerErrors;
- mIsCallLogComposer = isCallLogComposer;
mContentResolver = context.getContentResolver();
mIsV30 = VCardConfig.isV30(vcardType);
@@ -370,7 +368,9 @@
mUsesDefactProperty = VCardConfig.usesDefactProperty(vcardType);
mUsesUtf8 = VCardConfig.usesUtf8(vcardType);
mUsesShiftJis = VCardConfig.usesShiftJis(vcardType);
- mUsesQPToPrimaryProperties = VCardConfig.usesQPToPrimaryProperties(vcardType);
+ mRefrainsQPToPrimaryProperties = VCardConfig.refrainsQPToPrimaryProperties(vcardType);
+ mAppendTypeParamName = VCardConfig.appendTypeParamName(vcardType);
+ mNeedsToConvertPhoneticString = VCardConfig.needsToConvertPhoneticString(vcardType);
mHandlerList = new ArrayList<OneEntryHandler>();
if (mIsDoCoMo) {
@@ -389,50 +389,32 @@
}
/**
- * This static function is to compose vCard for phone own number
- */
- public String composeVCardForPhoneOwnNumber(int phonetype, String phoneName,
- String phoneNumber, boolean vcardVer21) {
- final StringBuilder builder = new StringBuilder();
- appendVCardLine(builder, VCARD_PROPERTY_BEGIN, VCARD_DATA_VCARD);
- if (!vcardVer21) {
- appendVCardLine(builder, VCARD_PROPERTY_VERSION, Constants.VERSION_V30);
- } else {
- appendVCardLine(builder, VCARD_PROPERTY_VERSION, Constants.VERSION_V21);
- }
-
- boolean needCharset = false;
- if (!(VCardUtils.containsOnlyPrintableAscii(phoneName))) {
- needCharset = true;
- }
- // TODO: QP should be used? Using mUsesQPToPrimaryProperties should help.
- appendVCardLine(builder, VCARD_PROPERTY_FULL_NAME, phoneName, needCharset, false);
- appendVCardLine(builder, VCARD_PROPERTY_NAME, phoneName, needCharset, false);
-
- String label = Integer.toString(phonetype);
- appendVCardTelephoneLine(builder, phonetype, label, phoneNumber);
-
- appendVCardLine(builder, VCARD_PROPERTY_END, VCARD_DATA_VCARD);
-
- return builder.toString();
- }
-
- /**
* Must call before {{@link #init()}.
*/
public void addHandler(OneEntryHandler handler) {
mHandlerList.add(handler);
}
- public boolean init() {
- return init(null, null);
- }
-
/**
* @return Returns true when initialization is successful and all the other
* methods are available. Returns false otherwise.
*/
+ public boolean init() {
+ return init(null, null);
+ }
+
public boolean init(final String selection, final String[] selectionArgs) {
+ return init(Contacts.CONTENT_URI, selection, selectionArgs, null);
+ }
+
+ /**
+ * Note that this is unstable interface, may be deleted in the future.
+ */
+ public boolean init(final Uri contentUri, final String selection,
+ final String[] selectionArgs, final String sortOrder) {
+ if (contentUri == null) {
+ return false;
+ }
if (mCareHandlerErrors) {
List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
mHandlerList.size());
@@ -451,13 +433,19 @@
}
}
- if (mIsCallLogComposer) {
- mCursor = mContentResolver.query(CallLog.Calls.CONTENT_URI, sCallLogProjection,
- selection, selectionArgs, null);
+ final String[] projection;
+ if (CallLog.Calls.CONTENT_URI.equals(contentUri)) {
+ projection = sCallLogProjection;
+ mIsCallLogComposer = true;
+ } else if (Contacts.CONTENT_URI.equals(contentUri) ||
+ CONTACTS_TEST_CONTENT_URI.equals(contentUri)) {
+ projection = sContactsProjection;
} else {
- mCursor = mContentResolver.query(Contacts.CONTENT_URI, sContactsProjection,
- selection, selectionArgs, null);
+ mErrorReason = FAILURE_REASON_UNSUPPORTED_URI;
+ return false;
}
+ mCursor = mContentResolver.query(
+ contentUri, projection, selection, selectionArgs, sortOrder);
if (mCursor == null) {
mErrorReason = FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO;
@@ -534,89 +522,6 @@
return true;
}
- /**
- * Format according to RFC 2445 DATETIME type.
- * The format is: ("%Y%m%dT%H%M%SZ").
- */
- private final String toRfc2455Format(final long millSecs) {
- Time startDate = new Time();
- startDate.set(millSecs);
- String date = startDate.format2445();
- return date + FLAG_TIMEZONE_UTC;
- }
-
- /**
- * Try to append the property line for a call history time stamp field if possible.
- * Do nothing if the call log type gotton from the database is invalid.
- */
- private void tryAppendCallHistoryTimeStampField(final StringBuilder builder) {
- // Extension for call history as defined in
- // in the Specification for Ic Mobile Communcation - ver 1.1,
- // Oct 2000. This is used to send the details of the call
- // history - missed, incoming, outgoing along with date and time
- // to the requesting device (For example, transferring phone book
- // when connected over bluetooth)
- //
- // e.g. "X-IRMC-CALL-DATETIME;MISSED:20050320T100000Z"
- final int callLogType = mCursor.getInt(CALL_TYPE_COLUMN_INDEX);
- final String callLogTypeStr;
- switch (callLogType) {
- case Calls.INCOMING_TYPE: {
- callLogTypeStr = VCARD_PROPERTY_CALLTYPE_INCOMING;
- break;
- }
- case Calls.OUTGOING_TYPE: {
- callLogTypeStr = VCARD_PROPERTY_CALLTYPE_OUTGOING;
- break;
- }
- case Calls.MISSED_TYPE: {
- callLogTypeStr = VCARD_PROPERTY_CALLTYPE_MISSED;
- break;
- }
- default: {
- Log.w(LOG_TAG, "Call log type not correct.");
- return;
- }
- }
-
- final long dateAsLong = mCursor.getLong(DATE_COLUMN_INDEX);
- builder.append(VCARD_PROPERTY_X_TIMESTAMP);
- builder.append(VCARD_ATTR_SEPARATOR);
- appendTypeAttribute(builder, callLogTypeStr);
- builder.append(VCARD_DATA_SEPARATOR);
- builder.append(toRfc2455Format(dateAsLong));
- builder.append(VCARD_COL_SEPARATOR);
- }
-
- private String createOneCallLogEntryInternal() {
- final StringBuilder builder = new StringBuilder();
- appendVCardLine(builder, VCARD_PROPERTY_BEGIN, VCARD_DATA_VCARD);
- if (mIsV30) {
- appendVCardLine(builder, VCARD_PROPERTY_VERSION, Constants.VERSION_V30);
- } else {
- appendVCardLine(builder, VCARD_PROPERTY_VERSION, Constants.VERSION_V21);
- }
- String name = mCursor.getString(CALLER_NAME_COLUMN_INDEX);
- if (TextUtils.isEmpty(name)) {
- name = mCursor.getString(NUMBER_COLUMN_INDEX);
- }
- final boolean needCharset = !(VCardUtils.containsOnlyPrintableAscii(name));
- // TODO: QP should be used? Using mUsesQPToPrimaryProperties should help.
- appendVCardLine(builder, VCARD_PROPERTY_FULL_NAME, name, needCharset, false);
- appendVCardLine(builder, VCARD_PROPERTY_NAME, name, needCharset, false);
-
- String number = mCursor.getString(NUMBER_COLUMN_INDEX);
- int type = mCursor.getInt(CALLER_NUMBERTYPE_COLUMN_INDEX);
- String label = mCursor.getString(CALLER_NUMBERLABEL_COLUMN_INDEX);
- if (TextUtils.isEmpty(label)) {
- label = Integer.toString(type);
- }
- appendVCardTelephoneLine(builder, type, label, number);
- tryAppendCallHistoryTimeStampField(builder);
- appendVCardLine(builder, VCARD_PROPERTY_END, VCARD_DATA_VCARD);
- return builder.toString();
- }
-
private String createOneEntryInternal(final String contactId) {
final Map<String, List<ContentValues>> contentValuesListMap =
new HashMap<String, List<ContentValues>>();
@@ -633,8 +538,7 @@
dataExists = entityIterator.hasNext();
while (entityIterator.hasNext()) {
Entity entity = entityIterator.next();
- for (NamedContentValues namedContentValues : entity
- .getSubValues()) {
+ for (NamedContentValues namedContentValues : entity.getSubValues()) {
ContentValues contentValues = namedContentValues.values;
String key = contentValues.getAsString(Data.MIMETYPE);
if (key != null) {
@@ -663,11 +567,11 @@
}
final StringBuilder builder = new StringBuilder();
- appendVCardLine(builder, VCARD_PROPERTY_BEGIN, VCARD_DATA_VCARD);
+ appendVCardLine(builder, Constants.PROPERTY_BEGIN, VCARD_DATA_VCARD);
if (mIsV30) {
- appendVCardLine(builder, VCARD_PROPERTY_VERSION, Constants.VERSION_V30);
+ appendVCardLine(builder, Constants.PROPERTY_VERSION, Constants.VERSION_V30);
} else {
- appendVCardLine(builder, VCARD_PROPERTY_VERSION, Constants.VERSION_V21);
+ appendVCardLine(builder, Constants.PROPERTY_VERSION, Constants.VERSION_V21);
}
appendStructuredNames(builder, contentValuesListMap);
@@ -681,16 +585,16 @@
appendOrganizations(builder, contentValuesListMap);
appendPhotos(builder, contentValuesListMap);
appendNotes(builder, contentValuesListMap);
- // TODO: GroupMembership
+ // TODO: GroupMembership, Relation, Event other than birthday.
if (mIsDoCoMo) {
- appendVCardLine(builder, VCARD_PROPERTY_X_CLASS, VCARD_DATA_PUBLIC);
- appendVCardLine(builder, VCARD_PROPERTY_X_REDUCTION, "");
- appendVCardLine(builder, VCARD_PROPERTY_X_NO, "");
- appendVCardLine(builder, VCARD_PROPERTY_X_DCM_HMN_MODE, "");
+ appendVCardLine(builder, Constants.PROPERTY_X_CLASS, VCARD_DATA_PUBLIC);
+ appendVCardLine(builder, Constants.PROPERTY_X_REDUCTION, "");
+ appendVCardLine(builder, Constants.PROPERTY_X_NO, "");
+ appendVCardLine(builder, Constants.PROPERTY_X_DCM_HMN_MODE, "");
}
- appendVCardLine(builder, VCARD_PROPERTY_END, VCARD_DATA_VCARD);
+ appendVCardLine(builder, Constants.PROPERTY_END, VCARD_DATA_VCARD);
return builder.toString();
}
@@ -748,11 +652,11 @@
if (contentValuesList != null && contentValuesList.size() > 0) {
appendStructuredNamesInternal(builder, contentValuesList);
} else if (mIsDoCoMo) {
- appendVCardLine(builder, VCARD_PROPERTY_NAME, "");
+ appendVCardLine(builder, Constants.PROPERTY_N, "");
} else if (mIsV30) {
// vCard 3.0 requires "N" and "FN" properties.
- appendVCardLine(builder, VCARD_PROPERTY_NAME, "");
- appendVCardLine(builder, VCARD_PROPERTY_FULL_NAME, "");
+ appendVCardLine(builder, Constants.PROPERTY_N, "");
+ appendVCardLine(builder, Constants.PROPERTY_FN, "");
}
}
@@ -762,21 +666,38 @@
// may get confused with multiple "N", "FN", etc. properties, though it is valid in
// vCard spec.
ContentValues primaryContentValues = null;
+ ContentValues subprimaryContentValues = null;
for (ContentValues contentValues : contentValuesList) {
+ if (contentValues == null){
+ continue;
+ }
Integer isSuperPrimary = contentValues.getAsInteger(StructuredName.IS_SUPER_PRIMARY);
- if (isSuperPrimary != null && isSuperPrimary != 0) {
+ if (isSuperPrimary != null && isSuperPrimary > 0) {
// We choose "super primary" ContentValues.
primaryContentValues = contentValues;
break;
- } else if (primaryContentValues == null && contentValues != null) {
- // We choose the first ContentValues if "super primary" ContentValues does not exist.
- primaryContentValues = contentValues;
+ } else if (primaryContentValues == null) {
+ // We choose the first "primary" ContentValues
+ // if "super primary" ContentValues does not exist.
+ Integer primary = contentValues.getAsInteger(StructuredName.IS_PRIMARY);
+ if (primary != null && primary > 0) {
+ primaryContentValues = contentValues;
+ // Do not break, since there may be ContentValues with "super primary"
+ // afterword.
+ } else if (subprimaryContentValues == null) {
+ subprimaryContentValues = contentValues;
+ }
}
}
if (primaryContentValues == null) {
- Log.e(LOG_TAG, "All ContentValues given from database is empty.");
- primaryContentValues = new ContentValues();
+ if (subprimaryContentValues != null) {
+ // We choose the first ContentValues if any "primary" ContentValues does not exist.
+ primaryContentValues = subprimaryContentValues;
+ } else {
+ Log.e(LOG_TAG, "All ContentValues given from database is empty.");
+ primaryContentValues = new ContentValues();
+ }
}
final String familyName = primaryContentValues
@@ -800,7 +721,7 @@
final String encodedSuffix;
final boolean reallyUseQuotedPrintableToName =
- (mUsesQPToPrimaryProperties &&
+ (!mRefrainsQPToPrimaryProperties &&
!(VCardUtils.containsOnlyNonCrLfPrintableAscii(familyName) &&
VCardUtils.containsOnlyNonCrLfPrintableAscii(givenName) &&
VCardUtils.containsOnlyNonCrLfPrintableAscii(middleName) &&
@@ -822,7 +743,7 @@
}
// N property. This order is specified by vCard spec and does not depend on countries.
- builder.append(VCARD_PROPERTY_NAME);
+ builder.append(Constants.PROPERTY_N);
if (shouldAppendCharsetAttribute(Arrays.asList(
familyName, givenName, middleName, prefix, suffix))) {
builder.append(VCARD_ATTR_SEPARATOR);
@@ -845,20 +766,20 @@
builder.append(encodedSuffix);
builder.append(VCARD_COL_SEPARATOR);
- final String fullname = VCardUtils.constructNameFromElements(
+ final String formattedName = VCardUtils.constructNameFromElements(
VCardConfig.getNameOrderType(mVCardType),
encodedFamily, encodedMiddle, encodedGiven, encodedPrefix, encodedSuffix);
final boolean reallyUseQuotedPrintableToFullname =
- mUsesQPToPrimaryProperties &&
- !VCardUtils.containsOnlyNonCrLfPrintableAscii(fullname);
+ !mRefrainsQPToPrimaryProperties &&
+ !VCardUtils.containsOnlyNonCrLfPrintableAscii(formattedName);
final String encodedFullname =
reallyUseQuotedPrintableToFullname ?
- encodeQuotedPrintable(fullname) :
- escapeCharacters(fullname);
+ encodeQuotedPrintable(formattedName) :
+ escapeCharacters(formattedName);
// FN property
- builder.append(VCARD_PROPERTY_FULL_NAME);
+ builder.append(Constants.PROPERTY_FN);
if (shouldAppendCharsetAttribute(encodedFullname)) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(mVCardAttributeCharset);
@@ -872,14 +793,14 @@
builder.append(VCARD_COL_SEPARATOR);
} else if (!TextUtils.isEmpty(displayName)) {
final boolean reallyUseQuotedPrintableToDisplayName =
- (mUsesQPToPrimaryProperties &&
+ (!mRefrainsQPToPrimaryProperties &&
!VCardUtils.containsOnlyNonCrLfPrintableAscii(displayName));
final String encodedDisplayName =
reallyUseQuotedPrintableToDisplayName ?
encodeQuotedPrintable(displayName) :
escapeCharacters(displayName);
- builder.append(VCARD_PROPERTY_NAME);
+ builder.append(Constants.PROPERTY_N);
if (shouldAppendCharsetAttribute(encodedDisplayName)) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(mVCardAttributeCharset);
@@ -895,38 +816,54 @@
builder.append(VCARD_ITEM_SEPARATOR);
builder.append(VCARD_ITEM_SEPARATOR);
builder.append(VCARD_COL_SEPARATOR);
- } else if (mIsDoCoMo) {
- appendVCardLine(builder, VCARD_PROPERTY_NAME, "");
+ if (mIsV30) {
+ builder.append(Constants.PROPERTY_FN);
+ // TODO: Not allowed formally...
+ if (shouldAppendCharsetAttribute(encodedDisplayName)) {
+ builder.append(VCARD_ATTR_SEPARATOR);
+ builder.append(mVCardAttributeCharset);
+ }
+ builder.append(VCARD_DATA_SEPARATOR);
+ builder.append(encodedDisplayName);
+ builder.append(VCARD_COL_SEPARATOR);
+ }
} else if (mIsV30) {
- appendVCardLine(builder, VCARD_PROPERTY_NAME, "");
- appendVCardLine(builder, VCARD_PROPERTY_FULL_NAME, "");
+ // vCard 3.0 specification requires these fields.
+ appendVCardLine(builder, Constants.PROPERTY_N, "");
+ appendVCardLine(builder, Constants.PROPERTY_FN, "");
+ } else if (mIsDoCoMo) {
+ appendVCardLine(builder, Constants.PROPERTY_N, "");
}
- String phoneticFamilyName = primaryContentValues
- .getAsString(StructuredName.PHONETIC_FAMILY_NAME);
- String phoneticMiddleName = primaryContentValues
- .getAsString(StructuredName.PHONETIC_MIDDLE_NAME);
- String phoneticGivenName = primaryContentValues
- .getAsString(StructuredName.PHONETIC_GIVEN_NAME);
- if (!(TextUtils.isEmpty(phoneticFamilyName)
- && TextUtils.isEmpty(phoneticMiddleName) &&
- TextUtils.isEmpty(phoneticGivenName))) { // if not empty
- if (mIsJapaneseMobilePhone) {
- phoneticFamilyName = VCardUtils
- .toHalfWidthString(phoneticFamilyName);
- phoneticMiddleName = VCardUtils
- .toHalfWidthString(phoneticMiddleName);
- phoneticGivenName = VCardUtils
- .toHalfWidthString(phoneticGivenName);
+ final String phoneticFamilyName;
+ final String phoneticMiddleName;
+ final String phoneticGivenName;
+ {
+ String tmpPhoneticFamilyName =
+ primaryContentValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME);
+ String tmpPhoneticMiddleName =
+ primaryContentValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME);
+ String tmpPhoneticGivenName =
+ primaryContentValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME);
+ if (mNeedsToConvertPhoneticString) {
+ phoneticFamilyName = VCardUtils.toHalfWidthString(tmpPhoneticFamilyName);
+ phoneticMiddleName = VCardUtils.toHalfWidthString(tmpPhoneticMiddleName);
+ phoneticGivenName = VCardUtils.toHalfWidthString(tmpPhoneticGivenName);
+ } else {
+ phoneticFamilyName = tmpPhoneticFamilyName;
+ phoneticMiddleName = tmpPhoneticMiddleName;
+ phoneticGivenName = tmpPhoneticGivenName;
}
+ }
+ if (!(TextUtils.isEmpty(phoneticFamilyName)
+ && TextUtils.isEmpty(phoneticMiddleName)
+ && TextUtils.isEmpty(phoneticGivenName))) {
if (mIsV30) {
final String sortString = VCardUtils
.constructNameFromElements(mVCardType,
- phoneticFamilyName,
- phoneticMiddleName,
- phoneticGivenName);
- builder.append(VCARD_PROPERTY_SORT_STRING);
+ phoneticFamilyName, phoneticMiddleName, phoneticGivenName);
+ builder.append(Constants.PROPERTY_SORT_STRING);
// Do not need to care about QP, since vCard 3.0 does not allow it.
final String encodedSortString = escapeCharacters(sortString);
@@ -937,25 +874,26 @@
builder.append(VCARD_DATA_SEPARATOR);
builder.append(encodedSortString);
builder.append(VCARD_COL_SEPARATOR);
- } else {
+ } else if (mIsJapaneseMobilePhone) {
// Note: There is no appropriate property for expressing
// phonetic name in vCard 2.1, while there is in
// vCard 3.0 (SORT-STRING).
- // We chose to use DoCoMo's way since it is supported by
+ // We chose to use DoCoMo's way when the device is Japanese one
+ // since it is supported by
// a lot of Japanese mobile phones. This is "X-" property, so
// any parser hopefully would not get confused with this.
- builder.append(VCARD_PROPERTY_SOUND);
+ builder.append(Constants.PROPERTY_SOUND);
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(Constants.ATTR_TYPE_X_IRMC_N);
boolean reallyUseQuotedPrintable =
- (mUsesQPToPrimaryProperties &&
- !(VCardUtils.containsOnlyNonCrLfPrintableAscii(
- phoneticFamilyName) &&
- VCardUtils.containsOnlyNonCrLfPrintableAscii(
- phoneticMiddleName) &&
- VCardUtils.containsOnlyNonCrLfPrintableAscii(
- phoneticGivenName)));
+ (!mRefrainsQPToPrimaryProperties
+ && !(VCardUtils.containsOnlyNonCrLfPrintableAscii(
+ phoneticFamilyName)
+ && VCardUtils.containsOnlyNonCrLfPrintableAscii(
+ phoneticMiddleName)
+ && VCardUtils.containsOnlyNonCrLfPrintableAscii(
+ phoneticGivenName)));
final String encodedPhoneticFamilyName;
final String encodedPhoneticMiddleName;
@@ -987,7 +925,7 @@
builder.append(VCARD_COL_SEPARATOR);
}
} else if (mIsDoCoMo) {
- builder.append(VCARD_PROPERTY_SOUND);
+ builder.append(Constants.PROPERTY_SOUND);
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(Constants.ATTR_TYPE_X_IRMC_N);
builder.append(VCARD_DATA_SEPARATOR);
@@ -1001,15 +939,14 @@
if (mUsesDefactProperty) {
if (!TextUtils.isEmpty(phoneticGivenName)) {
final boolean reallyUseQuotedPrintable =
- (mUsesQPToPrimaryProperties &&
- !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticGivenName));
+ !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticGivenName);
final String encodedPhoneticGivenName;
if (reallyUseQuotedPrintable) {
encodedPhoneticGivenName = encodeQuotedPrintable(phoneticGivenName);
} else {
encodedPhoneticGivenName = escapeCharacters(phoneticGivenName);
}
- builder.append(VCARD_PROPERTY_X_PHONETIC_FIRST_NAME);
+ builder.append(Constants.PROPERTY_X_PHONETIC_FIRST_NAME);
if (shouldAppendCharsetAttribute(encodedPhoneticGivenName)) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(mVCardAttributeCharset);
@@ -1024,15 +961,14 @@
}
if (!TextUtils.isEmpty(phoneticMiddleName)) {
final boolean reallyUseQuotedPrintable =
- (mUsesQPToPrimaryProperties &&
- !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticMiddleName));
+ !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticMiddleName);
final String encodedPhoneticMiddleName;
if (reallyUseQuotedPrintable) {
encodedPhoneticMiddleName = encodeQuotedPrintable(phoneticMiddleName);
} else {
encodedPhoneticMiddleName = escapeCharacters(phoneticMiddleName);
}
- builder.append(VCARD_PROPERTY_X_PHONETIC_MIDDLE_NAME);
+ builder.append(Constants.PROPERTY_X_PHONETIC_MIDDLE_NAME);
if (shouldAppendCharsetAttribute(encodedPhoneticMiddleName)) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(mVCardAttributeCharset);
@@ -1047,15 +983,14 @@
}
if (!TextUtils.isEmpty(phoneticFamilyName)) {
final boolean reallyUseQuotedPrintable =
- (mUsesQPToPrimaryProperties &&
- !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticFamilyName));
+ !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticFamilyName);
final String encodedPhoneticFamilyName;
if (reallyUseQuotedPrintable) {
encodedPhoneticFamilyName = encodeQuotedPrintable(phoneticFamilyName);
} else {
encodedPhoneticFamilyName = escapeCharacters(phoneticFamilyName);
}
- builder.append(VCARD_PROPERTY_X_PHONETIC_LAST_NAME);
+ builder.append(Constants.PROPERTY_X_PHONETIC_LAST_NAME);
if (shouldAppendCharsetAttribute(encodedPhoneticFamilyName)) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(mVCardAttributeCharset);
@@ -1075,46 +1010,48 @@
final Map<String, List<ContentValues>> contentValuesListMap) {
final List<ContentValues> contentValuesList = contentValuesListMap
.get(Nickname.CONTENT_ITEM_TYPE);
- if (contentValuesList != null) {
- final String propertyNickname;
- if (mIsV30) {
- propertyNickname = VCARD_PROPERTY_NICKNAME;
- } else if (mUsesAndroidProperty) {
- propertyNickname = VCARD_PROPERTY_X_NICKNAME;
+ if (contentValuesList == null) {
+ return;
+ }
+
+ final String propertyNickname;
+ if (mIsV30) {
+ propertyNickname = Constants.PROPERTY_NICKNAME;
+ /*} else if (mUsesAndroidProperty) {
+ propertyNickname = VCARD_PROPERTY_X_NICKNAME;*/
+ } else {
+ // There's no way to add this field.
+ return;
+ }
+
+ for (ContentValues contentValues : contentValuesList) {
+ final String nickname = contentValues.getAsString(Nickname.NAME);
+ if (TextUtils.isEmpty(nickname)) {
+ continue;
+ }
+
+ final String encodedNickname;
+ final boolean reallyUseQuotedPrintable =
+ (mUsesQuotedPrintable &&
+ !VCardUtils.containsOnlyNonCrLfPrintableAscii(nickname));
+ if (reallyUseQuotedPrintable) {
+ encodedNickname = encodeQuotedPrintable(nickname);
} else {
- // There's no way to add this field.
- return;
+ encodedNickname = escapeCharacters(nickname);
}
- for (ContentValues contentValues : contentValuesList) {
- final String nickname = contentValues.getAsString(Nickname.NAME);
- if (TextUtils.isEmpty(nickname)) {
- continue;
- }
-
- final String encodedNickname;
- final boolean reallyUseQuotedPrintable =
- (mUsesQuotedPrintable &&
- !VCardUtils.containsOnlyNonCrLfPrintableAscii(nickname));
- if (reallyUseQuotedPrintable) {
- encodedNickname = encodeQuotedPrintable(nickname);
- } else {
- encodedNickname = escapeCharacters(nickname);
- }
-
- builder.append(propertyNickname);
- if (shouldAppendCharsetAttribute(propertyNickname)) {
- builder.append(VCARD_ATTR_SEPARATOR);
- builder.append(mVCardAttributeCharset);
- }
- if (reallyUseQuotedPrintable) {
- builder.append(VCARD_ATTR_SEPARATOR);
- builder.append(VCARD_ATTR_ENCODING_QP);
- }
- builder.append(VCARD_DATA_SEPARATOR);
- builder.append(encodedNickname);
- builder.append(VCARD_COL_SEPARATOR);
+ builder.append(propertyNickname);
+ if (shouldAppendCharsetAttribute(propertyNickname)) {
+ builder.append(VCARD_ATTR_SEPARATOR);
+ builder.append(mVCardAttributeCharset);
}
+ if (reallyUseQuotedPrintable) {
+ builder.append(VCARD_ATTR_SEPARATOR);
+ builder.append(VCARD_ATTR_ENCODING_QP);
+ }
+ builder.append(VCARD_DATA_SEPARATOR);
+ builder.append(encodedNickname);
+ builder.append(VCARD_COL_SEPARATOR);
}
}
@@ -1128,6 +1065,9 @@
for (ContentValues contentValues : contentValuesList) {
final Integer typeAsObject = contentValues.getAsInteger(Phone.TYPE);
final String label = contentValues.getAsString(Phone.LABEL);
+ final Integer isPrimaryAsInteger = contentValues.getAsInteger(Phone.IS_PRIMARY);
+ final boolean isPrimary = (isPrimaryAsInteger != null ?
+ (isPrimaryAsInteger > 0) : false);
String phoneNumber = contentValues.getAsString(Phone.NUMBER);
if (phoneNumber != null) {
phoneNumber = phoneNumber.trim();
@@ -1136,18 +1076,18 @@
continue;
}
phoneLineExists = true;
- int type = (typeAsObject != null ? typeAsObject : Phone.TYPE_HOME);
+ int type = (typeAsObject != null ? typeAsObject : DEFAULT_PHONE_TYPE);
// TODO: Premature, since this allows two phone numbers which are
// same from the view of phone number format (e.g. "100" v.s. "1-0-0")
if (!phoneSet.contains(phoneNumber)) {
phoneSet.add(phoneNumber);
- appendVCardTelephoneLine(builder, type, label, phoneNumber);
+ appendVCardTelephoneLine(builder, type, label, phoneNumber, isPrimary);
}
}
}
if (!phoneLineExists && mIsDoCoMo) {
- appendVCardTelephoneLine(builder, Phone.TYPE_HOME, "", "");
+ appendVCardTelephoneLine(builder, Phone.TYPE_HOME, "", "", false);
}
}
@@ -1155,14 +1095,11 @@
final Map<String, List<ContentValues>> contentValuesListMap) {
final List<ContentValues> contentValuesList = contentValuesListMap
.get(Email.CONTENT_ITEM_TYPE);
+
boolean emailAddressExists = false;
if (contentValuesList != null) {
- Set<String> addressSet = new HashSet<String>();
+ final Set<String> addressSet = new HashSet<String>();
for (ContentValues contentValues : contentValuesList) {
- Integer typeAsObject = contentValues.getAsInteger(Email.TYPE);
- final int type = (typeAsObject != null ?
- typeAsObject : Email.TYPE_OTHER);
- final String label = contentValues.getAsString(Email.LABEL);
String emailAddress = contentValues.getAsString(Email.DATA);
if (emailAddress != null) {
emailAddress = emailAddress.trim();
@@ -1170,16 +1107,23 @@
if (TextUtils.isEmpty(emailAddress)) {
continue;
}
+ Integer typeAsObject = contentValues.getAsInteger(Email.TYPE);
+ final int type = (typeAsObject != null ?
+ typeAsObject : DEFAULT_EMAIL_TYPE);
+ final String label = contentValues.getAsString(Email.LABEL);
+ Integer isPrimaryAsInteger = contentValues.getAsInteger(Email.IS_PRIMARY);
+ final boolean isPrimary = (isPrimaryAsInteger != null ?
+ (isPrimaryAsInteger > 0) : false);
emailAddressExists = true;
if (!addressSet.contains(emailAddress)) {
addressSet.add(emailAddress);
- appendVCardEmailLine(builder, type, label, emailAddress);
+ appendVCardEmailLine(builder, type, label, emailAddress, isPrimary);
}
}
}
if (!emailAddressExists && mIsDoCoMo) {
- appendVCardEmailLine(builder, Email.TYPE_HOME, "", "");
+ appendVCardEmailLine(builder, Email.TYPE_HOME, "", "", false);
}
}
@@ -1194,7 +1138,7 @@
appendPostalsForGeneric(builder, contentValuesList);
}
} else if (mIsDoCoMo) {
- builder.append(VCARD_PROPERTY_ADR);
+ builder.append(Constants.PROPERTY_ADR);
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(Constants.ATTR_TYPE_HOME);
builder.append(VCARD_DATA_SEPARATOR);
@@ -1236,7 +1180,10 @@
final Integer type = contentValues.getAsInteger(StructuredPostal.TYPE);
final String label = contentValues.getAsString(StructuredPostal.LABEL);
if (type == preferedType) {
- appendVCardPostalLine(builder, type, label, contentValues);
+ // Note: Not sure why we need to emit "empty" line even when actual
+ // data does not exist. There may be some reason or may not.
+ // We keep safer side since the previous implementation did so.
+ appendVCardPostalLine(builder, type, label, contentValues, true, true);
return true;
}
}
@@ -1246,11 +1193,18 @@
private void appendPostalsForGeneric(final StringBuilder builder,
final List<ContentValues> contentValuesList) {
for (ContentValues contentValues : contentValuesList) {
- final Integer type = contentValues.getAsInteger(StructuredPostal.TYPE);
- final String label = contentValues.getAsString(StructuredPostal.LABEL);
- if (type != null) {
- appendVCardPostalLine(builder, type, label, contentValues);
+ if (contentValues == null) {
+ continue;
}
+ final Integer typeAsObject = contentValues.getAsInteger(StructuredPostal.TYPE);
+ final int type = (typeAsObject != null ?
+ typeAsObject : DEFAULT_POSTAL_TYPE);
+ final String label = contentValues.getAsString(StructuredPostal.LABEL);
+ final Integer isPrimaryAsInteger =
+ contentValues.getAsInteger(StructuredPostal.IS_PRIMARY);
+ final boolean isPrimary = (isPrimaryAsInteger != null ?
+ (isPrimaryAsInteger > 0) : false);
+ appendVCardPostalLine(builder, type, label, contentValues, isPrimary, false);
}
}
@@ -1258,24 +1212,63 @@
final Map<String, List<ContentValues>> contentValuesListMap) {
final List<ContentValues> contentValuesList = contentValuesListMap
.get(Im.CONTENT_ITEM_TYPE);
- if (contentValuesList != null) {
- for (ContentValues contentValues : contentValuesList) {
- Integer protocol = contentValues.getAsInteger(Im.PROTOCOL);
- String data = contentValues.getAsString(Im.DATA);
- if (data != null) {
- data = data.trim();
- }
- if (TextUtils.isEmpty(data)) {
- continue;
- }
-
- if (protocol != null && protocol == Im.PROTOCOL_GOOGLE_TALK) {
- if (VCardConfig.usesAndroidSpecificProperty(mVCardType)) {
- appendVCardLine(builder, Constants.PROPERTY_X_GOOGLE_TALK, data);
+ if (contentValuesList == null) {
+ return;
+ }
+ for (ContentValues contentValues : contentValuesList) {
+ final Integer protocolAsObject = contentValues.getAsInteger(Im.PROTOCOL);
+ if (protocolAsObject == null) {
+ continue;
+ }
+ final String propertyName = VCardUtils.getPropertyNameForIm(protocolAsObject);
+ if (propertyName == null) {
+ continue;
+ }
+ String data = contentValues.getAsString(Im.DATA);
+ if (data != null) {
+ data = data.trim();
+ }
+ if (TextUtils.isEmpty(data)) {
+ continue;
+ }
+ final String typeAsString;
+ {
+ final Integer typeAsInteger = contentValues.getAsInteger(Im.TYPE);
+ switch (typeAsInteger != null ? typeAsInteger : Im.TYPE_OTHER) {
+ case Im.TYPE_HOME: {
+ typeAsString = Constants.ATTR_TYPE_HOME;
+ break;
}
- // TODO: add "X-GOOGLE TALK" case...
+ case Im.TYPE_WORK: {
+ typeAsString = Constants.ATTR_TYPE_WORK;
+ break;
+ }
+ case Im.TYPE_CUSTOM: {
+ final String label = contentValues.getAsString(Im.LABEL);
+ typeAsString = (label != null ? "X-" + label : null);
+ break;
+ }
+ case Im.TYPE_OTHER: // Ignore
+ default: {
+ typeAsString = null;
+ break;
+ }
}
}
+
+ List<String> attributeList = new ArrayList<String>();
+ if (!TextUtils.isEmpty(typeAsString)) {
+ attributeList.add(typeAsString);
+ }
+ final Integer isPrimaryAsInteger = contentValues.getAsInteger(Im.IS_PRIMARY);
+ final boolean isPrimary = (isPrimaryAsInteger != null ?
+ (isPrimaryAsInteger > 0) : false);
+ if (isPrimary) {
+ attributeList.add(Constants.ATTR_TYPE_PREF);
+ }
+
+ appendVCardLineWithCharsetAndQPDetection(
+ builder, propertyName, attributeList, data);
}
}
@@ -1283,39 +1276,83 @@
final Map<String, List<ContentValues>> contentValuesListMap) {
final List<ContentValues> contentValuesList = contentValuesListMap
.get(Website.CONTENT_ITEM_TYPE);
- if (contentValuesList != null) {
- for (ContentValues contentValues : contentValuesList) {
- String website = contentValues.getAsString(Website.URL);
- if (website != null) {
- website = website.trim();
- }
- if (!TextUtils.isEmpty(website)) {
- appendVCardLine(builder, VCARD_PROPERTY_URL, website);
- }
+ if (contentValuesList == null) {
+ return;
+ }
+ for (ContentValues contentValues : contentValuesList) {
+ String website = contentValues.getAsString(Website.URL);
+ if (website != null) {
+ website = website.trim();
+ }
+ // Note: vCard 3.0 does not allow any attribute addition toward "URL"
+ // property, while there's no document in vCard 2.1.
+ //
+ // TODO: Should we allow adding it when appropriate?
+ // (Actually, we drop some data. Using "group.X-URL-TYPE" or something
+ // may help)
+ if (!TextUtils.isEmpty(website)) {
+ appendVCardLine(builder, Constants.PROPERTY_URL, website);
}
}
}
+ /**
+ * Theoretically, there must be only one birthday for each vCard entry.
+ * Also, we are afraid of some importer's parse error during its import.
+ * We emit only one birthday entry even when there are more than one.
+ */
private void appendBirthday(final StringBuilder builder,
final Map<String, List<ContentValues>> contentValuesListMap) {
- final List<ContentValues> contentValuesList = contentValuesListMap
- .get(Event.CONTENT_ITEM_TYPE);
- if (contentValuesList != null && contentValuesList.size() > 0) {
- Integer eventType = contentValuesList.get(0).getAsInteger(Event.TYPE);
+ final List<ContentValues> contentValuesList =
+ contentValuesListMap.get(Event.CONTENT_ITEM_TYPE);
+ if (contentValuesList == null) {
+ return;
+ }
+ String primaryBirthday = null;
+ String secondaryBirthday = null;
+ for (ContentValues contentValues : contentValuesList) {
+ if (contentValues == null) {
+ continue;
+ }
+ final Integer eventType = contentValues.getAsInteger(Event.TYPE);
if (eventType == null || !eventType.equals(Event.TYPE_BIRTHDAY)) {
- return;
+ continue;
}
- // Theoretically, there must be only one birthday for each vCard data and
- // we are afraid of some parse error occuring in some devices, so
- // we emit only one birthday entry for now.
- String birthday = contentValuesList.get(0).getAsString(Event.START_DATE);
- if (birthday != null) {
- birthday = birthday.trim();
+ final String birthdayCandidate = contentValues.getAsString(Event.START_DATE);
+ if (birthdayCandidate == null) {
+ continue;
}
- if (!TextUtils.isEmpty(birthday)) {
- appendVCardLine(builder, VCARD_PROPERTY_BIRTHDAY, birthday);
+ final Integer isSuperPrimaryAsInteger =
+ contentValues.getAsInteger(Event.IS_SUPER_PRIMARY);
+ final boolean isSuperPrimary = (isSuperPrimaryAsInteger != null ?
+ (isSuperPrimaryAsInteger > 0) : false);
+ if (isSuperPrimary) {
+ // "super primary" birthday should the prefered one.
+ primaryBirthday = birthdayCandidate;
+ break;
+ }
+ final Integer isPrimaryAsInteger =
+ contentValues.getAsInteger(Event.IS_PRIMARY);
+ final boolean isPrimary = (isPrimaryAsInteger != null ?
+ (isPrimaryAsInteger > 0) : false);
+ if (isPrimary) {
+ // We don't break here since "super primary" birthday may exist later.
+ primaryBirthday = birthdayCandidate;
+ } else if (secondaryBirthday == null) {
+ // First entry is set to the "secondary" candidate.
+ secondaryBirthday = birthdayCandidate;
}
}
+
+ final String birthday;
+ if (primaryBirthday != null) {
+ birthday = primaryBirthday.trim();
+ } else if (secondaryBirthday != null){
+ birthday = secondaryBirthday.trim();
+ } else {
+ return;
+ }
+ appendVCardLineWithCharsetAndQPDetection(builder, Constants.PROPERTY_BDAY, birthday);
}
private void appendOrganizations(final StringBuilder builder,
@@ -1324,25 +1361,37 @@
.get(Organization.CONTENT_ITEM_TYPE);
if (contentValuesList != null) {
for (ContentValues contentValues : contentValuesList) {
- String company = contentValues
- .getAsString(Organization.COMPANY);
+ String company = contentValues.getAsString(Organization.COMPANY);
if (company != null) {
company = company.trim();
}
- String title = contentValues
- .getAsString(Organization.TITLE);
+ String department = contentValues.getAsString(Organization.DEPARTMENT);
+ if (department != null) {
+ department = department.trim();
+ }
+ String title = contentValues.getAsString(Organization.TITLE);
if (title != null) {
title = title.trim();
}
+ StringBuilder orgBuilder = new StringBuilder();
if (!TextUtils.isEmpty(company)) {
- appendVCardLine(builder, VCARD_PROPERTY_ORG, company,
- !VCardUtils.containsOnlyPrintableAscii(company),
- (mUsesQuotedPrintable &&
- !VCardUtils.containsOnlyNonCrLfPrintableAscii(company)));
+ orgBuilder.append(company);
}
+ if (!TextUtils.isEmpty(department)) {
+ if (orgBuilder.length() > 0) {
+ orgBuilder.append(';');
+ }
+ orgBuilder.append(department);
+ }
+ final String orgline = orgBuilder.toString();
+ appendVCardLine(builder, Constants.PROPERTY_ORG, orgline,
+ !VCardUtils.containsOnlyPrintableAscii(orgline),
+ (mUsesQuotedPrintable &&
+ !VCardUtils.containsOnlyNonCrLfPrintableAscii(orgline)));
+
if (!TextUtils.isEmpty(title)) {
- appendVCardLine(builder, VCARD_PROPERTY_TITLE, title,
+ appendVCardLine(builder, Constants.PROPERTY_TITLE, title,
!VCardUtils.containsOnlyPrintableAscii(title),
(mUsesQuotedPrintable &&
!VCardUtils.containsOnlyNonCrLfPrintableAscii(title)));
@@ -1369,11 +1418,9 @@
photoType = "GIF";
} else if (data.length >= 4 && data[0] == (byte) 0x89
&& data[1] == 'P' && data[2] == 'N' && data[3] == 'G') {
- // Note: vCard 2.1 officially does not support PNG, but we
- // may have it
- // and using X- word like "X-PNG" may not let importers know
- // it is
- // PNG. So we use the String "PNG" as is...
+ // Note: vCard 2.1 officially does not support PNG, but we may
+ // have it and using X- word like "X-PNG" may not let importers
+ // know it is PNG. So we use the String "PNG" as is...
photoType = "PNG";
} else if (data.length >= 2 && data[0] == (byte) 0xff
&& data[1] == (byte) 0xd8) {
@@ -1420,7 +1467,7 @@
final boolean reallyUseQuotedPrintable =
(mUsesQuotedPrintable &&
!VCardUtils.containsOnlyNonCrLfPrintableAscii(noteStr));
- appendVCardLine(builder, VCARD_PROPERTY_NOTE, noteStr,
+ appendVCardLine(builder, Constants.PROPERTY_NOTE, noteStr,
shouldAppendCharsetInfo, reallyUseQuotedPrintable);
} else {
for (ContentValues contentValues : contentValuesList) {
@@ -1431,7 +1478,7 @@
final boolean reallyUseQuotedPrintable =
(mUsesQuotedPrintable &&
!VCardUtils.containsOnlyNonCrLfPrintableAscii(noteStr));
- appendVCardLine(builder, VCARD_PROPERTY_NOTE, noteStr,
+ appendVCardLine(builder, Constants.PROPERTY_NOTE, noteStr,
shouldAppendCharsetInfo, reallyUseQuotedPrintable);
}
}
@@ -1517,7 +1564,7 @@
private void appendVCardPhotoLine(final StringBuilder builder,
final String encodedData, final String photoType) {
StringBuilder tmpBuilder = new StringBuilder();
- tmpBuilder.append(VCARD_PROPERTY_PHOTO);
+ tmpBuilder.append(Constants.PROPERTY_PHOTO);
tmpBuilder.append(VCARD_ATTR_SEPARATOR);
if (mIsV30) {
tmpBuilder.append(VCARD_ATTR_ENCODING_BASE64_V30);
@@ -1547,59 +1594,126 @@
builder.append(VCARD_COL_SEPARATOR);
}
- private void appendVCardPostalLine(final StringBuilder builder,
- final Integer typeAsObject, final String label,
- final ContentValues contentValues) {
- builder.append(VCARD_PROPERTY_ADR);
- builder.append(VCARD_ATTR_SEPARATOR);
+ private class PostalStruct {
+ final boolean reallyUseQuotedPrintable;
+ final boolean appendCharset;
+ final String addressData;
+ public PostalStruct(final boolean reallyUseQuotedPrintable,
+ final boolean appendCharset, final String addressData) {
+ this.reallyUseQuotedPrintable = reallyUseQuotedPrintable;
+ this.appendCharset = appendCharset;
+ this.addressData = addressData;
+ }
+ }
- // Note: Not sure why we need to emit "empty" line even when actual data does not exist.
- // There may be some reason or may not be any. We keep safer side.
- // TODO: investigate this.
- boolean dataExists = false;
+ /**
+ * @return null when there's no information available to construct the data.
+ */
+ private PostalStruct tryConstructPostalStruct(ContentValues contentValues) {
+ boolean reallyUseQuotedPrintable = false;
+ boolean appendCharset = false;
+
+ boolean dataArrayExists = false;
String[] dataArray = VCardUtils.getVCardPostalElements(contentValues);
- boolean actuallyUseQuotedPrintable = false;
- boolean shouldAppendCharset = false;
for (String data : dataArray) {
if (!TextUtils.isEmpty(data)) {
- dataExists = true;
- if (!shouldAppendCharset && !VCardUtils.containsOnlyPrintableAscii(data)) {
- shouldAppendCharset = true;
+ dataArrayExists = true;
+ if (!appendCharset && !VCardUtils.containsOnlyPrintableAscii(data)) {
+ appendCharset = true;
}
if (mUsesQuotedPrintable && !VCardUtils.containsOnlyNonCrLfPrintableAscii(data)) {
- actuallyUseQuotedPrintable = true;
+ reallyUseQuotedPrintable = true;
break;
}
}
}
- int length = dataArray.length;
- for (int i = 0; i < length; i++) {
- String data = dataArray[i];
- if (!TextUtils.isEmpty(data)) {
- if (actuallyUseQuotedPrintable) {
- dataArray[i] = encodeQuotedPrintable(data);
+ if (dataArrayExists) {
+ StringBuffer addressBuffer = new StringBuffer();
+ boolean first = true;
+ for (String data : dataArray) {
+ if (first) {
+ first = false;
} else {
- dataArray[i] = escapeCharacters(data);
+ addressBuffer.append(VCARD_ITEM_SEPARATOR);
}
+ if (!TextUtils.isEmpty(data)) {
+ if (reallyUseQuotedPrintable) {
+ addressBuffer.append(encodeQuotedPrintable(data));
+ } else {
+ addressBuffer.append(escapeCharacters(data));
+ }
+ }
+ }
+ return new PostalStruct(reallyUseQuotedPrintable, appendCharset,
+ addressBuffer.toString());
+ }
+
+ String formattedAddress =
+ contentValues.getAsString(StructuredPostal.FORMATTED_ADDRESS);
+ if (!TextUtils.isEmpty(formattedAddress)) {
+ reallyUseQuotedPrintable =
+ !VCardUtils.containsOnlyPrintableAscii(formattedAddress);
+ appendCharset =
+ !VCardUtils.containsOnlyNonCrLfPrintableAscii(formattedAddress);
+ if (reallyUseQuotedPrintable) {
+ formattedAddress = encodeQuotedPrintable(formattedAddress);
+ } else {
+ formattedAddress = escapeCharacters(formattedAddress);
+ }
+ // We use the second value ("Extended Address").
+ //
+ // adr-value = 0*6(text-value ";") text-value
+ // ; PO Box, Extended Address, Street, Locality, Region, Postal
+ // ; Code, Country Name
+ StringBuffer addressBuffer = new StringBuffer();
+ addressBuffer.append(VCARD_ITEM_SEPARATOR);
+ addressBuffer.append(formattedAddress);
+ addressBuffer.append(VCARD_ITEM_SEPARATOR);
+ addressBuffer.append(VCARD_ITEM_SEPARATOR);
+ addressBuffer.append(VCARD_ITEM_SEPARATOR);
+ addressBuffer.append(VCARD_ITEM_SEPARATOR);
+ addressBuffer.append(VCARD_ITEM_SEPARATOR);
+ return new PostalStruct(
+ reallyUseQuotedPrintable, appendCharset, addressBuffer.toString());
+ }
+ return null; // There's no data available.
+ }
+
+ private void appendVCardPostalLine(final StringBuilder builder,
+ final int type, final String label, final ContentValues contentValues,
+ final boolean isPrimary, final boolean emitLineEveryTime) {
+ final boolean reallyUseQuotedPrintable;
+ final boolean appendCharset;
+ final String addressData;
+ {
+ PostalStruct postalStruct = tryConstructPostalStruct(contentValues);
+ if (postalStruct == null) {
+ if (emitLineEveryTime) {
+ reallyUseQuotedPrintable = false;
+ appendCharset = false;
+ addressData = "";
+ } else {
+ return;
+ }
+ } else {
+ reallyUseQuotedPrintable = postalStruct.reallyUseQuotedPrintable;
+ appendCharset = postalStruct.appendCharset;
+ addressData = postalStruct.addressData;
}
}
- final int typeAsPrimitive;
- if (typeAsObject == null) {
- typeAsPrimitive = StructuredPostal.TYPE_OTHER;
- } else {
- typeAsPrimitive = typeAsObject;
+ List<String> attributeList = new ArrayList<String>();
+ if (isPrimary) {
+ attributeList.add(Constants.ATTR_TYPE_PREF);
}
-
- String typeAsString = null;
- switch (typeAsPrimitive) {
+ switch (type) {
case StructuredPostal.TYPE_HOME: {
- typeAsString = Constants.ATTR_TYPE_HOME;
+ attributeList.add(Constants.ATTR_TYPE_HOME);
break;
}
case StructuredPostal.TYPE_WORK: {
- typeAsString = Constants.ATTR_TYPE_WORK;
+ attributeList.add(Constants.ATTR_TYPE_WORK);
break;
}
case StructuredPostal.TYPE_CUSTOM: {
@@ -1609,9 +1723,7 @@
// ("IANA-token" in the vCard 3.0 is unclear...)
// Just for safety, we add "X-" at the beggining of each label.
// Also checks the label obeys with vCard 3.0 spec.
- builder.append("X-");
- builder.append(label);
- builder.append(VCARD_DATA_SEPARATOR);
+ attributeList.add("X-" + label);
}
break;
}
@@ -1619,82 +1731,56 @@
break;
}
default: {
- Log.e(LOG_TAG, "Unknown StructuredPostal type: " + typeAsPrimitive);
+ Log.e(LOG_TAG, "Unknown StructuredPostal type: " + type);
break;
}
}
- // Attribute(s).
+ // Actual data construction starts from here.
+ // TODO: add a new version of appendVCardLine() for this purpose.
+ builder.append(Constants.PROPERTY_ADR);
+ builder.append(VCARD_ATTR_SEPARATOR);
+
+ // Attributes
{
boolean shouldAppendAttrSeparator = false;
- if (typeAsString != null) {
- appendTypeAttribute(builder, typeAsString);
+ if (!attributeList.isEmpty()) {
+ appendTypeAttributes(builder, attributeList);
shouldAppendAttrSeparator = true;
}
- if (dataExists) {
- if (shouldAppendCharset) {
- // Strictly, vCard 3.0 does not allow exporters to emit charset information,
- // but we will add it since the information should be useful for importers,
- //
- // Assume no parser does not emit error with this attribute in vCard 3.0.
- if (shouldAppendAttrSeparator) {
- builder.append(VCARD_ATTR_SEPARATOR);
- }
- builder.append(mVCardAttributeCharset);
- shouldAppendAttrSeparator = true;
+ if (appendCharset) {
+ // Strictly, vCard 3.0 does not allow exporters to emit charset information,
+ // but we will add it since the information should be useful for importers,
+ //
+ // Assume no parser does not emit error with this attribute in vCard 3.0.
+ if (shouldAppendAttrSeparator) {
+ builder.append(VCARD_ATTR_SEPARATOR);
}
+ builder.append(mVCardAttributeCharset);
+ shouldAppendAttrSeparator = true;
+ }
- if (actuallyUseQuotedPrintable) {
- if (shouldAppendAttrSeparator) {
- builder.append(VCARD_ATTR_SEPARATOR);
- }
- builder.append(VCARD_ATTR_ENCODING_QP);
- shouldAppendAttrSeparator = true;
+ if (reallyUseQuotedPrintable) {
+ if (shouldAppendAttrSeparator) {
+ builder.append(VCARD_ATTR_SEPARATOR);
}
+ builder.append(VCARD_ATTR_ENCODING_QP);
+ shouldAppendAttrSeparator = true;
}
}
- // Property values.
-
builder.append(VCARD_DATA_SEPARATOR);
- if (dataExists) {
- // The elements in dataArray are already encoded to quoted printable
- // if needed.
- // See above.
- //
- // TODO: in vCard 3.0, one line may become too huge. Fix this.
- builder.append(dataArray[0]);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(dataArray[1]);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(dataArray[2]);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(dataArray[3]);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(dataArray[4]);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(dataArray[5]);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(dataArray[6]);
- }
+ builder.append(addressData);
builder.append(VCARD_COL_SEPARATOR);
}
private void appendVCardEmailLine(final StringBuilder builder,
- final Integer typeAsObject, final String label, final String data) {
- builder.append(VCARD_PROPERTY_EMAIL);
-
- final int typeAsPrimitive;
- if (typeAsObject == null) {
- typeAsPrimitive = Email.TYPE_OTHER;
- } else {
- typeAsPrimitive = typeAsObject;
- }
-
+ final int type, final String label,
+ final String rawData, final boolean isPrimary) {
final String typeAsString;
- switch (typeAsPrimitive) {
+ switch (type) {
case Email.TYPE_CUSTOM: {
// For backward compatibility.
// Detail: Until Donut, there isn't TYPE_MOBILE for email while there is now.
@@ -1706,7 +1792,7 @@
&& VCardUtils.containsOnlyAlphaDigitHyphen(label)) {
typeAsString = "X-" + label;
} else {
- typeAsString = DEFAULT_EMAIL_TYPE;
+ typeAsString = null;
}
break;
}
@@ -1719,7 +1805,7 @@
break;
}
case Email.TYPE_OTHER: {
- typeAsString = DEFAULT_EMAIL_TYPE;
+ typeAsString = null;
break;
}
case Email.TYPE_MOBILE: {
@@ -1727,23 +1813,28 @@
break;
}
default: {
- Log.e(LOG_TAG, "Unknown Email type: " + typeAsPrimitive);
- typeAsString = DEFAULT_EMAIL_TYPE;
+ Log.e(LOG_TAG, "Unknown Email type: " + type);
+ typeAsString = null;
break;
}
}
- builder.append(VCARD_ATTR_SEPARATOR);
- appendTypeAttribute(builder, typeAsString);
- builder.append(VCARD_DATA_SEPARATOR);
- builder.append(data);
- builder.append(VCARD_COL_SEPARATOR);
+ final List<String> attributeList = new ArrayList<String>();
+ if (isPrimary) {
+ attributeList.add(Constants.ATTR_TYPE_PREF);
+ }
+ if (!TextUtils.isEmpty(typeAsString)) {
+ attributeList.add(typeAsString);
+ }
+
+ appendVCardLineWithCharsetAndQPDetection(builder, Constants.PROPERTY_EMAIL,
+ attributeList, rawData);
}
private void appendVCardTelephoneLine(final StringBuilder builder,
final Integer typeAsObject, final String label,
- String encodedData) {
- builder.append(VCARD_PROPERTY_TEL);
+ final String encodedData, boolean isPrimary) {
+ builder.append(Constants.PROPERTY_TEL);
builder.append(VCARD_ATTR_SEPARATOR);
final int typeAsPrimitive;
@@ -1753,53 +1844,102 @@
typeAsPrimitive = typeAsObject;
}
+ ArrayList<String> attributeList = new ArrayList<String>();
switch (typeAsPrimitive) {
case Phone.TYPE_HOME:
- appendTypeAttributes(builder, Arrays.asList(
- Constants.ATTR_TYPE_HOME, Constants.ATTR_TYPE_VOICE));
+ attributeList.addAll(
+ Arrays.asList(Constants.ATTR_TYPE_HOME, Constants.ATTR_TYPE_VOICE));
break;
case Phone.TYPE_WORK:
- appendTypeAttributes(builder, Arrays.asList(
- Constants.ATTR_TYPE_WORK, Constants.ATTR_TYPE_VOICE));
+ attributeList.addAll(
+ Arrays.asList(Constants.ATTR_TYPE_WORK, Constants.ATTR_TYPE_VOICE));
break;
case Phone.TYPE_FAX_HOME:
- appendTypeAttributes(builder, Arrays.asList(
- Constants.ATTR_TYPE_HOME, Constants.ATTR_TYPE_FAX));
+ attributeList.addAll(
+ Arrays.asList(Constants.ATTR_TYPE_HOME, Constants.ATTR_TYPE_FAX));
break;
case Phone.TYPE_FAX_WORK:
- appendTypeAttributes(builder, Arrays.asList(
- Constants.ATTR_TYPE_WORK, Constants.ATTR_TYPE_FAX));
+ attributeList.addAll(
+ Arrays.asList(Constants.ATTR_TYPE_WORK, Constants.ATTR_TYPE_FAX));
break;
case Phone.TYPE_MOBILE:
- builder.append(Constants.ATTR_TYPE_CELL);
+ attributeList.add(Constants.ATTR_TYPE_CELL);
break;
case Phone.TYPE_PAGER:
if (mIsDoCoMo) {
// Not sure about the reason, but previous implementation had
// used "VOICE" instead of "PAGER"
- // Also, refrain from using appendType() so that "TYPE=" is never be appended.
- builder.append(Constants.ATTR_TYPE_VOICE);
+ attributeList.add(Constants.ATTR_TYPE_VOICE);
} else {
- appendTypeAttribute(builder, Constants.ATTR_TYPE_PAGER);
+ attributeList.add(Constants.ATTR_TYPE_PAGER);
}
break;
case Phone.TYPE_OTHER:
- appendTypeAttribute(builder, Constants.ATTR_TYPE_VOICE);
+ attributeList.add(Constants.ATTR_TYPE_VOICE);
+ break;
+ case Phone.TYPE_CAR:
+ attributeList.add(Constants.ATTR_TYPE_CAR);
+ break;
+ case Phone.TYPE_COMPANY_MAIN:
+ // There's no relevant field in vCard (at least 2.1).
+ attributeList.add(Constants.ATTR_TYPE_WORK);
+ isPrimary = true;
+ break;
+ case Phone.TYPE_ISDN:
+ attributeList.add(Constants.ATTR_TYPE_ISDN);
+ break;
+ case Phone.TYPE_MAIN:
+ isPrimary = true;
+ break;
+ case Phone.TYPE_OTHER_FAX:
+ attributeList.add(Constants.ATTR_TYPE_FAX);
+ break;
+ case Phone.TYPE_TELEX:
+ attributeList.add(Constants.ATTR_TYPE_TLX);
+ break;
+ case Phone.TYPE_WORK_MOBILE:
+ attributeList.addAll(
+ Arrays.asList(Constants.ATTR_TYPE_WORK, Constants.ATTR_TYPE_CELL));
+ break;
+ case Phone.TYPE_WORK_PAGER:
+ attributeList.add(Constants.ATTR_TYPE_WORK);
+ // See above.
+ if (mIsDoCoMo) {
+ attributeList.add(Constants.ATTR_TYPE_VOICE);
+ } else {
+ attributeList.add(Constants.ATTR_TYPE_PAGER);
+ }
+ break;
+ case Phone.TYPE_MMS:
+ attributeList.add(Constants.ATTR_TYPE_MSG);
break;
case Phone.TYPE_CUSTOM:
if (mUsesAndroidProperty && !TextUtils.isEmpty(label)
&& VCardUtils.containsOnlyAlphaDigitHyphen(label)) {
- appendTypeAttribute(builder, "X-" + label);
+ // Note: Strictly, vCard 2.1 does not allow "X-" attribute without
+ // "TYPE=" string.
+ attributeList.add("X-" + label);
} else {
// Just ignore the custom type.
- appendTypeAttribute(builder, Constants.ATTR_TYPE_VOICE);
+ attributeList.add(Constants.ATTR_TYPE_VOICE);
}
break;
+ case Phone.TYPE_RADIO:
+ case Phone.TYPE_TTY_TDD:
default:
- appendUncommonPhoneType(builder, typeAsPrimitive);
break;
}
+ if (isPrimary) {
+ attributeList.add(Constants.ATTR_TYPE_PREF);
+ }
+
+ if (attributeList.isEmpty()) {
+ appendUncommonPhoneType(builder, typeAsPrimitive);
+ } else {
+ appendTypeAttributes(builder, attributeList);
+ }
+
builder.append(VCARD_DATA_SEPARATOR);
builder.append(encodedData);
builder.append(VCARD_COL_SEPARATOR);
@@ -1823,15 +1963,43 @@
}
}
+ private void appendVCardLineWithCharsetAndQPDetection(final StringBuilder builder,
+ final String propertyName, final String rawData) {
+ appendVCardLineWithCharsetAndQPDetection(builder, propertyName, null, rawData);
+ }
+
+ private void appendVCardLineWithCharsetAndQPDetection(final StringBuilder builder,
+ final String propertyName,
+ final List<String> attributeList, final String rawData) {
+ final boolean needCharset =
+ (mUsesQuotedPrintable && !VCardUtils.containsOnlyPrintableAscii(rawData));
+ final boolean reallyUseQuotedPrintable =
+ !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawData);
+ appendVCardLine(builder, propertyName, attributeList,
+ rawData, needCharset, reallyUseQuotedPrintable);
+ }
+
private void appendVCardLine(final StringBuilder builder,
final String propertyName, final String rawData) {
appendVCardLine(builder, propertyName, rawData, false, false);
}
private void appendVCardLine(final StringBuilder builder,
- final String field, final String rawData, final boolean needCharset,
+ final String propertyName, final String rawData, final boolean needCharset,
boolean needQuotedPrintable) {
- builder.append(field);
+ appendVCardLine(builder, propertyName, null, rawData, needCharset, needQuotedPrintable);
+ }
+
+ private void appendVCardLine(final StringBuilder builder,
+ final String propertyName,
+ final List<String> attributeList,
+ final String rawData, final boolean needCharset,
+ boolean needQuotedPrintable) {
+ builder.append(propertyName);
+ if (attributeList != null && attributeList.size() > 0) {
+ builder.append(VCARD_ATTR_SEPARATOR);
+ appendTypeAttributes(builder, attributeList);
+ }
if (needCharset) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(mVCardAttributeCharset);
@@ -1853,6 +2021,9 @@
builder.append(VCARD_COL_SEPARATOR);
}
+ /**
+ * VCARD_ATTR_SEPARATOR must be appended before this method being called.
+ */
private void appendTypeAttributes(final StringBuilder builder,
final List<String> types) {
// We may have to make this comma separated form like "TYPE=DOM,WORK" in the future,
@@ -1868,9 +2039,15 @@
}
}
+ /**
+ * VCARD_ATTR_SEPARATOR must be appended before this method being called.
+ */
private void appendTypeAttribute(final StringBuilder builder, final String type) {
+ // Refrain from using appendType() so that "TYPE=" is not be appended when the
+ // device is DoCoMo's (just for safety).
+ //
// Note: In vCard 3.0, Type strings also can be like this: "TYPE=HOME,PREF"
- if (mIsV30) {
+ if ((mIsV30 || mAppendTypeParamName) && !mIsDoCoMo) {
builder.append(Constants.ATTR_TYPE).append(VCARD_ATTR_EQUAL);
}
builder.append(type);
@@ -1961,4 +2138,116 @@
return tmpBuilder.toString();
}
+
+ //// The methods bellow are for call log history ////
+
+ /**
+ * This static function is to compose vCard for phone own number
+ */
+ public String composeVCardForPhoneOwnNumber(int phonetype, String phoneName,
+ String phoneNumber, boolean vcardVer21) {
+ final StringBuilder builder = new StringBuilder();
+ appendVCardLine(builder, Constants.PROPERTY_BEGIN, VCARD_DATA_VCARD);
+ if (!vcardVer21) {
+ appendVCardLine(builder, Constants.PROPERTY_VERSION, Constants.VERSION_V30);
+ } else {
+ appendVCardLine(builder, Constants.PROPERTY_VERSION, Constants.VERSION_V21);
+ }
+
+ boolean needCharset = false;
+ if (!(VCardUtils.containsOnlyPrintableAscii(phoneName))) {
+ needCharset = true;
+ }
+ appendVCardLine(builder, Constants.PROPERTY_FN, phoneName, needCharset, false);
+ appendVCardLine(builder, Constants.PROPERTY_N, phoneName, needCharset, false);
+
+ String label = Integer.toString(phonetype);
+ appendVCardTelephoneLine(builder, phonetype, label, phoneNumber, false);
+
+ appendVCardLine(builder, Constants.PROPERTY_END, VCARD_DATA_VCARD);
+
+ return builder.toString();
+ }
+
+ /**
+ * Format according to RFC 2445 DATETIME type.
+ * The format is: ("%Y%m%dT%H%M%SZ").
+ */
+ private final String toRfc2455Format(final long millSecs) {
+ Time startDate = new Time();
+ startDate.set(millSecs);
+ String date = startDate.format2445();
+ return date + FLAG_TIMEZONE_UTC;
+ }
+
+ /**
+ * Try to append the property line for a call history time stamp field if possible.
+ * Do nothing if the call log type gotton from the database is invalid.
+ */
+ private void tryAppendCallHistoryTimeStampField(final StringBuilder builder) {
+ // Extension for call history as defined in
+ // in the Specification for Ic Mobile Communcation - ver 1.1,
+ // Oct 2000. This is used to send the details of the call
+ // history - missed, incoming, outgoing along with date and time
+ // to the requesting device (For example, transferring phone book
+ // when connected over bluetooth)
+ //
+ // e.g. "X-IRMC-CALL-DATETIME;MISSED:20050320T100000Z"
+ final int callLogType = mCursor.getInt(CALL_TYPE_COLUMN_INDEX);
+ final String callLogTypeStr;
+ switch (callLogType) {
+ case Calls.INCOMING_TYPE: {
+ callLogTypeStr = VCARD_PROPERTY_CALLTYPE_INCOMING;
+ break;
+ }
+ case Calls.OUTGOING_TYPE: {
+ callLogTypeStr = VCARD_PROPERTY_CALLTYPE_OUTGOING;
+ break;
+ }
+ case Calls.MISSED_TYPE: {
+ callLogTypeStr = VCARD_PROPERTY_CALLTYPE_MISSED;
+ break;
+ }
+ default: {
+ Log.w(LOG_TAG, "Call log type not correct.");
+ return;
+ }
+ }
+
+ final long dateAsLong = mCursor.getLong(DATE_COLUMN_INDEX);
+ builder.append(VCARD_PROPERTY_X_TIMESTAMP);
+ builder.append(VCARD_ATTR_SEPARATOR);
+ appendTypeAttribute(builder, callLogTypeStr);
+ builder.append(VCARD_DATA_SEPARATOR);
+ builder.append(toRfc2455Format(dateAsLong));
+ builder.append(VCARD_COL_SEPARATOR);
+ }
+
+ private String createOneCallLogEntryInternal() {
+ final StringBuilder builder = new StringBuilder();
+ appendVCardLine(builder, Constants.PROPERTY_BEGIN, VCARD_DATA_VCARD);
+ if (mIsV30) {
+ appendVCardLine(builder, Constants.PROPERTY_VERSION, Constants.VERSION_V30);
+ } else {
+ appendVCardLine(builder, Constants.PROPERTY_VERSION, Constants.VERSION_V21);
+ }
+ String name = mCursor.getString(CALLER_NAME_COLUMN_INDEX);
+ if (TextUtils.isEmpty(name)) {
+ name = mCursor.getString(NUMBER_COLUMN_INDEX);
+ }
+ final boolean needCharset = !(VCardUtils.containsOnlyPrintableAscii(name));
+ appendVCardLine(builder, Constants.PROPERTY_FN, name, needCharset, false);
+ appendVCardLine(builder, Constants.PROPERTY_N, name, needCharset, false);
+
+ String number = mCursor.getString(NUMBER_COLUMN_INDEX);
+ int type = mCursor.getInt(CALLER_NUMBERTYPE_COLUMN_INDEX);
+ String label = mCursor.getString(CALLER_NUMBERLABEL_COLUMN_INDEX);
+ if (TextUtils.isEmpty(label)) {
+ label = Integer.toString(type);
+ }
+ appendVCardTelephoneLine(builder, type, label, number, false);
+ tryAppendCallHistoryTimeStampField(builder);
+ appendVCardLine(builder, Constants.PROPERTY_END, VCARD_DATA_VCARD);
+ return builder.toString();
+ }
}
diff --git a/core/java/android/pim/vcard/VCardConfig.java b/core/java/android/pim/vcard/VCardConfig.java
index 68cd0df..9581c74 100644
--- a/core/java/android/pim/vcard/VCardConfig.java
+++ b/core/java/android/pim/vcard/VCardConfig.java
@@ -15,14 +15,20 @@
*/
package android.pim.vcard;
+import android.util.Log;
+
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
/**
* The class representing VCard related configurations. Useful static methods are not in this class
* but in VCardUtils.
*/
public class VCardConfig {
+ private static final String LOG_TAG = "vcard.VCardConfig";
+
// TODO: may be better to make the instance of this available and stop using static methods and
// one integer.
@@ -41,8 +47,8 @@
// TODO: make the other codes use this flag
public static final boolean IGNORE_CASE_EXCEPT_VALUE = true;
- private static final int FLAG_V21 = 0;
- private static final int FLAG_V30 = 1;
+ public static final int FLAG_V21 = 0;
+ public static final int FLAG_V30 = 1;
// 0x2 is reserved for the future use ...
@@ -95,96 +101,187 @@
private static final int FLAG_DOCOMO = 0x20000000;
/**
- * The flag indicating the vCard composer use Quoted-Printable toward even "primary" types.
- * In this context, "primary" types means "N", "FN", etc. which are usually "not" encoded
- * into Quoted-Printable format in external exporters.
- * This flag is useful when some target importer does not accept "primary" property values
- * without Quoted-Printable encoding.
- *
- * @hide Temporaly made public. We don't strictly define "primary", so we may change the
- * behavior around this flag in the future. Do not use this flag without any reason.
+ * <P>
+ * The flag indicating the vCard composer does "NOT" use Quoted-Printable toward "primary"
+ * properties even though it is required by vCard 2.1 (QP is prohibited in vCard 3.0).
+ * </P>
+ * <P>
+ * We actually cannot define what is the "primary" property. Note that this is NOT defined
+ * in vCard specification either. Also be aware that it is NOT related to "primary" notion
+ * used in {@link android.provider.ContactsContract}.
+ * This notion is just for vCard composition in Android.
+ * </P>
+ * <P>
+ * We added this Android-specific notion since some (incomplete) vCard exporters for vCard 2.1
+ * do NOT use Quoted-Printable encoding toward some properties like "N", "FN", etc. even when
+ * their values contain non-ascii or/and CR/LF, while they use the encoding in the other
+ * properties like "ADR", "ORG", etc.
+ * <P>
+ * We are afraid of the case where some vCard importer also forget handling QP presuming QP is
+ * not used in such fields.
+ * </P>
+ * <P>
+ * This flag is useful when some target importer you are going to focus on does not accept
+ * such "primary" property values with Quoted-Printable encoding.
+ * </P>
+ * <P>
+ * Again, we should not use this flag at all for complying vCard 2.1 spec.
+ * </P>
+ * <P>
+ * We will change the behavior around this flag in the future, after understanding the other
+ * real vCard cases around this problem. Please use this flag with extreme caution even when
+ * needed.
+ * </P>
+ * <P>
+ * In vCard 3.0, Quoted-Printable is explicitly "prohibitted", so we don't need to care this
+ * kind of problem (hopefully).
+ * </P>
*/
- public static final int FLAG_USE_QP_TO_PRIMARY_PROPERTIES = 0x10000000;
-
- // VCard types
+ public static final int FLAG_REFRAIN_QP_TO_PRIMARY_PROPERTIES = 0x10000000;
/**
- * General vCard format with the version 2.1. Uses UTF-8 for the charset.
- * When composing a vCard entry, the US convension will be used.
- *
- * e.g. The order of the display name would be "Prefix Given Middle Family Suffix",
- * while in Japan, it should be "Prefix Family Middle Given Suffix".
+ * <P>
+ * The flag indicating that phonetic name related fields must be converted to
+ * appropriate form. Note that "appropriate" is not defined in any vCard specification.
+ * This is Android-specific.
+ * </P>
+ * <P>
+ * One typical (and currently sole) example where we need this flag is the time when
+ * we need to emit Japanese phonetic names into vCard entries. The property values
+ * should be encoded into half-width katakana when the target importer is Japanese mobile
+ * phones', which are probably not able to parse full-width hiragana/katakana for
+ * historical reasons, while the vCard importers embedded to softwares for PC should be
+ * able to parse them as we expect.
+ * </P>
*/
- public static final int VCARD_TYPE_V21_GENERIC =
+ public static final int FLAG_CONVERT_PHONETIC_NAME_STRINGS = 0x0800000;
+
+ /**
+ * <P>
+ * The flag indicating the vCard composer "for 2.1" emits "TYPE=" string every time
+ * possible. The default behavior does not emit it and is valid, while adding "TYPE="
+ * is also valid. In vCrad 3.0, this flag is unnecessary, since "TYPE=" is MUST in
+ * vCard 3.0 specification.
+ *
+ * If you are targeting to some importer which cannot accept type attributes (params)
+ * without "TYPE=" string (which should be rare though), please use this flag.
+ *
+ * XXX: Really rare?
+ *
+ * e.g. int vcardType = (VCARD_TYPE_V21_GENERIC | FLAG_APPEND_TYPE_PARAM);
+ */
+ public static final int FLAG_APPEND_TYPE_PARAM = 0x04000000;
+
+ //// The followings are VCard types available from importer/exporter. ////
+
+ /**
+ * <P>
+ * General vCard format with the version 2.1. Uses UTF-8 for the charset.
+ * When composing a vCard entry, the US convension will be used toward formatting
+ * some values
+ * </P>
+ * <P>
+ * e.g. The order of the display name would be "Prefix Given Middle Family Suffix",
+ * while it should be "Prefix Family Middle Given Suffix" in Japan.
+ * </P>
+ */
+ public static final int VCARD_TYPE_V21_GENERIC_UTF8 =
(FLAG_V21 | NAME_ORDER_DEFAULT | FLAG_CHARSET_UTF8 |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
- /* package */ static String VCARD_TYPE_V21_GENERIC_STR = "v21_generic";
+ /* package */ static String VCARD_TYPE_V21_GENERIC_UTF8_STR = "v21_generic";
/**
+ * <P>
* General vCard format with the version 3.0. Uses UTF-8 for the charset.
- *
- * Note that this type is not fully implemented, so probably some bugs remain both in
- * parsing and composing.
- *
- * TODO: implement this type correctly.
+ * </P>
+ * <P>
+ * Not ready yet. Use with caution when you use this.
+ * </P>
*/
- public static final int VCARD_TYPE_V30_GENERIC =
+ public static final int VCARD_TYPE_V30_GENERIC_UTF8 =
(FLAG_V30 | NAME_ORDER_DEFAULT | FLAG_CHARSET_UTF8 |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
- /* package */ static final String VCARD_TYPE_V30_GENERIC_STR = "v30_generic";
+ /* package */ static final String VCARD_TYPE_V30_GENERIC_UTF8_STR = "v30_generic";
/**
- * General vCard format with the version 2.1 with some Europe convension. Uses Utf-8.
+ * <P>
+ * General vCard format for the vCard 2.1 with some Europe convension. Uses Utf-8.
* Currently, only name order is considered ("Prefix Middle Given Family Suffix")
+ * </P>
*/
- public static final int VCARD_TYPE_V21_EUROPE =
+ public static final int VCARD_TYPE_V21_EUROPE_UTF8 =
(FLAG_V21 | NAME_ORDER_EUROPE | FLAG_CHARSET_UTF8 |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
- /* package */ static final String VCARD_TYPE_V21_EUROPE_STR = "v21_europe";
+ /* package */ static final String VCARD_TYPE_V21_EUROPE_UTF8_STR = "v21_europe";
/**
+ * <P>
* General vCard format with the version 3.0 with some Europe convension. Uses UTF-8
+ * </P>
+ * <P>
+ * Not ready yet. Use with caution when you use this.
+ * </P>
*/
- public static final int VCARD_TYPE_V30_EUROPE =
+ public static final int VCARD_TYPE_V30_EUROPE_UTF8 =
(FLAG_V30 | NAME_ORDER_EUROPE | FLAG_CHARSET_UTF8 |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static final String VCARD_TYPE_V30_EUROPE_STR = "v30_europe";
-
- /**
- * vCard 2.1 format for miscellaneous Japanese devices. Shift_Jis is used for
- * parsing/composing the vCard data.
- */
- public static final int VCARD_TYPE_V21_JAPANESE =
- (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
- FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
- /* package */ static final String VCARD_TYPE_V21_JAPANESE_STR = "v21_japanese";
-
/**
- * vCard 2.1 format for miscellaneous Japanese devices, using UTF-8 as default charset.
+ * <P>
+ * The vCard 2.1 format for miscellaneous Japanese devices, using UTF-8 as default charset.
+ * </P>
+ * <P>
+ * Not ready yet. Use with caution when you use this.
+ * </P>
*/
public static final int VCARD_TYPE_V21_JAPANESE_UTF8 =
(FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_UTF8 |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static final String VCARD_TYPE_V21_JAPANESE_UTF8_STR = "v21_japanese_utf8";
+
+ /**
+ * <P>
+ * vCard 2.1 format for miscellaneous Japanese devices. Shift_Jis is used for
+ * parsing/composing the vCard data.
+ * </P>
+ * <P>
+ * Not ready yet. Use with caution when you use this.
+ * </P>
+ */
+ public static final int VCARD_TYPE_V21_JAPANESE_SJIS =
+ (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
+ FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
+
+ /* package */ static final String VCARD_TYPE_V21_JAPANESE_SJIS_STR = "v21_japanese_sjis";
/**
+ * <P>
* vCard format for miscellaneous Japanese devices, using Shift_Jis for
* parsing/composing the vCard data.
+ * </P>
+ * <P>
+ * Not ready yet. Use with caution when you use this.
+ * </P>
*/
- public static final int VCARD_TYPE_V30_JAPANESE =
+ public static final int VCARD_TYPE_V30_JAPANESE_SJIS =
(FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
- /* package */ static final String VCARD_TYPE_V30_JAPANESE_STR = "v30_japanese";
+ /* package */ static final String VCARD_TYPE_V30_JAPANESE_SJIS_STR = "v30_japanese_sjis";
/**
- * vCard 3.0 format for miscellaneous Japanese devices, using UTF-8 as default charset.
+ * <P>
+ * The vCard 3.0 format for miscellaneous Japanese devices, using UTF-8 as default charset.
+ * </P>
+ * <P>
+ * Not ready yet. Use with caution when you use this.
+ * </P>
*/
public static final int VCARD_TYPE_V30_JAPANESE_UTF8 =
(FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_CHARSET_UTF8 |
@@ -193,38 +290,72 @@
/* package */ static final String VCARD_TYPE_V30_JAPANESE_UTF8_STR = "v30_japanese_utf8";
/**
- * VCard format used in DoCoMo, which is one of Japanese mobile phone careers.
- * Base version is vCard 2.1, but the data has several DoCoMo-specific convensions.
- * No Android-specific property nor defact property is included.
+ * <P>
+ * The vCard 2.1 based format which (partially) considers the convention in Japanese
+ * mobile phones, where phonetic names are translated to half-width katakana if
+ * possible, etc.
+ * </P>
+ * <P>
+ * Not ready yet. Use with caution when you use this.
+ * </P>
+ */
+ public static final int VCARD_TYPE_V21_JAPANESE_MOBILE =
+ (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
+ FLAG_CONVERT_PHONETIC_NAME_STRINGS |
+ FLAG_REFRAIN_QP_TO_PRIMARY_PROPERTIES);
+
+ public static final String VCARD_TYPE_V21_JAPANESE_MOBILE_STR = "v21_japanese_mobile";
+
+ /**
+ * <P>
+ * VCard format used in DoCoMo, which is one of Japanese mobile phone careers.
+ * </p>
+ * <P>
+ * Base version is vCard 2.1, but the data has several DoCoMo-specific convensions.
+ * No Android-specific property nor defact property is included. The "Primary" properties
+ * are NOT encoded to Quoted-Printable.
+ * </P>
*/
public static final int VCARD_TYPE_DOCOMO =
- (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS | FLAG_DOCOMO);
+ (VCARD_TYPE_V21_JAPANESE_MOBILE | FLAG_DOCOMO);
private static final String VCARD_TYPE_DOCOMO_STR = "docomo";
- public static int VCARD_TYPE_DEFAULT = VCARD_TYPE_V21_GENERIC;
+ public static int VCARD_TYPE_DEFAULT = VCARD_TYPE_V21_GENERIC_UTF8;
- private static final Map<String, Integer> VCARD_TYPES_MAP;
+ private static final Map<String, Integer> sVCardTypeMap;
+ private static final Set<Integer> sJapaneseMobileTypeSet;
static {
- VCARD_TYPES_MAP = new HashMap<String, Integer>();
- VCARD_TYPES_MAP.put(VCARD_TYPE_V21_GENERIC_STR, VCARD_TYPE_V21_GENERIC);
- VCARD_TYPES_MAP.put(VCARD_TYPE_V30_GENERIC_STR, VCARD_TYPE_V30_GENERIC);
- VCARD_TYPES_MAP.put(VCARD_TYPE_V21_EUROPE_STR, VCARD_TYPE_V21_EUROPE);
- VCARD_TYPES_MAP.put(VCARD_TYPE_V30_EUROPE_STR, VCARD_TYPE_V30_EUROPE);
- VCARD_TYPES_MAP.put(VCARD_TYPE_V21_JAPANESE_STR, VCARD_TYPE_V21_JAPANESE);
- VCARD_TYPES_MAP.put(VCARD_TYPE_V21_JAPANESE_UTF8_STR, VCARD_TYPE_V21_JAPANESE_UTF8);
- VCARD_TYPES_MAP.put(VCARD_TYPE_V30_JAPANESE_STR, VCARD_TYPE_V30_JAPANESE);
- VCARD_TYPES_MAP.put(VCARD_TYPE_V30_JAPANESE_UTF8_STR, VCARD_TYPE_V30_JAPANESE_UTF8);
- VCARD_TYPES_MAP.put(VCARD_TYPE_DOCOMO_STR, VCARD_TYPE_DOCOMO);
+ sVCardTypeMap = new HashMap<String, Integer>();
+ sVCardTypeMap.put(VCARD_TYPE_V21_GENERIC_UTF8_STR, VCARD_TYPE_V21_GENERIC_UTF8);
+ sVCardTypeMap.put(VCARD_TYPE_V30_GENERIC_UTF8_STR, VCARD_TYPE_V30_GENERIC_UTF8);
+ sVCardTypeMap.put(VCARD_TYPE_V21_EUROPE_UTF8_STR, VCARD_TYPE_V21_EUROPE_UTF8);
+ sVCardTypeMap.put(VCARD_TYPE_V30_EUROPE_STR, VCARD_TYPE_V30_EUROPE_UTF8);
+ sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_SJIS_STR, VCARD_TYPE_V21_JAPANESE_SJIS);
+ sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_UTF8_STR, VCARD_TYPE_V21_JAPANESE_UTF8);
+ sVCardTypeMap.put(VCARD_TYPE_V30_JAPANESE_SJIS_STR, VCARD_TYPE_V30_JAPANESE_SJIS);
+ sVCardTypeMap.put(VCARD_TYPE_V30_JAPANESE_UTF8_STR, VCARD_TYPE_V30_JAPANESE_UTF8);
+ sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_MOBILE_STR, VCARD_TYPE_V21_JAPANESE_MOBILE);
+ sVCardTypeMap.put(VCARD_TYPE_DOCOMO_STR, VCARD_TYPE_DOCOMO);
+
+ sJapaneseMobileTypeSet = new HashSet<Integer>();
+ sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_SJIS);
+ sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_UTF8);
+ sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_SJIS);
+ sJapaneseMobileTypeSet.add(VCARD_TYPE_V30_JAPANESE_SJIS);
+ sJapaneseMobileTypeSet.add(VCARD_TYPE_V30_JAPANESE_UTF8);
+ sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_MOBILE);
+ sJapaneseMobileTypeSet.add(VCARD_TYPE_DOCOMO);
}
public static int getVCardTypeFromString(String vcardTypeString) {
String loweredKey = vcardTypeString.toLowerCase();
- if (VCARD_TYPES_MAP.containsKey(loweredKey)) {
- return VCARD_TYPES_MAP.get(loweredKey);
+ if (sVCardTypeMap.containsKey(loweredKey)) {
+ return sVCardTypeMap.get(loweredKey);
} else {
// XXX: should return the value indicating the input is invalid?
+ Log.e(LOG_TAG, "Unknown vCard type String: \"" + vcardTypeString + "\"");
return VCARD_TYPE_DEFAULT;
}
}
@@ -237,22 +368,6 @@
return !isV30(vcardType);
}
- public static boolean isDoCoMo(int vcardType) {
- return ((vcardType & FLAG_DOCOMO) != 0);
- }
-
- /**
- * @return true if the device is Japanese and some Japanese convension is
- * applied to creating "formatted" something like FORMATTED_ADDRESS.
- */
- public static boolean isJapaneseDevice(int vcardType) {
- return ((vcardType == VCARD_TYPE_V21_JAPANESE) ||
- (vcardType == VCARD_TYPE_V21_JAPANESE_UTF8) ||
- (vcardType == VCARD_TYPE_V30_JAPANESE) ||
- (vcardType == VCARD_TYPE_V30_JAPANESE_UTF8) ||
- (vcardType == VCARD_TYPE_DOCOMO));
- }
-
public static boolean usesUtf8(int vcardType) {
return ((vcardType & FLAG_CHARSET_UTF8) != 0);
}
@@ -261,17 +376,6 @@
return ((vcardType & FLAG_CHARSET_SHIFT_JIS) != 0);
}
- /**
- * @return true when Japanese phonetic string must be converted to a string
- * containing only half-width katakana. This method exists since Japanese mobile
- * phones usually use only half-width katakana for expressing phonetic names and
- * some devices are not ready for parsing other phonetic strings like hiragana and
- * full-width katakana.
- */
- public static boolean needsToConvertPhoneticString(int vcardType) {
- return (vcardType == VCARD_TYPE_DOCOMO);
- }
-
public static int getNameOrderType(int vcardType) {
return vcardType & NAME_ORDER_MASK;
}
@@ -284,20 +388,37 @@
return ((vcardType & FLAG_USE_DEFACT_PROPERTY) != 0);
}
- public static boolean onlyOneNoteFieldIsAvailable(int vcardType) {
- return vcardType == VCARD_TYPE_DOCOMO;
- }
-
public static boolean showPerformanceLog() {
return (VCardConfig.LOG_LEVEL & VCardConfig.LOG_LEVEL_PERFORMANCE_MEASUREMENT) != 0;
}
+ public static boolean refrainsQPToPrimaryProperties(int vcardType) {
+ return (!usesQuotedPrintable(vcardType) ||
+ ((vcardType & FLAG_REFRAIN_QP_TO_PRIMARY_PROPERTIES) != 0));
+ }
+
+ public static boolean appendTypeParamName(int vcardType) {
+ return (isV30(vcardType) || ((vcardType & FLAG_APPEND_TYPE_PARAM) != 0));
+ }
+
/**
- * @hide
+ * @return true if the device is Japanese and some Japanese convension is
+ * applied to creating "formatted" something like FORMATTED_ADDRESS.
*/
- public static boolean usesQPToPrimaryProperties(int vcardType) {
- return (usesQuotedPrintable(vcardType) &&
- ((vcardType & FLAG_USE_QP_TO_PRIMARY_PROPERTIES) != 0));
+ public static boolean isJapaneseDevice(int vcardType) {
+ return sJapaneseMobileTypeSet.contains(vcardType);
+ }
+
+ public static boolean needsToConvertPhoneticString(int vcardType) {
+ return ((vcardType & FLAG_CONVERT_PHONETIC_NAME_STRINGS) != 0);
+ }
+
+ public static boolean onlyOneNoteFieldIsAvailable(int vcardType) {
+ return vcardType == VCARD_TYPE_DOCOMO;
+ }
+
+ public static boolean isDoCoMo(int vcardType) {
+ return ((vcardType & FLAG_DOCOMO) != 0);
}
private VCardConfig() {
diff --git a/core/java/android/pim/vcard/VCardDataBuilder.java b/core/java/android/pim/vcard/VCardDataBuilder.java
index d2026d0..76ad482 100644
--- a/core/java/android/pim/vcard/VCardDataBuilder.java
+++ b/core/java/android/pim/vcard/VCardDataBuilder.java
@@ -69,7 +69,7 @@
private List<EntryHandler> mEntryHandlers = new ArrayList<EntryHandler>();
public VCardDataBuilder() {
- this(null, null, false, VCardConfig.VCARD_TYPE_V21_GENERIC, null);
+ this(null, null, false, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8, null);
}
/**
@@ -86,7 +86,7 @@
boolean strictLineBreakParsing, int vcardType, Account account) {
this(null, charset, strictLineBreakParsing, vcardType, account);
}
-
+
/**
* @hide
*/
@@ -127,6 +127,18 @@
}
/**
+ * Called when the parse failed between startRecord() and endRecord().
+ * Currently it happens only when the vCard format is 3.0.
+ * (VCardVersionException is thrown by VCardParser_V21 and this object is reused by
+ * VCardParser_V30. At that time, startRecord() is called twice before endRecord() is called.)
+ * TODO: Should this be in VCardBuilder interface?
+ */
+ public void clear() {
+ mCurrentContactStruct = null;
+ mCurrentProperty = new ContactStruct.Property();
+ }
+
+ /**
* Assume that VCard is not nested. In other words, this code does not accept
*/
public void startRecord(String type) {
diff --git a/core/java/android/pim/vcard/VCardParser_V21.java b/core/java/android/pim/vcard/VCardParser_V21.java
index 974fca8..b3ff8fa 100644
--- a/core/java/android/pim/vcard/VCardParser_V21.java
+++ b/core/java/android/pim/vcard/VCardParser_V21.java
@@ -144,10 +144,14 @@
}
}
- protected String getVersion() {
- return "2.1";
+ protected int getVersion() {
+ return VCardConfig.FLAG_V21;
}
-
+
+ protected String getVersionString() {
+ return Constants.VERSION_V21;
+ }
+
/**
* @return true when the propertyName is a valid property name.
*/
@@ -356,7 +360,7 @@
* / [groups "."] "ADR" [params] ":" addressparts CRLF
* / [groups "."] "ORG" [params] ":" orgparts CRLF
* / [groups "."] "N" [params] ":" nameparts CRLF
- * / [groups "."] "AGENT" [params] ":" vcard CRLF
+ * / [groups "."] "AGENT" [params] ":" vcard CRLF
*/
protected boolean parseItem() throws IOException, VCardException {
mEncoding = sDefaultEncoding;
@@ -392,9 +396,10 @@
} else {
throw new VCardException("Unknown BEGIN type: " + propertyValue);
}
- } else if (propertyName.equals("VERSION") && !propertyValue.equals(getVersion())) {
+ } else if (propertyName.equals("VERSION") &&
+ !propertyValue.equals(getVersionString())) {
throw new VCardVersionException("Incompatible version: " +
- propertyValue + " != " + getVersion());
+ propertyValue + " != " + getVersionString());
}
start = System.currentTimeMillis();
handlePropertyValue(propertyName, propertyValue);
@@ -520,11 +525,19 @@
throw new VCardException("Unknown type \"" + paramName + "\"");
}
} else {
- handleType(strArray[0]);
+ handleParamWithoutName(strArray[0]);
}
}
/**
+ * vCard 3.0 parser may throw VCardException.
+ */
+ @SuppressWarnings("unused")
+ protected void handleParamWithoutName(final String paramValue) throws VCardException {
+ handleType(paramValue);
+ }
+
+ /**
* ptypeval = knowntype / "X-" word
*/
protected void handleType(String ptypeval) {
@@ -761,32 +774,11 @@
}
if (mBuilder != null) {
- 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 = maybeUnescapeCharacter(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);
+ mBuilder.propertyValues(VCardUtils.constructListFromValue(
+ propertyValue, (getVersion() == VCardConfig.FLAG_V30)));
}
}
-
+
/**
* vCard 2.1 specifies AGENT allows one vcard entry. It is not encoded at all.
*
@@ -819,12 +811,16 @@
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 maybeUnescapeCharacter(char ch) {
+ return unescapeCharacter(ch);
+ }
+
+ public static String unescapeCharacter(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.
@@ -844,6 +840,9 @@
@Override
public boolean parse(InputStream is, String charset, VCardBuilder builder)
throws IOException, VCardException {
+ if (charset == null) {
+ charset = VCardConfig.DEFAULT_CHARSET;
+ }
final InputStreamReader tmpReader = new InputStreamReader(is, charset);
if (VCardConfig.showPerformanceLog()) {
mReader = new CustomBufferedReader(tmpReader);
diff --git a/core/java/android/pim/vcard/VCardParser_V30.java b/core/java/android/pim/vcard/VCardParser_V30.java
index 384649a..86e7625 100644
--- a/core/java/android/pim/vcard/VCardParser_V30.java
+++ b/core/java/android/pim/vcard/VCardParser_V30.java
@@ -46,14 +46,42 @@
private static final HashSet<String> acceptablePropsWithoutParam = new HashSet<String>();
private String mPreviousLine;
-
+
private boolean mEmittedAgentWarning = false;
-
+
+ /**
+ * True when the caller wants the parser to be strict about the input.
+ * Currently this is only for testing.
+ */
+ private final boolean mStrictParsing;
+
+ public VCardParser_V30() {
+ super();
+ mStrictParsing = false;
+ }
+
+ /**
+ * @param strictParsing when true, this object throws VCardException when the vcard is not
+ * valid from the view of vCard 3.0 specification (defined in RFC 2426). Note that this class
+ * is not fully yet for being used with this flag and may not notice invalid line(s).
+ *
+ * @hide currently only for testing!
+ */
+ public VCardParser_V30(boolean strictParsing) {
+ super();
+ mStrictParsing = strictParsing;
+ }
+
@Override
- protected String getVersion() {
+ protected int getVersion() {
+ return VCardConfig.FLAG_V30;
+ }
+
+ @Override
+ protected String getVersionString() {
return Constants.VERSION_V30;
}
-
+
@Override
protected boolean isValidPropertyName(String propertyName) {
if (!(sAcceptablePropsWithParam.contains(propertyName) ||
@@ -199,7 +227,16 @@
// TODO: fix this.
super.handleAnyParam(paramName, paramValue);
}
-
+
+ @Override
+ protected void handleParamWithoutName(final String paramValue) throws VCardException {
+ if (mStrictParsing) {
+ throw new VCardException("Parameter without name is not acceptable in vCard 3.0");
+ } else {
+ super.handleParamWithoutName(paramValue);
+ }
+ }
+
/**
* vCard 3.0 defines
*
@@ -284,6 +321,10 @@
*/
@Override
protected String maybeUnescapeText(String text) {
+ return unescapeText(text);
+ }
+
+ public static String unescapeText(String text) {
StringBuilder builder = new StringBuilder();
int length = text.length();
for (int i = 0; i < length; i++) {
@@ -299,15 +340,19 @@
builder.append(ch);
}
}
- return builder.toString();
+ return builder.toString();
}
@Override
protected String maybeUnescapeCharacter(char ch) {
+ return unescapeCharacter(ch);
+ }
+
+ public static String unescapeCharacter(char ch) {
if (ch == 'n' || ch == 'N') {
return "\n";
} else {
return String.valueOf(ch);
- }
+ }
}
}
diff --git a/core/java/android/pim/vcard/VCardUtils.java b/core/java/android/pim/vcard/VCardUtils.java
index 4f50103..376327c 100644
--- a/core/java/android/pim/vcard/VCardUtils.java
+++ b/core/java/android/pim/vcard/VCardUtils.java
@@ -18,13 +18,16 @@
import android.content.ContentProviderOperation;
import android.content.ContentValues;
import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.text.TextUtils;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -41,44 +44,56 @@
// vCard and current (as of 2009-08-07) Contacts structure.
private static final Map<Integer, String> sKnownPhoneTypesMap_ItoS;
private static final Set<String> sPhoneTypesSetUnknownToContacts;
-
- private static final Map<String, Integer> sKnownPhoneTypesMap_StoI;
-
+
+ private static final Map<String, Integer> sKnownPhoneTypeMap_StoI;
+
+ private static final Map<Integer, String> sKnownImPropNameMap_ItoS;
+
static {
sKnownPhoneTypesMap_ItoS = new HashMap<Integer, String>();
- sKnownPhoneTypesMap_StoI = new HashMap<String, Integer>();
+ sKnownPhoneTypeMap_StoI = new HashMap<String, Integer>();
sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_CAR, Constants.ATTR_TYPE_CAR);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_CAR, Phone.TYPE_CAR);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_TYPE_CAR, Phone.TYPE_CAR);
sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_PAGER, Constants.ATTR_TYPE_PAGER);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PAGER, Phone.TYPE_PAGER);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_TYPE_PAGER, Phone.TYPE_PAGER);
sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_ISDN, Constants.ATTR_TYPE_ISDN);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_ISDN, Phone.TYPE_ISDN);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_TYPE_ISDN, Phone.TYPE_ISDN);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_HOME, Phone.TYPE_HOME);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_WORK, Phone.TYPE_WORK);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_CELL, Phone.TYPE_MOBILE);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_TYPE_HOME, Phone.TYPE_HOME);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_TYPE_WORK, Phone.TYPE_WORK);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_TYPE_CELL, Phone.TYPE_MOBILE);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_OTHER, Phone.TYPE_OTHER);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_CALLBACK, Phone.TYPE_CALLBACK);
- sKnownPhoneTypesMap_StoI.put(
- Constants.ATTR_TYPE_PHONE_EXTRA_COMPANY_MAIN, Phone.TYPE_COMPANY_MAIN);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_RADIO, Phone.TYPE_RADIO);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_TELEX, Phone.TYPE_TELEX);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_TTY_TDD, Phone.TYPE_TTY_TDD);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_ASSISTANT, Phone.TYPE_ASSISTANT);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_PHONE_EXTRA_TYPE_OTHER, Phone.TYPE_OTHER);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_PHONE_EXTRA_TYPE_CALLBACK, Phone.TYPE_CALLBACK);
+ sKnownPhoneTypeMap_StoI.put(
+ Constants.ATTR_PHONE_EXTRA_TYPE_COMPANY_MAIN, Phone.TYPE_COMPANY_MAIN);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_PHONE_EXTRA_TYPE_RADIO, Phone.TYPE_RADIO);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_PHONE_EXTRA_TYPE_TTY_TDD, Phone.TYPE_TTY_TDD);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_PHONE_EXTRA_TYPE_ASSISTANT,
+ Phone.TYPE_ASSISTANT);
sPhoneTypesSetUnknownToContacts = new HashSet<String>();
sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_MODEM);
- sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_MSG);
sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_BBS);
sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_VIDEO);
+
+ sKnownImPropNameMap_ItoS = new HashMap<Integer, String>();
+ sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_AIM, Constants.PROPERTY_X_AIM);
+ sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_MSN, Constants.PROPERTY_X_MSN);
+ sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_YAHOO, Constants.PROPERTY_X_YAHOO);
+ sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_SKYPE, Constants.PROPERTY_X_SKYPE_USERNAME);
+ sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_GOOGLE_TALK, Constants.PROPERTY_X_GOOGLE_TALK);
+ sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_ICQ, Constants.PROPERTY_X_ICQ);
+ sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_JABBER, Constants.PROPERTY_X_JABBER);
+ sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_QQ, Constants.PROPERTY_X_QQ);
+ sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_NETMEETING, Constants.PROPERTY_X_NETMEETING);
}
-
+
public static String getPhoneAttributeString(Integer type) {
return sKnownPhoneTypesMap_ItoS.get(type);
}
-
+
/**
* Returns Interger when the given types can be parsed as known type. Returns String object
* when not, which should be set to label.
@@ -100,7 +115,7 @@
if (typeString.startsWith("X-") && type < 0) {
typeString = typeString.substring(2);
}
- Integer tmp = sKnownPhoneTypesMap_StoI.get(typeString);
+ Integer tmp = sKnownPhoneTypeMap_StoI.get(typeString);
if (tmp != null) {
type = tmp;
} else if (type < 0) {
@@ -133,7 +148,11 @@
return type;
}
}
-
+
+ public static String getPropertyNameForIm(int protocol) {
+ return sKnownImPropNameMap_ItoS.get(protocol);
+ }
+
public static boolean isValidPhoneAttribute(String phoneAttribute, int vcardType) {
// TODO: check the following.
// - it may violate vCard spec
@@ -187,7 +206,10 @@
}
builder.withValue(StructuredPostal.POBOX, postalData.pobox);
- // Extended address is dropped since there's no relevant entry in ContactsContract.
+ // TODO: Japanese phone seems to use this field for expressing all the address including
+ // region, city, etc. Not sure we're ok to store them into NEIGHBORHOOD, while it would be
+ // better than dropping them all.
+ builder.withValue(StructuredPostal.NEIGHBORHOOD, postalData.extendedAddress);
builder.withValue(StructuredPostal.STREET, postalData.street);
builder.withValue(StructuredPostal.CITY, postalData.localty);
builder.withValue(StructuredPostal.REGION, postalData.region);
@@ -200,12 +222,12 @@
builder.withValue(Data.IS_PRIMARY, 1);
}
}
-
+
/**
* Returns String[] containing address information based on vCard spec
* (PO Box, Extended Address, Street, Locality, Region, Postal Code, Country Name).
* All String objects are non-null ("" is used when the relevant data is empty).
- *
+ *
* Note that the data structure of ContactsContract is different from that defined in vCard.
* So some conversion may be performed in this method. See also
* {{@link #insertStructuredPostalDataUsingContactsStruct(int,
@@ -213,13 +235,20 @@
* android.pim.vcard.ContactStruct.PostalData)}
*/
public static String[] getVCardPostalElements(ContentValues contentValues) {
+ // adr-value = 0*6(text-value ";") text-value
+ // ; PO Box, Extended Address, Street, Locality, Region, Postal
+ // ; Code, Country Name
String[] dataArray = new String[7];
dataArray[0] = contentValues.getAsString(StructuredPostal.POBOX);
if (dataArray[0] == null) {
dataArray[0] = "";
}
- // Extended addr. There's no relevant data in ContactsContract.
- dataArray[1] = "";
+ // We keep all the data in StructuredPostal, presuming NEIGHBORHOOD is
+ // similar to "Extended Address".
+ dataArray[1] = contentValues.getAsString(StructuredPostal.NEIGHBORHOOD);
+ if (dataArray[1] == null) {
+ dataArray[1] = "";
+ }
dataArray[2] = contentValues.getAsString(StructuredPostal.STREET);
if (dataArray[2] == null) {
dataArray[2] = "";
@@ -282,7 +311,36 @@
}
return builder.toString();
}
-
+
+ public static List<String> constructListFromValue(final String value,
+ final boolean isV30) {
+ final List<String> list = new ArrayList<String>();
+ 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);
+ final String unescapedString =
+ (isV30 ? VCardParser_V30.unescapeCharacter(nextCh) :
+ VCardParser_V21.unescapeCharacter(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());
+ return list;
+ }
+
public static boolean containsOnlyPrintableAscii(String str) {
if (TextUtils.isEmpty(str)) {
return true;
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 08a2a9f..197d976 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -188,17 +188,7 @@
mContext = context;
TypedArray a = context.obtainStyledAttributes(attrs,
- com.android.internal.R.styleable.Preference);
- if (a.hasValue(com.android.internal.R.styleable.Preference_layout) ||
- a.hasValue(com.android.internal.R.styleable.Preference_widgetLayout)) {
- // This preference has a custom layout defined (not one taken from
- // the default style)
- mHasSpecifiedLayout = true;
- }
- a.recycle();
-
- a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Preference,
- defStyle, 0);
+ com.android.internal.R.styleable.Preference, defStyle, 0);
for (int i = a.getIndexCount(); i >= 0; i--) {
int attr = a.getIndex(i);
switch (attr) {
@@ -252,6 +242,11 @@
}
}
a.recycle();
+
+ if (!getClass().getName().startsWith("android.preference")) {
+ // For subclasses not in this package, assume the worst and don't cache views
+ mHasSpecifiedLayout = true;
+ }
}
/**
@@ -332,11 +327,11 @@
* @see #setWidgetLayoutResource(int)
*/
public void setLayoutResource(int layoutResId) {
-
- if (!mHasSpecifiedLayout) {
+ if (layoutResId != mLayoutResId) {
+ // Layout changed
mHasSpecifiedLayout = true;
}
-
+
mLayoutResId = layoutResId;
}
@@ -360,6 +355,10 @@
* @see #setLayoutResource(int)
*/
public void setWidgetLayoutResource(int widgetLayoutResId) {
+ if (widgetLayoutResId != mWidgetLayoutResId) {
+ // Layout changed
+ mHasSpecifiedLayout = true;
+ }
mWidgetLayoutResId = widgetLayoutResId;
}
diff --git a/core/java/android/preference/PreferenceGroupAdapter.java b/core/java/android/preference/PreferenceGroupAdapter.java
index 14c0054..a908ecd 100644
--- a/core/java/android/preference/PreferenceGroupAdapter.java
+++ b/core/java/android/preference/PreferenceGroupAdapter.java
@@ -69,7 +69,9 @@
* count once--when the adapter is being set). We will not recycle views for
* Preference subclasses seen after the count has been returned.
*/
- private List<String> mPreferenceClassNames;
+ private ArrayList<PreferenceLayout> mPreferenceLayouts;
+
+ private PreferenceLayout mTempPreferenceLayout = new PreferenceLayout();
/**
* Blocks the mPreferenceClassNames from being changed anymore.
@@ -86,14 +88,37 @@
}
};
+ private static class PreferenceLayout implements Comparable<PreferenceLayout> {
+ private int resId;
+ private int widgetResId;
+ private String name;
+
+ public int compareTo(PreferenceLayout other) {
+ int compareNames = name.compareTo(other.name);
+ if (compareNames == 0) {
+ if (resId == other.resId) {
+ if (widgetResId == other.widgetResId) {
+ return 0;
+ } else {
+ return widgetResId - other.widgetResId;
+ }
+ } else {
+ return resId - other.resId;
+ }
+ } else {
+ return compareNames;
+ }
+ }
+ }
+
public PreferenceGroupAdapter(PreferenceGroup preferenceGroup) {
mPreferenceGroup = preferenceGroup;
// If this group gets or loses any children, let us know
mPreferenceGroup.setOnPreferenceChangeInternalListener(this);
-
+
mPreferenceList = new ArrayList<Preference>();
- mPreferenceClassNames = new ArrayList<String>();
-
+ mPreferenceLayouts = new ArrayList<PreferenceLayout>();
+
syncMyPreferences();
}
@@ -102,7 +127,7 @@
if (mIsSyncing) {
return;
}
-
+
mIsSyncing = true;
}
@@ -128,7 +153,7 @@
preferences.add(preference);
- if (!mHasReturnedViewTypeCount) {
+ if (!mHasReturnedViewTypeCount && !preference.hasSpecifiedLayout()) {
addPreferenceClassName(preference);
}
@@ -143,15 +168,28 @@
}
}
+ /**
+ * Creates a string that includes the preference name, layout id and widget layout id.
+ * If a particular preference type uses 2 different resources, they will be treated as
+ * different view types.
+ */
+ private PreferenceLayout createPreferenceLayout(Preference preference, PreferenceLayout in) {
+ PreferenceLayout pl = in != null? in : new PreferenceLayout();
+ pl.name = preference.getClass().getName();
+ pl.resId = preference.getLayoutResource();
+ pl.widgetResId = preference.getWidgetLayoutResource();
+ return pl;
+ }
+
private void addPreferenceClassName(Preference preference) {
- final String name = preference.getClass().getName();
- int insertPos = Collections.binarySearch(mPreferenceClassNames, name);
-
+ final PreferenceLayout pl = createPreferenceLayout(preference, null);
+ int insertPos = Collections.binarySearch(mPreferenceLayouts, pl);
+
// Only insert if it doesn't exist (when it is negative).
if (insertPos < 0) {
// Convert to insert index
insertPos = insertPos * -1 - 1;
- mPreferenceClassNames.add(insertPos, name);
+ mPreferenceLayouts.add(insertPos, pl);
}
}
@@ -171,19 +209,15 @@
public View getView(int position, View convertView, ViewGroup parent) {
final Preference preference = this.getItem(position);
-
- if (preference.hasSpecifiedLayout()) {
- // If the preference had specified a layout (as opposed to the
- // default), don't use convert views.
+ // Build a PreferenceLayout to compare with known ones that are cacheable.
+ mTempPreferenceLayout = createPreferenceLayout(preference, mTempPreferenceLayout);
+
+ // If it's not one of the cached ones, set the convertView to null so that
+ // the layout gets re-created by the Preference.
+ if (Collections.binarySearch(mPreferenceLayouts, mTempPreferenceLayout) < 0) {
convertView = null;
- } else {
- // TODO: better way of doing this
- final String name = preference.getClass().getName();
- if (Collections.binarySearch(mPreferenceClassNames, name) < 0) {
- convertView = null;
- }
}
-
+
return preference.getView(convertView, parent);
}
@@ -225,8 +259,9 @@
return IGNORE_ITEM_VIEW_TYPE;
}
- final String name = preference.getClass().getName();
- int viewType = Collections.binarySearch(mPreferenceClassNames, name);
+ mTempPreferenceLayout = createPreferenceLayout(preference, mTempPreferenceLayout);
+
+ int viewType = Collections.binarySearch(mPreferenceLayouts, mTempPreferenceLayout);
if (viewType < 0) {
// This is a class that was seen after we returned the count, so
// don't recycle it.
@@ -242,7 +277,7 @@
mHasReturnedViewTypeCount = true;
}
- return Math.max(1, mPreferenceClassNames.size());
+ return Math.max(1, mPreferenceLayouts.size());
}
}
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index f046cef..faa63af 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -514,6 +514,12 @@
* <P>Type: String</P>
*/
public static final String OWNER_ACCOUNT = "ownerAccount";
+
+ /**
+ * Whether the row has been deleted. A deleted row should be ignored.
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String DELETED = "deleted";
}
/**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 062080d..cd71682 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -238,6 +238,7 @@
private static final int FULL_SCREEN_KIND = 2;
private static final int MICRO_KIND = 3;
private static final String[] PROJECTION = new String[] {_ID, MediaColumns.DATA};
+ static final int DEFAULT_GROUP_ID = 0;
/**
* This method cancels the thumbnail request so clients waiting for getThumbnail will be
@@ -246,11 +247,14 @@
*
* @param cr ContentResolver
* @param origId original image or video id. use -1 to cancel all requests.
+ * @param groupId the same groupId used in getThumbnail
* @param baseUri the base URI of requested thumbnails
*/
- static void cancelThumbnailRequest(ContentResolver cr, long origId, Uri baseUri) {
+ static void cancelThumbnailRequest(ContentResolver cr, long origId, Uri baseUri,
+ long groupId) {
Uri cancelUri = baseUri.buildUpon().appendQueryParameter("cancel", "1")
- .appendQueryParameter("orig_id", String.valueOf(origId)).build();
+ .appendQueryParameter("orig_id", String.valueOf(origId))
+ .appendQueryParameter("group_id", String.valueOf(groupId)).build();
Cursor c = null;
try {
c = cr.query(cancelUri, PROJECTION, null, null, null);
@@ -271,9 +275,10 @@
* @param kind could be MINI_KIND or MICRO_KIND
* @param options this is only used for MINI_KIND when decoding the Bitmap
* @param baseUri the base URI of requested thumbnails
+ * @param groupId the id of group to which this request belongs
* @return Bitmap bitmap of specified thumbnail kind
*/
- static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
+ static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId, int kind,
BitmapFactory.Options options, Uri baseUri, boolean isVideo) {
Bitmap bitmap = null;
String filePath = null;
@@ -297,7 +302,8 @@
Cursor c = null;
try {
Uri blockingUri = baseUri.buildUpon().appendQueryParameter("blocking", "1")
- .appendQueryParameter("orig_id", String.valueOf(origId)).build();
+ .appendQueryParameter("orig_id", String.valueOf(origId))
+ .appendQueryParameter("group_id", String.valueOf(groupId)).build();
c = cr.query(blockingUri, PROJECTION, null, null, null);
// This happens when original image/video doesn't exist.
if (c == null) return null;
@@ -354,7 +360,7 @@
}
if (isVideo) {
bitmap = ThumbnailUtil.createVideoThumbnail(filePath);
- if (kind == MICRO_KIND) {
+ if (kind == MICRO_KIND && bitmap != null) {
bitmap = ThumbnailUtil.extractMiniThumb(bitmap,
ThumbnailUtil.MINI_THUMB_TARGET_SIZE,
ThumbnailUtil.MINI_THUMB_TARGET_SIZE,
@@ -669,7 +675,8 @@
* @param origId original image id
*/
public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
- InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI);
+ InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI,
+ InternalThumbnails.DEFAULT_GROUP_ID);
}
/**
@@ -685,7 +692,39 @@
*/
public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
BitmapFactory.Options options) {
- return InternalThumbnails.getThumbnail(cr, origId, kind, options,
+ return InternalThumbnails.getThumbnail(cr, origId,
+ InternalThumbnails.DEFAULT_GROUP_ID, kind, options,
+ EXTERNAL_CONTENT_URI, false);
+ }
+
+ /**
+ * This method cancels the thumbnail request so clients waiting for getThumbnail will be
+ * interrupted and return immediately. Only the original process which made the getThumbnail
+ * requests can cancel their own requests.
+ *
+ * @param cr ContentResolver
+ * @param origId original image id
+ * @param groupId the same groupId used in getThumbnail.
+ */
+ public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
+ InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, groupId);
+ }
+
+ /**
+ * This method checks if the thumbnails of the specified image (origId) has been created.
+ * It will be blocked until the thumbnails are generated.
+ *
+ * @param cr ContentResolver used to dispatch queries to MediaProvider.
+ * @param origId Original image id associated with thumbnail of interest.
+ * @param groupId the id of group to which this request belongs
+ * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
+ * @param options this is only used for MINI_KIND when decoding the Bitmap
+ * @return A Bitmap instance. It could be null if the original image
+ * associated with origId doesn't exist or memory is not enough.
+ */
+ public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
+ int kind, BitmapFactory.Options options) {
+ return InternalThumbnails.getThumbnail(cr, origId, groupId, kind, options,
EXTERNAL_CONTENT_URI, false);
}
@@ -1598,7 +1637,8 @@
* @param origId original video id
*/
public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
- InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI);
+ InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI,
+ InternalThumbnails.DEFAULT_GROUP_ID);
}
/**
@@ -1607,18 +1647,50 @@
*
* @param cr ContentResolver used to dispatch queries to MediaProvider.
* @param origId Original image id associated with thumbnail of interest.
+ * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
+ * @param options this is only used for MINI_KIND when decoding the Bitmap
+ * @return A Bitmap instance. It could be null if the original image
+ * associated with origId doesn't exist or memory is not enough.
+ */
+ public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
+ BitmapFactory.Options options) {
+ return InternalThumbnails.getThumbnail(cr, origId,
+ InternalThumbnails.DEFAULT_GROUP_ID, kind, options,
+ EXTERNAL_CONTENT_URI, true);
+ }
+
+ /**
+ * This method checks if the thumbnails of the specified image (origId) has been created.
+ * It will be blocked until the thumbnails are generated.
+ *
+ * @param cr ContentResolver used to dispatch queries to MediaProvider.
+ * @param origId Original image id associated with thumbnail of interest.
+ * @param groupId the id of group to which this request belongs
* @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND
* @param options this is only used for MINI_KIND when decoding the Bitmap
* @return A Bitmap instance. It could be null if the original image associated with
* origId doesn't exist or memory is not enough.
*/
- public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
- BitmapFactory.Options options) {
- return InternalThumbnails.getThumbnail(cr, origId, kind, options,
+ public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
+ int kind, BitmapFactory.Options options) {
+ return InternalThumbnails.getThumbnail(cr, origId, groupId, kind, options,
EXTERNAL_CONTENT_URI, true);
}
/**
+ * This method cancels the thumbnail request so clients waiting for getThumbnail will be
+ * interrupted and return immediately. Only the original process which made the getThumbnail
+ * requests can cancel their own requests.
+ *
+ * @param cr ContentResolver
+ * @param origId original video id
+ * @param groupId the same groupId used in getThumbnail.
+ */
+ public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
+ InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, groupId);
+ }
+
+ /**
* Get the content:// style URI for the image media table on the
* given volume.
*
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index cb3dc16..456181c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3110,6 +3110,13 @@
= "google_login_generic_auth_service";
/**
+ * Duration in milliseconds after setup at which market does not reconcile applications
+ * which are installed during restore.
+ */
+ public static final String VENDING_RESTORE_WINDOW_MS = "vending_restore_window_ms";
+
+
+ /**
* Frequency in milliseconds at which we should sync the locally installed Vending Machine
* content with the server.
*/
@@ -3623,7 +3630,6 @@
*/
public static final String SEARCH_PER_SOURCE_CONCURRENT_QUERY_LIMIT =
"search_per_source_concurrent_query_limit";
-
/**
* Flag for allowing ActivityManagerService to send ACTION_APP_ERROR intents
* on application crashes and ANRs. If this is disabled, the crash/ANR dialog
@@ -3638,6 +3644,32 @@
public static final String LAST_KMSG_KB = "last_kmsg_kb";
/**
+ * Maximum age of entries kept by {@link android.os.IDropBox}.
+ */
+ public static final String DROPBOX_AGE_SECONDS =
+ "dropbox_age_seconds";
+ /**
+ * Maximum amount of disk space used by {@link android.os.IDropBox} no matter what.
+ */
+ public static final String DROPBOX_QUOTA_KB =
+ "dropbox_quota_kb";
+ /**
+ * Percent of free disk (excluding reserve) which {@link android.os.IDropBox} will use.
+ */
+ public static final String DROPBOX_QUOTA_PERCENT =
+ "dropbox_quota_percent";
+ /**
+ * Percent of total disk which {@link android.os.IDropBox} will never dip into.
+ */
+ public static final String DROPBOX_RESERVE_PERCENT =
+ "dropbox_reserve_percent";
+ /**
+ * Prefix for per-tag dropbox disable/enable settings.
+ */
+ public static final String DROPBOX_TAG_PREFIX =
+ "dropbox:";
+
+ /**
* @deprecated
* @hide
*/
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 67b30a9..0db29a4 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -61,7 +61,7 @@
public class BluetoothService extends IBluetooth.Stub {
private static final String TAG = "BluetoothService";
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
private int mNativeData;
private BluetoothEventLoop mEventLoop;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index a92800d..afc6864 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -294,7 +294,12 @@
lbaseline, lbottom, buf,
start, end, par, this);
- left += margin.getLeadingMargin(par);
+ boolean useMargin = par;
+ if (margin instanceof LeadingMarginSpan.LeadingMarginSpan2) {
+ int count = ((LeadingMarginSpan.LeadingMarginSpan2)margin).getLeadingMarginLineCount();
+ useMargin = count > i;
+ }
+ left += margin.getLeadingMargin(useMargin);
}
}
}
@@ -1293,7 +1298,13 @@
LeadingMarginSpan.class);
for (int i = 0; i < spans.length; i++) {
- left += spans[i].getLeadingMargin(par);
+ boolean margin = par;
+ LeadingMarginSpan span = spans[i];
+ if (span instanceof LeadingMarginSpan.LeadingMarginSpan2) {
+ int count = ((LeadingMarginSpan.LeadingMarginSpan2)span).getLeadingMarginLineCount();
+ margin = count >= line;
+ }
+ left += span.getLeadingMargin(margin);
}
}
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index f0a5ffd..fbf1261 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -161,6 +161,7 @@
else
end++;
+ int firstWidthLineCount = 1;
int firstwidth = outerwidth;
int restwidth = outerwidth;
@@ -171,8 +172,12 @@
sp = spanned.getSpans(start, end, LeadingMarginSpan.class);
for (int i = 0; i < sp.length; i++) {
+ LeadingMarginSpan lms = sp[i];
firstwidth -= sp[i].getLeadingMargin(true);
restwidth -= sp[i].getLeadingMargin(false);
+ if (lms instanceof LeadingMarginSpan.LeadingMarginSpan2) {
+ firstWidthLineCount = ((LeadingMarginSpan.LeadingMarginSpan2)lms).getLeadingMarginLineCount();
+ }
}
chooseht = spanned.getSpans(start, end, LineHeightSpan.class);
@@ -750,7 +755,9 @@
fitascent = fitdescent = fittop = fitbottom = 0;
okascent = okdescent = oktop = okbottom = 0;
- width = restwidth;
+ if (--firstWidthLineCount <= 0) {
+ width = restwidth;
+ }
}
}
}
diff --git a/core/java/android/text/style/LeadingMarginSpan.java b/core/java/android/text/style/LeadingMarginSpan.java
index 8e212e3..cb55329 100644
--- a/core/java/android/text/style/LeadingMarginSpan.java
+++ b/core/java/android/text/style/LeadingMarginSpan.java
@@ -33,6 +33,11 @@
CharSequence text, int start, int end,
boolean first, Layout layout);
+
+ public interface LeadingMarginSpan2 extends LeadingMarginSpan, WrapTogetherSpan {
+ public int getLeadingMarginLineCount();
+ };
+
public static class Standard implements LeadingMarginSpan, ParcelableSpan {
private final int mFirst, mRest;
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index a6d644b..5c2b3c9 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -68,6 +68,7 @@
View.AttachInfo.Callbacks {
private static final String TAG = "ViewRoot";
private static final boolean DBG = false;
+ private static final boolean SHOW_FPS = false;
@SuppressWarnings({"ConstantConditionalExpression"})
private static final boolean LOCAL_LOGV = false ? Config.LOGD : Config.LOGV;
/** @noinspection PointlessBooleanExpression*/
@@ -898,6 +899,7 @@
// all at once.
newSurface = true;
fullRedrawNeeded = true;
+ mPreviousTransparentRegion.setEmpty();
if (mGlWanted && !mUseGL) {
initializeGL();
@@ -1243,7 +1245,7 @@
mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
checkEglErrors();
- if (Config.DEBUG && ViewDebug.showFps) {
+ if (SHOW_FPS || Config.DEBUG && ViewDebug.showFps) {
int now = (int)SystemClock.elapsedRealtime();
if (sDrawTime != 0) {
nativeShowFPS(canvas, now - sDrawTime);
@@ -1355,7 +1357,7 @@
mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
}
- if (Config.DEBUG && ViewDebug.showFps) {
+ if (SHOW_FPS || Config.DEBUG && ViewDebug.showFps) {
int now = (int)SystemClock.elapsedRealtime();
if (sDrawTime != 0) {
nativeShowFPS(canvas, now - sDrawTime);
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 9456ae1..d1db35e 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -19,17 +19,21 @@
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.AssetManager;
+import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.ParseException;
+import android.net.Uri;
import android.net.WebAddress;
import android.net.http.SslCertificate;
import android.os.Handler;
import android.os.Message;
+import android.provider.OpenableColumns;
import android.util.Log;
import android.util.TypedValue;
import junit.framework.Assert;
+import java.io.InputStream;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
@@ -463,6 +467,63 @@
}
/**
+ * Called by JNI. Given a URI, find the associated file and return its size
+ * @param uri A String representing the URI of the desired file.
+ * @return int The size of the given file.
+ */
+ private int getFileSize(String uri) {
+ int size = 0;
+ Cursor cursor = mContext.getContentResolver().query(Uri.parse(uri),
+ new String[] { OpenableColumns.SIZE },
+ null,
+ null,
+ null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToNext()) {
+ size = cursor.getInt(0);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ return size;
+ }
+
+ /**
+ * Called by JNI. Given a URI, a buffer, and an offset into the buffer,
+ * copy the resource into buffer.
+ * @param uri A String representing the URI of the desired file.
+ * @param buffer The byte array to copy the data into.
+ * @param offset The offet into buffer to place the data.
+ * @param expectSize The size that the buffer has allocated for this file.
+ * @return int The size of the given file, or zero if it fails.
+ */
+ private int getFile(String uri, byte[] buffer, int offset,
+ int expectedSize) {
+ int size = 0;
+ try {
+ InputStream stream = mContext.getContentResolver()
+ .openInputStream(Uri.parse(uri));
+ size = stream.available();
+ if (size <= expectedSize && buffer != null
+ && buffer.length - offset >= size) {
+ stream.read(buffer, offset, size);
+ } else {
+ size = 0;
+ }
+ stream.close();
+ } catch (java.io.FileNotFoundException e) {
+ Log.e(LOGTAG, "FileNotFoundException:" + e);
+ size = 0;
+ } catch (java.io.IOException e2) {
+ Log.e(LOGTAG, "IOException: " + e2);
+ size = 0;
+ }
+ return size;
+ }
+
+ /**
* Start loading a resource.
* @param loaderHandle The native ResourceLoader that is the target of the
* data.
diff --git a/core/java/android/webkit/ByteArrayBuilder.java b/core/java/android/webkit/ByteArrayBuilder.java
index 145411c..334526b 100644
--- a/core/java/android/webkit/ByteArrayBuilder.java
+++ b/core/java/android/webkit/ByteArrayBuilder.java
@@ -16,6 +16,8 @@
package android.webkit;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
import java.util.LinkedList;
import java.util.ListIterator;
@@ -23,47 +25,37 @@
them back out. It does not optimize for returning the result in a
single array, though this is supported in the API. It is fastest
if the retrieval can be done via iterating through chunks.
-
- Things to add:
- - consider dynamically increasing our min_capacity,
- as we see mTotalSize increase
*/
class ByteArrayBuilder {
private static final int DEFAULT_CAPACITY = 8192;
+ // Global pool of chunks to be used by other ByteArrayBuilders.
+ private static final LinkedList<SoftReference<Chunk>> sPool =
+ new LinkedList<SoftReference<Chunk>>();
+ // Reference queue for processing gc'd entries.
+ private static final ReferenceQueue<Chunk> sQueue =
+ new ReferenceQueue<Chunk>();
+
private LinkedList<Chunk> mChunks;
- /** free pool */
- private LinkedList<Chunk> mPool;
-
- private int mMinCapacity;
-
public ByteArrayBuilder() {
- init(0);
- }
-
- public ByteArrayBuilder(int minCapacity) {
- init(minCapacity);
- }
-
- private void init(int minCapacity) {
mChunks = new LinkedList<Chunk>();
- mPool = new LinkedList<Chunk>();
-
- if (minCapacity <= 0) {
- minCapacity = DEFAULT_CAPACITY;
- }
- mMinCapacity = minCapacity;
- }
-
- public void append(byte[] array) {
- append(array, 0, array.length);
}
public synchronized void append(byte[] array, int offset, int length) {
while (length > 0) {
- Chunk c = appendChunk(length);
+ Chunk c = null;
+ if (mChunks.isEmpty()) {
+ c = obtainChunk(length);
+ mChunks.addLast(c);
+ } else {
+ c = mChunks.getLast();
+ if (c.mLength == c.mArray.length) {
+ c = obtainChunk(length);
+ mChunks.addLast(c);
+ }
+ }
int amount = Math.min(length, c.mArray.length - c.mLength);
System.arraycopy(array, offset, c.mArray, c.mLength, amount);
c.mLength += amount;
@@ -75,7 +67,7 @@
/**
* The fastest way to retrieve the data is to iterate through the
* chunks. This returns the first chunk. Note: this pulls the
- * chunk out of the queue. The caller must call releaseChunk() to
+ * chunk out of the queue. The caller must call Chunk.release() to
* dispose of it.
*/
public synchronized Chunk getFirstChunk() {
@@ -83,23 +75,11 @@
return mChunks.removeFirst();
}
- /**
- * recycles chunk
- */
- public synchronized void releaseChunk(Chunk c) {
- c.mLength = 0;
- mPool.addLast(c);
- }
-
- public boolean isEmpty() {
+ public synchronized boolean isEmpty() {
return mChunks.isEmpty();
}
- public int size() {
- return mChunks.size();
- }
-
- public int getByteSize() {
+ public synchronized int getByteSize() {
int total = 0;
ListIterator<Chunk> it = mChunks.listIterator(0);
while (it.hasNext()) {
@@ -112,37 +92,40 @@
public synchronized void clear() {
Chunk c = getFirstChunk();
while (c != null) {
- releaseChunk(c);
+ c.release();
c = getFirstChunk();
}
}
- private Chunk appendChunk(int length) {
- if (length < mMinCapacity) {
- length = mMinCapacity;
- }
-
- Chunk c;
- if (mChunks.isEmpty()) {
- c = obtainChunk(length);
- } else {
- c = mChunks.getLast();
- if (c.mLength == c.mArray.length) {
- c = obtainChunk(length);
+ // Must be called with lock held on sPool.
+ private void processPoolLocked() {
+ while (true) {
+ SoftReference<Chunk> entry = (SoftReference<Chunk>) sQueue.poll();
+ if (entry == null) {
+ break;
}
+ sPool.remove(entry);
}
- return c;
}
private Chunk obtainChunk(int length) {
- Chunk c;
- if (mPool.isEmpty()) {
- c = new Chunk(length);
- } else {
- c = mPool.removeFirst();
+ // Correct a small length.
+ if (length < DEFAULT_CAPACITY) {
+ length = DEFAULT_CAPACITY;
}
- mChunks.addLast(c);
- return c;
+ synchronized (sPool) {
+ // Process any queued references and remove them from the pool.
+ processPoolLocked();
+ if (!sPool.isEmpty()) {
+ Chunk c = sPool.removeFirst().get();
+ // The first item may have been queued after processPoolLocked
+ // so check for null.
+ if (c != null) {
+ return c;
+ }
+ }
+ return new Chunk(length);
+ }
}
public static class Chunk {
@@ -153,5 +136,19 @@
mArray = new byte[length];
mLength = 0;
}
+
+ /**
+ * Release the chunk and make it available for reuse.
+ */
+ public void release() {
+ mLength = 0;
+ synchronized (sPool) {
+ // Add the chunk back to the pool as a SoftReference so it can
+ // be gc'd if needed.
+ sPool.offer(new SoftReference<Chunk>(this, sQueue));
+ sPool.notifyAll();
+ }
+ }
+
}
}
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 8d55247..f9dec7f 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -107,6 +107,7 @@
private static final int GEOLOCATION_PERMISSIONS_HIDE_PROMPT = 131;
private static final int RECEIVED_TOUCH_ICON_URL = 132;
private static final int GET_VISITED_HISTORY = 133;
+ private static final int OPEN_FILE_CHOOSER = 134;
// Message triggered by the client to resume execution
private static final int NOTIFY = 200;
@@ -149,6 +150,16 @@
}
/**
+ * Get the WebViewClient.
+ * @return the current WebViewClient instance.
+ *
+ *@hide pending API council approval.
+ */
+ public WebViewClient getWebViewClient() {
+ return mWebViewClient;
+ }
+
+ /**
* Set the WebChromeClient.
* @param client An implementation of WebChromeClient.
*/
@@ -662,6 +673,12 @@
mWebChromeClient.getVisitedHistory((ValueCallback<String[]>)msg.obj);
}
break;
+
+ case OPEN_FILE_CHOOSER:
+ if (mWebChromeClient != null) {
+ mWebChromeClient.openFileChooser((UploadFile) msg.obj);
+ }
+ break;
}
}
@@ -1348,4 +1365,40 @@
msg.obj = callback;
sendMessage(msg);
}
+
+ private class UploadFile implements ValueCallback<Uri> {
+ private Uri mValue;
+ public void onReceiveValue(Uri value) {
+ mValue = value;
+ synchronized (CallbackProxy.this) {
+ CallbackProxy.this.notify();
+ }
+ }
+ public Uri getResult() {
+ return mValue;
+ }
+ }
+
+ /**
+ * Called by WebViewCore to open a file chooser.
+ */
+ /* package */ Uri openFileChooser() {
+ if (mWebChromeClient == null) {
+ return null;
+ }
+ Message myMessage = obtainMessage(OPEN_FILE_CHOOSER);
+ UploadFile uploadFile = new UploadFile();
+ myMessage.obj = uploadFile;
+ synchronized (this) {
+ sendMessage(myMessage);
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ Log.e(LOGTAG,
+ "Caught exception while waiting for openFileChooser");
+ Log.e(LOGTAG, Log.getStackTraceString(e));
+ }
+ }
+ return uploadFile.getResult();
+ }
}
diff --git a/core/java/android/webkit/HttpDateTime.java b/core/java/android/webkit/HttpDateTime.java
index 2f46f2b..042953c 100644
--- a/core/java/android/webkit/HttpDateTime.java
+++ b/core/java/android/webkit/HttpDateTime.java
@@ -50,13 +50,15 @@
* Wdy Mon DD HH:MM:SS YYYY GMT
*
* HH can be H if the first digit is zero.
+ *
+ * Mon can be the full name of the month.
*/
private static final String HTTP_DATE_RFC_REGEXP =
- "([0-9]{1,2})[- ]([A-Za-z]{3,3})[- ]([0-9]{2,4})[ ]"
+ "([0-9]{1,2})[- ]([A-Za-z]{3,9})[- ]([0-9]{2,4})[ ]"
+ "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])";
private static final String HTTP_DATE_ANSIC_REGEXP =
- "[ ]([A-Za-z]{3,3})[ ]+([0-9]{1,2})[ ]"
+ "[ ]([A-Za-z]{3,9})[ ]+([0-9]{1,2})[ ]"
+ "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])[ ]([0-9]{2,4})";
/**
diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java
index 4c17f99..5c0ce3c 100644
--- a/core/java/android/webkit/LoadListener.java
+++ b/core/java/android/webkit/LoadListener.java
@@ -78,7 +78,7 @@
private static int sNativeLoaderCount;
- private final ByteArrayBuilder mDataBuilder = new ByteArrayBuilder(8192);
+ private final ByteArrayBuilder mDataBuilder = new ByteArrayBuilder();
private String mUrl;
private WebAddress mUri;
@@ -522,17 +522,18 @@
* IMPORTANT: as this is called from network thread, can't call native
* directly
* XXX: Unlike the other network thread methods, this method can do the
- * work of decoding the data and appending it to the data builder because
- * mDataBuilder is a thread-safe structure.
+ * work of decoding the data and appending it to the data builder.
*/
public void data(byte[] data, int length) {
if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener.data(): url: " + url());
}
- // Synchronize on mData because commitLoad may write mData to WebCore
- // and we don't want to replace mData or mDataLength at the same time
- // as a write.
+ // The reason isEmpty() and append() need to synchronized together is
+ // because it is possible for getFirstChunk() to be called multiple
+ // times between isEmpty() and append(). This could cause commitLoad()
+ // to finish before processing the newly appended data and no message
+ // will be sent.
boolean sendMessage = false;
synchronized (mDataBuilder) {
sendMessage = mDataBuilder.isEmpty();
@@ -1009,28 +1010,34 @@
if (mIsMainPageLoader) {
String type = sCertificateTypeMap.get(mMimeType);
if (type != null) {
- // In the case of downloading certificate, we will save it to
- // the KeyStore and stop the current loading so that it will not
- // generate a new history page
- byte[] cert = new byte[mDataBuilder.getByteSize()];
- int offset = 0;
- while (true) {
- ByteArrayBuilder.Chunk c = mDataBuilder.getFirstChunk();
- if (c == null) break;
+ // This must be synchronized so that no more data can be added
+ // after getByteSize returns.
+ synchronized (mDataBuilder) {
+ // In the case of downloading certificate, we will save it
+ // to the KeyStore and stop the current loading so that it
+ // will not generate a new history page
+ byte[] cert = new byte[mDataBuilder.getByteSize()];
+ int offset = 0;
+ while (true) {
+ ByteArrayBuilder.Chunk c = mDataBuilder.getFirstChunk();
+ if (c == null) break;
- if (c.mLength != 0) {
- System.arraycopy(c.mArray, 0, cert, offset, c.mLength);
- offset += c.mLength;
+ if (c.mLength != 0) {
+ System.arraycopy(c.mArray, 0, cert, offset, c.mLength);
+ offset += c.mLength;
+ }
+ c.release();
}
- mDataBuilder.releaseChunk(c);
+ CertTool.addCertificate(mContext, type, cert);
+ mBrowserFrame.stopLoading();
+ return;
}
- CertTool.addCertificate(mContext, type, cert);
- mBrowserFrame.stopLoading();
- return;
}
}
- // Give the data to WebKit now
+ // Give the data to WebKit now. We don't have to synchronize on
+ // mDataBuilder here because pulling each chunk removes it from the
+ // internal list so it cannot be modified.
PerfChecker checker = new PerfChecker();
ByteArrayBuilder.Chunk c;
while (true) {
@@ -1047,7 +1054,7 @@
}
nativeAddData(c.mArray, c.mLength);
}
- mDataBuilder.releaseChunk(c);
+ c.release();
checker.responseAlert("res nativeAddData");
}
}
@@ -1210,8 +1217,17 @@
// mRequestHandle can be null when the request was satisfied
// by the cache, and the cache returned a redirect
if (mRequestHandle != null) {
- mRequestHandle.setupRedirect(mUrl, mStatusCode,
- mRequestHeaders);
+ try {
+ mRequestHandle.setupRedirect(mUrl, mStatusCode,
+ mRequestHeaders);
+ } catch(RuntimeException e) {
+ Log.e(LOGTAG, e.getMessage());
+ // Signal a bad url error if we could not load the
+ // redirection.
+ handleError(EventHandler.ERROR_BAD_URL,
+ mContext.getString(R.string.httpErrorBadUrl));
+ return;
+ }
} else {
// If the original request came from the cache, there is no
// RequestHandle, we have to create a new one through
diff --git a/core/java/android/webkit/MimeTypeMap.java b/core/java/android/webkit/MimeTypeMap.java
index fffba1b..84a8a3c 100644
--- a/core/java/android/webkit/MimeTypeMap.java
+++ b/core/java/android/webkit/MimeTypeMap.java
@@ -431,6 +431,8 @@
sMimeTypeMap.loadEntry("text/calendar", "icz");
sMimeTypeMap.loadEntry("text/comma-separated-values", "csv");
sMimeTypeMap.loadEntry("text/css", "css");
+ sMimeTypeMap.loadEntry("text/html", "htm");
+ sMimeTypeMap.loadEntry("text/html", "html");
sMimeTypeMap.loadEntry("text/h323", "323");
sMimeTypeMap.loadEntry("text/iuls", "uls");
sMimeTypeMap.loadEntry("text/mathml", "mml");
@@ -481,6 +483,7 @@
sMimeTypeMap.loadEntry("video/dv", "dif");
sMimeTypeMap.loadEntry("video/dv", "dv");
sMimeTypeMap.loadEntry("video/fli", "fli");
+ sMimeTypeMap.loadEntry("video/m4v", "m4v");
sMimeTypeMap.loadEntry("video/mpeg", "mpeg");
sMimeTypeMap.loadEntry("video/mpeg", "mpg");
sMimeTypeMap.loadEntry("video/mpeg", "mpe");
diff --git a/core/java/android/webkit/Network.java b/core/java/android/webkit/Network.java
index af0cb1e..b53e404 100644
--- a/core/java/android/webkit/Network.java
+++ b/core/java/android/webkit/Network.java
@@ -180,20 +180,24 @@
}
RequestQueue q = mRequestQueue;
+ RequestHandle handle = null;
if (loader.isSynchronous()) {
- q = new RequestQueue(loader.getContext(), 1);
- }
-
- RequestHandle handle = q.queueRequest(
- url, loader.getWebAddress(), method, headers, loader,
- bodyProvider, bodyLength);
- loader.attachRequestHandle(handle);
-
- if (loader.isSynchronous()) {
- handle.waitUntilComplete();
+ handle = q.queueSynchronousRequest(url, loader.getWebAddress(),
+ method, headers, loader, bodyProvider, bodyLength);
+ loader.attachRequestHandle(handle);
+ handle.processRequest();
loader.loadSynchronousMessages();
- q.shutdown();
+ } else {
+ handle = q.queueRequest(url, loader.getWebAddress(), method,
+ headers, loader, bodyProvider, bodyLength);
+ // FIXME: Although this is probably a rare condition, normal network
+ // requests are processed in a separate thread. This means that it
+ // is possible to process part of the request before setting the
+ // request handle on the loader. We should probably refactor this to
+ // ensure the handle is attached before processing begins.
+ loader.attachRequestHandle(handle);
}
+
return true;
}
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index 232ed36..211e5e4 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -367,19 +367,23 @@
/** Regex used to parse content-disposition headers */
private static final Pattern CONTENT_DISPOSITION_PATTERN =
- Pattern.compile("attachment;\\s*filename\\s*=\\s*\"([^\"]*)\"");
+ Pattern.compile("attachment;\\s*filename\\s*=\\s*(\"?)([^\"]*)\\1\\s*$",
+ Pattern.CASE_INSENSITIVE);
/*
* Parse the Content-Disposition HTTP Header. The format of the header
* is defined here: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html
* This header provides a filename for content that is going to be
* downloaded to the file system. We only support the attachment type.
+ * Note that RFC 2616 specifies the filename value must be double-quoted.
+ * Unfortunately some servers do not quote the value so to maintain
+ * consistent behaviour with other browsers, we allow unquoted values too.
*/
static String parseContentDisposition(String contentDisposition) {
try {
Matcher m = CONTENT_DISPOSITION_PATTERN.matcher(contentDisposition);
if (m.find()) {
- return m.group(1);
+ return m.group(2);
}
} catch (IllegalStateException ex) {
// This function is defined as returning null when it can't parse the header
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 7f5b862..ae4f7c2 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -17,6 +17,7 @@
package android.webkit;
import android.graphics.Bitmap;
+import android.net.Uri;
import android.os.Message;
import android.view.View;
@@ -302,4 +303,13 @@
public void getVisitedHistory(ValueCallback<String[]> callback) {
}
+ /**
+ * Tell the client to open a file chooser.
+ * @param uploadFile A ValueCallback to set the URI of the file to upload.
+ * onReceiveValue must be called to wake up the thread.
+ * @hide
+ */
+ public void openFileChooser(ValueCallback<Uri> uploadFile) {
+ uploadFile.onReceiveValue(null);
+ }
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 4fedec9..79d8c03 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1007,7 +1007,8 @@
* should never be null.
*/
public synchronized void setGeolocationDatabasePath(String databasePath) {
- if (databasePath != null && !databasePath.equals(mDatabasePath)) {
+ if (databasePath != null
+ && !databasePath.equals(mGeolocationDatabasePath)) {
mGeolocationDatabasePath = databasePath;
postSync();
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index eb09c66..06ab14d 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -390,6 +390,8 @@
private static final int LONG_PRESS_TIMEOUT = 1000;
// needed to avoid flinging after a pause of no movement
private static final int MIN_FLING_TIME = 250;
+ // draw unfiltered after drag is held without movement
+ private static final int MOTIONLESS_TIME = 100;
// The time that the Zoom Controls are visible before fading away
private static final long ZOOM_CONTROLS_TIMEOUT =
ViewConfiguration.getZoomControlsTimeout();
@@ -427,6 +429,10 @@
private Scroller mScroller;
private boolean mWrapContent;
+ private static final int MOTIONLESS_FALSE = 0;
+ private static final int MOTIONLESS_PENDING = 1;
+ private static final int MOTIONLESS_TRUE = 2;
+ private int mHeldMotionless;
/**
* Private message ids
@@ -438,6 +444,8 @@
private static final int RELEASE_SINGLE_TAP = 5;
private static final int REQUEST_FORM_DATA = 6;
private static final int RESUME_WEBCORE_UPDATE = 7;
+ private static final int DRAG_HELD_MOTIONLESS = 8;
+ private static final int AWAKEN_SCROLL_BARS = 9;
//! arg1=x, arg2=y
static final int SCROLL_TO_MSG_ID = 10;
@@ -468,9 +476,9 @@
"SWITCH_TO_LONGPRESS", // = 4;
"RELEASE_SINGLE_TAP", // = 5;
"REQUEST_FORM_DATA", // = 6;
- "SWITCH_TO_CLICK", // = 7;
- "RESUME_WEBCORE_UPDATE", // = 8;
- "9",
+ "RESUME_WEBCORE_UPDATE", // = 7;
+ "DRAG_HELD_MOTIONLESS", // = 8;
+ "AWAKEN_SCROLL_BARS", // = 9;
"SCROLL_TO_MSG_ID", // = 10;
"SCROLL_BY_MSG_ID", // = 11;
"SPAWN_SCROLL_TO_MSG_ID", // = 12;
@@ -1713,6 +1721,7 @@
* as the data member with "url" as key. The result can be null.
*/
public void requestImageRef(Message msg) {
+ if (0 == mNativeClass) return; // client isn't initialized
int contentX = viewToContentX((int) mLastTouchX + mScrollX);
int contentY = viewToContentY((int) mLastTouchY + mScrollY);
String ref = nativeImageURI(contentX, contentY);
@@ -2331,6 +2340,7 @@
* @param forward Direction to search.
*/
public void findNext(boolean forward) {
+ if (0 == mNativeClass) return; // client isn't initialized
nativeFindNext(forward);
}
@@ -2341,6 +2351,7 @@
* that were found.
*/
public int findAll(String find) {
+ if (0 == mNativeClass) return 0; // client isn't initialized
if (mFindIsUp == false) {
recordNewContentSize(mContentWidth, mContentHeight + mFindHeight,
false);
@@ -2630,6 +2641,16 @@
}
/**
+ * Gets the WebViewClient
+ * @return the current WebViewClient instance.
+ *
+ *@hide pending API council approval.
+ */
+ public WebViewClient getWebViewClient() {
+ return mCallbackProxy.getWebViewClient();
+ }
+
+ /**
* Register the interface to be used when content can not be handled by
* the rendering engine, and should be downloaded instead. This will replace
* the current handler.
@@ -2836,6 +2857,21 @@
*/
private boolean mNeedToAdjustWebTextView;
+ private boolean didUpdateTextViewBounds(boolean allowIntersect) {
+ Rect contentBounds = nativeFocusCandidateNodeBounds();
+ Rect vBox = contentToViewRect(contentBounds);
+ Rect visibleRect = new Rect();
+ calcOurVisibleRect(visibleRect);
+ if (allowIntersect ? Rect.intersects(visibleRect, vBox) :
+ visibleRect.contains(vBox)) {
+ mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
+ vBox.height());
+ return true;
+ } else {
+ return false;
+ }
+ }
+
private void drawCoreAndCursorRing(Canvas canvas, int color,
boolean drawCursorRing) {
if (mDrawHistory) {
@@ -2845,8 +2881,22 @@
}
boolean animateZoom = mZoomScale != 0;
- boolean animateScroll = !mScroller.isFinished()
- || mVelocityTracker != null;
+ boolean animateScroll = (!mScroller.isFinished()
+ || mVelocityTracker != null)
+ && (mTouchMode != TOUCH_DRAG_MODE ||
+ mHeldMotionless != MOTIONLESS_TRUE);
+ if (mTouchMode == TOUCH_DRAG_MODE) {
+ if (mHeldMotionless == MOTIONLESS_PENDING) {
+ mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
+ mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
+ mHeldMotionless = MOTIONLESS_FALSE;
+ }
+ if (mHeldMotionless == MOTIONLESS_FALSE) {
+ mPrivateHandler.sendMessageDelayed(mPrivateHandler
+ .obtainMessage(DRAG_HELD_MOTIONLESS), MOTIONLESS_TIME);
+ mHeldMotionless = MOTIONLESS_PENDING;
+ }
+ }
if (animateZoom) {
float zoomScale;
int interval = (int) (SystemClock.uptimeMillis() - mZoomStart);
@@ -2863,19 +2913,13 @@
invalidate();
if (mNeedToAdjustWebTextView) {
mNeedToAdjustWebTextView = false;
- Rect contentBounds = nativeFocusCandidateNodeBounds();
- Rect vBox = contentToViewRect(contentBounds);
- Rect visibleRect = new Rect();
- calcOurVisibleRect(visibleRect);
- if (visibleRect.contains(vBox)) {
- // As a result of the zoom, the textfield is now on
- // screen. Place the WebTextView in its new place,
- // accounting for our new scroll/zoom values.
+ // As a result of the zoom, the textfield is now on
+ // screen. Place the WebTextView in its new place,
+ // accounting for our new scroll/zoom values.
+ if (didUpdateTextViewBounds(false)) {
mWebTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
contentToViewDimension(
nativeFocusCandidateTextSize()));
- mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
- vBox.height());
// If it is a password field, start drawing the
// WebTextView once again.
if (nativeFocusCandidateIsPassword()) {
@@ -2951,6 +2995,10 @@
if (mFindIsUp && !animateScroll) {
nativeDrawMatches(canvas);
}
+ if (mFocusSizeChanged) {
+ mFocusSizeChanged = false;
+ didUpdateTextViewBounds(true);
+ }
}
// draw history
@@ -3304,7 +3352,7 @@
}
}
- if (nativeCursorIsPlugin()) {
+ if (nativeFocusCandidateIsPlugin()) {
nativeUpdatePluginReceivesEvents();
invalidate();
} else if (nativeCursorIsTextInput()) {
@@ -3316,7 +3364,9 @@
// our view system's notion of focus
rebuildWebTextView();
// Now we need to pass the event to it
- return mWebTextView.onKeyDown(keyCode, event);
+ if (inEditingMode()) {
+ return mWebTextView.onKeyDown(keyCode, event);
+ }
} else if (nativeHasFocusNode()) {
// In this case, the cursor is not on a text input, but the focus
// might be. Check it, and if so, hand over to the WebTextView.
@@ -3430,6 +3480,7 @@
* @hide
*/
public void emulateShiftHeld() {
+ if (0 == mNativeClass) return; // client isn't initialized
mExtendSelection = false;
mShiftIsPressed = true;
nativeHideCursor();
@@ -3701,8 +3752,10 @@
mLastSentTouchTime = eventTime;
}
- int deltaX = (int) (mLastTouchX - x);
- int deltaY = (int) (mLastTouchY - y);
+ float fDeltaX = mLastTouchX - x;
+ float fDeltaY = mLastTouchY - y;
+ int deltaX = (int) fDeltaX;
+ int deltaY = (int) fDeltaY;
switch (action) {
case MotionEvent.ACTION_DOWN: {
@@ -3828,13 +3881,27 @@
// do pan
int newScrollX = pinLocX(mScrollX + deltaX);
- deltaX = newScrollX - mScrollX;
+ int newDeltaX = newScrollX - mScrollX;
+ if (deltaX != newDeltaX) {
+ deltaX = newDeltaX;
+ fDeltaX = (float) newDeltaX;
+ }
int newScrollY = pinLocY(mScrollY + deltaY);
- deltaY = newScrollY - mScrollY;
+ int newDeltaY = newScrollY - mScrollY;
+ if (deltaY != newDeltaY) {
+ deltaY = newDeltaY;
+ fDeltaY = (float) newDeltaY;
+ }
boolean done = false;
- if (deltaX == 0 && deltaY == 0) {
- done = true;
+ boolean keepScrollBarsVisible = false;
+ if (Math.abs(fDeltaX) < 1.0f && Math.abs(fDeltaY) < 1.0f) {
+ keepScrollBarsVisible = done = true;
} else {
+ if (mHeldMotionless != MOTIONLESS_FALSE) {
+ mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
+ mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
+ mHeldMotionless = MOTIONLESS_FALSE;
+ }
if (mSnapScrollMode == SNAP_X || mSnapScrollMode == SNAP_Y) {
int ax = Math.abs(deltaX);
int ay = Math.abs(deltaY);
@@ -3874,8 +3941,7 @@
if (deltaX == 0) {
// keep the scrollbar on the screen even there is no
// scroll
- awakenScrollBars(ViewConfiguration
- .getScrollDefaultDelay(), false);
+ keepScrollBarsVisible = true;
} else {
scrollBy(deltaX, 0);
}
@@ -3885,8 +3951,7 @@
if (deltaY == 0) {
// keep the scrollbar on the screen even there is no
// scroll
- awakenScrollBars(ViewConfiguration
- .getScrollDefaultDelay(), false);
+ keepScrollBarsVisible = true;
} else {
scrollBy(0, deltaY);
}
@@ -3913,13 +3978,17 @@
}
}
- if (done) {
+ if (keepScrollBarsVisible) {
+ if (mHeldMotionless != MOTIONLESS_TRUE) {
+ mHeldMotionless = MOTIONLESS_TRUE;
+ invalidate();
+ }
// keep the scrollbar on the screen even there is no scroll
awakenScrollBars(ViewConfiguration.getScrollDefaultDelay(),
false);
// return false to indicate that we can't pan out of the
// view space
- return false;
+ return !done;
}
break;
}
@@ -3972,6 +4041,9 @@
break;
}
case TOUCH_DRAG_MODE:
+ mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
+ mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
+ mHeldMotionless = MOTIONLESS_TRUE;
// redraw in high-quality, as we're done dragging
invalidate();
// if the user waits a while w/o moving before the
@@ -4011,6 +4083,9 @@
}
mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+ mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
+ mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
+ mHeldMotionless = MOTIONLESS_TRUE;
mTouchMode = TOUCH_DONE_MODE;
nativeHideCursor();
break;
@@ -4037,6 +4112,7 @@
private static final int SELECT_CURSOR_OFFSET = 16;
private int mSelectX = 0;
private int mSelectY = 0;
+ private boolean mFocusSizeChanged = false;
private boolean mShiftIsPressed = false;
private boolean mTrackballDown = false;
private long mTrackballUpTime = 0;
@@ -4596,9 +4672,6 @@
}
int x = viewToContentX((int) event.getX() + mWebTextView.getLeft());
int y = viewToContentY((int) event.getY() + mWebTextView.getTop());
- // In case the soft keyboard has been dismissed, bring it back up.
- InputMethodManager.getInstance(getContext()).showSoftInput(mWebTextView,
- 0);
if (nativeFocusNodePointer() != nativeCursorNodePointer()) {
nativeMotionUp(x, y, mNavSlop);
}
@@ -4683,8 +4756,12 @@
// called by JNI
private void sendPluginState(int state) {
WebViewCore.PluginStateData psd = new WebViewCore.PluginStateData();
- psd.mFrame = nativeCursorFramePointer();
- psd.mNode = nativeCursorNodePointer();
+ psd.mFrame = nativeFocusCandidateFramePointer();
+ psd.mNode = nativeFocusCandidatePointer();
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "sendPluginState frame=" + psd.mFrame
+ + " node=" + psd.mNode);
+ }
psd.mState = state;
mWebViewCore.sendMessage(EventHub.PLUGIN_STATE, psd);
}
@@ -4875,9 +4952,10 @@
class PrivateHandler extends Handler {
@Override
public void handleMessage(Message msg) {
- if (DebugFlags.WEB_VIEW) {
+ // exclude INVAL_RECT_MSG_ID since it is frequently output
+ if (DebugFlags.WEB_VIEW && msg.what != INVAL_RECT_MSG_ID) {
Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what
- > INVAL_RECT_MSG_ID ? Integer.toString(msg.what)
+ > REQUEST_KEYBOARD ? Integer.toString(msg.what)
: HandlerDebugString[msg.what - REMEMBER_PASSWORD]);
}
if (mWebViewCore == null) {
@@ -5039,6 +5117,9 @@
/ mZoomOverviewWidth, false);
}
}
+ if (draw.mFocusSizeChanged && inEditingMode()) {
+ mFocusSizeChanged = true;
+ }
break;
}
case WEBCORE_INITIALIZED_MSG_ID:
@@ -5165,9 +5246,29 @@
hideSoftKeyboard();
} else {
displaySoftKeyboard(false);
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "REQUEST_KEYBOARD"
+ + " focusCandidateIsPlugin="
+ + nativeFocusCandidateIsPlugin());
+ }
}
break;
+ case DRAG_HELD_MOTIONLESS:
+ mHeldMotionless = MOTIONLESS_TRUE;
+ invalidate();
+ // fall through to keep scrollbars awake
+
+ case AWAKEN_SCROLL_BARS:
+ if (mTouchMode == TOUCH_DRAG_MODE
+ && mHeldMotionless == MOTIONLESS_TRUE) {
+ awakenScrollBars(ViewConfiguration
+ .getScrollDefaultDelay(), false);
+ mPrivateHandler.sendMessageDelayed(mPrivateHandler
+ .obtainMessage(AWAKEN_SCROLL_BARS),
+ ViewConfiguration.getScrollDefaultDelay());
+ }
+ break;
default:
super.handleMessage(msg);
break;
@@ -5574,6 +5675,17 @@
}
/**
+ * Draw the HTML page into the specified canvas. This call ignores any
+ * view-specific zoom, scroll offset, or other changes. It does not draw
+ * any view-specific chrome, such as progress or URL bars.
+ *
+ * @hide only needs to be accessible to Browser and testing
+ */
+ public void drawPage(Canvas canvas) {
+ mWebViewCore.drawContentPicture(canvas, 0, false, false);
+ }
+
+ /**
* Update our cache with updatedText.
* @param updatedText The new text to put in our cache.
*/
@@ -5591,7 +5703,6 @@
/* package */ native boolean nativeCursorMatchesFocus();
private native boolean nativeCursorIntersects(Rect visibleRect);
private native boolean nativeCursorIsAnchor();
- private native boolean nativeCursorIsPlugin();
private native boolean nativeCursorIsTextInput();
private native Point nativeCursorPosition();
private native String nativeCursorText();
@@ -5610,7 +5721,9 @@
private native void nativeDumpDisplayTree(String urlOrNull);
private native int nativeFindAll(String findLower, String findUpper);
private native void nativeFindNext(boolean forward);
+ private native int nativeFocusCandidateFramePointer();
private native boolean nativeFocusCandidateIsPassword();
+ private native boolean nativeFocusCandidateIsPlugin();
private native boolean nativeFocusCandidateIsRtlText();
private native boolean nativeFocusCandidateIsTextField();
private native boolean nativeFocusCandidateIsTextInput();
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 86685fb..ad19b0f 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.Intent;
+import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.DrawFilter;
import android.graphics.Paint;
@@ -26,11 +27,13 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.provider.Browser;
+import android.provider.OpenableColumns;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.KeyEvent;
@@ -273,6 +276,39 @@
mCallbackProxy.onJsAlert(url, message);
}
+
+ /**
+ * Called by JNI. Open a file chooser to upload a file.
+ * @return String version of the URI plus the name of the file.
+ * FIXME: Just return the URI here, and in FileSystem::pathGetFileName, call
+ * into Java to get the filename.
+ */
+ private String openFileChooser() {
+ Uri uri = mCallbackProxy.openFileChooser();
+ if (uri == null) return "";
+ // Find out the name, and append it to the URI.
+ // Webkit will treat the name as the filename, and
+ // the URI as the path. The URI will be used
+ // in BrowserFrame to get the actual data.
+ Cursor cursor = mContext.getContentResolver().query(
+ uri,
+ new String[] { OpenableColumns.DISPLAY_NAME },
+ null,
+ null,
+ null);
+ String name = "";
+ if (cursor != null) {
+ try {
+ if (cursor.moveToNext()) {
+ name = cursor.getString(0);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ return uri.toString() + "/" + name;
+ }
+
/**
* Notify the browser that the origin has exceeded it's database quota.
* @param url The URL that caused the overflow.
@@ -422,6 +458,8 @@
*/
private native boolean nativeRecordContent(Region invalRegion, Point wh);
+ private native boolean nativeFocusBoundsChanged();
+
/**
* Splits slow parts of the picture set. Called from the webkit
* thread after nativeDrawContent returns true.
@@ -1593,6 +1631,7 @@
int mMinPrefWidth;
RestoreState mRestoreState; // only non-null if it is for the first
// picture set after the first layout
+ boolean mFocusSizeChanged;
}
private void webkitDraw() {
@@ -1607,6 +1646,7 @@
if (mWebView != null) {
// Send the native view size that was used during the most recent
// layout.
+ draw.mFocusSizeChanged = nativeFocusBoundsChanged();
draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
if (mSettings.getUseWideViewPort()) {
draw.mMinPrefWidth = Math.max(
@@ -2175,6 +2215,11 @@
return view;
}
+ private void updateSurface(ViewManager.ChildView childView, int x, int y,
+ int width, int height) {
+ childView.attachView(x, y, width, height);
+ }
+
private void destroySurface(ViewManager.ChildView childView) {
childView.removeView();
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 165794a..5991ad4 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3560,6 +3560,7 @@
// into the scrap heap
int viewType = lp.viewType;
if (!shouldRecycleViewType(viewType)) {
+ removeDetachedView(scrap, false);
return;
}
diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java
index 8019f14..07c3e4b 100644
--- a/core/java/android/widget/QuickContactBadge.java
+++ b/core/java/android/widget/QuickContactBadge.java
@@ -25,9 +25,9 @@
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.QuickContact;
import android.provider.ContactsContract.Intents;
import android.provider.ContactsContract.PhoneLookup;
+import android.provider.ContactsContract.QuickContact;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.util.AttributeSet;
@@ -55,21 +55,28 @@
static final private int TOKEN_PHONE_LOOKUP = 1;
static final private int TOKEN_EMAIL_LOOKUP_AND_TRIGGER = 2;
static final private int TOKEN_PHONE_LOOKUP_AND_TRIGGER = 3;
+ static final private int TOKEN_CONTACT_LOOKUP_AND_TRIGGER = 4;
static final String[] EMAIL_LOOKUP_PROJECTION = new String[] {
RawContacts.CONTACT_ID,
Contacts.LOOKUP_KEY,
};
- static int EMAIL_ID_COLUMN_INDEX = 0;
- static int EMAIL_LOOKUP_STRING_COLUMN_INDEX = 1;
+ static final int EMAIL_ID_COLUMN_INDEX = 0;
+ static final int EMAIL_LOOKUP_STRING_COLUMN_INDEX = 1;
static final String[] PHONE_LOOKUP_PROJECTION = new String[] {
PhoneLookup._ID,
PhoneLookup.LOOKUP_KEY,
};
- static int PHONE_ID_COLUMN_INDEX = 0;
- static int PHONE_LOOKUP_STRING_COLUMN_INDEX = 1;
+ static final int PHONE_ID_COLUMN_INDEX = 0;
+ static final int PHONE_LOOKUP_STRING_COLUMN_INDEX = 1;
+ static final String[] CONTACT_LOOKUP_PROJECTION = new String[] {
+ Contacts._ID,
+ Contacts.LOOKUP_KEY,
+ };
+ static final int CONTACT_ID_COLUMN_INDEX = 0;
+ static final int CONTACT_LOOKUPKEY_COLUMN_INDEX = 1;
public QuickContactBadge(Context context) {
@@ -181,9 +188,9 @@
public void onClick(View v) {
if (mContactUri != null) {
- final ContentResolver resolver = getContext().getContentResolver();
- final Uri lookupUri = Contacts.getLookupUri(resolver, mContactUri);
- trigger(lookupUri);
+ mQueryHandler.startQuery(TOKEN_CONTACT_LOOKUP_AND_TRIGGER, null,
+ mContactUri,
+ CONTACT_LOOKUP_PROJECTION, null, null, null);
} else if (mContactEmail != null) {
mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP_AND_TRIGGER, mContactEmail,
Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)),
@@ -249,6 +256,17 @@
lookupUri = Contacts.getLookupUri(contactId, lookupKey);
}
}
+
+ case TOKEN_CONTACT_LOOKUP_AND_TRIGGER: {
+ if (cursor != null && cursor.moveToFirst()) {
+ long contactId = cursor.getLong(CONTACT_ID_COLUMN_INDEX);
+ String lookupKey = cursor.getString(CONTACT_LOOKUPKEY_COLUMN_INDEX);
+ lookupUri = Contacts.getLookupUri(contactId, lookupKey);
+ trigger = true;
+ }
+
+ break;
+ }
}
} finally {
if (cursor != null) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index bf3d26e..3ed995bc 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5198,7 +5198,7 @@
mDesiredHeightAtMeasure = desired;
if (heightMode == MeasureSpec.AT_MOST) {
- height = Math.min(desired, height);
+ height = Math.min(desired, heightSize);
}
}
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index 38f3fda..46000c9 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -20,6 +20,7 @@
#include <utils/misc.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
+#include <utils/String16.h>
#include "wifi.h"
@@ -92,7 +93,8 @@
if (doCommand(cmd, reply, sizeof(reply)) != 0) {
return env->NewStringUTF(NULL);
} else {
- return env->NewStringUTF(reply);
+ String16 str((char *)reply);
+ return env->NewString((const jchar *)str.string(), str.size());
}
}
diff --git a/core/res/assets/images/combobox-disabled.png b/core/res/assets/images/combobox-disabled.png
index 42fc0c5..fe220e4 100644
--- a/core/res/assets/images/combobox-disabled.png
+++ b/core/res/assets/images/combobox-disabled.png
Binary files differ
diff --git a/core/res/assets/images/combobox-noHighlight.png b/core/res/assets/images/combobox-noHighlight.png
index 838dc65..abcdf72 100644
--- a/core/res/assets/images/combobox-noHighlight.png
+++ b/core/res/assets/images/combobox-noHighlight.png
Binary files differ
diff --git a/core/res/assets/webkit/youtube.html b/core/res/assets/webkit/youtube.html
index 2aaaa15..45d9c5e 100644
--- a/core/res/assets/webkit/youtube.html
+++ b/core/res/assets/webkit/youtube.html
@@ -30,14 +30,8 @@
</head>
<body>
<div id="bg">
- <table height="100%" width="100%" border="0" cellpadding="0"
- cellspacing="0">
- <tr>
- <td valign="middle">
- <img src="http://img.youtube.com/vi/VIDEO_ID/0.jpg" width="100%"/>
- </td>
- </tr>
- </table>
+ <img src="http://img.youtube.com/vi/VIDEO_ID/0.jpg"
+ style="width:100%; height:100%"/>
</div>
<div id="main">
<table height="100%" width="100%">
diff --git a/core/res/res/layout/alert_dialog.xml b/core/res/res/layout/alert_dialog.xml
index cf2de05..40e3f42 100644
--- a/core/res/res/layout/alert_dialog.xml
+++ b/core/res/res/layout/alert_dialog.xml
@@ -50,11 +50,11 @@
android:paddingTop="6dip"
android:paddingRight="10dip"
android:src="@drawable/ic_dialog_info" />
- <com.android.internal.widget.DialogTitle android:id="@+id/alertTitle"
+ <com.android.internal.widget.DialogTitle android:id="@+id/alertTitle"
style="?android:attr/textAppearanceLarge"
android:singleLine="true"
android:ellipsize="end"
- android:layout_width="fill_parent"
+ android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<ImageView android:id="@+id/titleDivider"
@@ -63,9 +63,7 @@
android:visibility="gone"
android:scaleType="fitXY"
android:gravity="fill_horizontal"
- android:src="@android:drawable/dialog_divider_horizontal_light"
- android:layout_marginLeft="10dip"
- android:layout_marginRight="10dip"/>
+ android:src="@android:drawable/divider_horizontal_dark" />
<!-- If the client uses a customTitle, it will be added here. -->
</LinearLayout>
@@ -88,7 +86,7 @@
android:padding="5dip" />
</ScrollView>
</LinearLayout>
-
+
<FrameLayout android:id="@+id/customPanel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
@@ -99,13 +97,13 @@
android:paddingTop="5dip"
android:paddingBottom="5dip" />
</FrameLayout>
-
+
<LinearLayout android:id="@+id/buttonPanel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="54dip"
- android:orientation="vertical" >
- <LinearLayout
+ android:orientation="vertical" >
+ <LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
diff --git a/core/res/res/layout/grant_credentials_permission.xml b/core/res/res/layout/grant_credentials_permission.xml
index fe1c22e..84b66230 100644
--- a/core/res/res/layout/grant_credentials_permission.xml
+++ b/core/res/res/layout/grant_credentials_permission.xml
@@ -1,41 +1,163 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
+/**
+ * Copyright (c) 2008, Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:layout_height="fill_parent">
+
+ <!-- The header -->
<TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/message" />
- <Button android:id="@+id/allow"
+ android:id="@+id/header_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:text="@string/allow" />
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/white"
+ android:textStyle="bold"
+ android:text="@string/grant_permissions_header_text"
+ android:shadowColor="@color/shadow"
+ android:shadowRadius="2"
+ android:singleLine="true"
+ android:background="@drawable/title_bar_medium"
+ android:gravity="left|center_vertical"
+ android:paddingLeft="19dip"
+ android:ellipsize="marquee" />
- <Button android:id="@+id/deny"
+ <!-- The list of packages that correspond to the requesting UID
+ and the account/authtokenType that is being requested -->
+ <ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:text="@string/deny" />
+ android:fillViewport="true"
+ android:layout_weight="1"
+ android:gravity="top|center_horizontal"
+ android:foreground="@drawable/title_bar_shadow">
- <ListView android:id="@+id/packages_list"
- android:layout_width="fill_parent" android:layout_height="fill_parent"/>
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:paddingTop="14dip"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/grant_credentials_permission_message_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/grant_credentials_permission_message_header"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:paddingLeft="19dip"
+ android:paddingBottom="12dip" />
+
+ <LinearLayout
+ android:id="@+id/packages_list"
+ android:orientation="vertical"
+ android:paddingLeft="16dip"
+ android:paddingRight="12dip"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ <RelativeLayout
+ android:paddingLeft="16dip"
+ android:paddingRight="12dip"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/permission_icon"
+ android:layout_width="30dip"
+ android:layout_height="30dip"
+ android:src="@drawable/ic_bullet_key_permission"
+ android:layout_alignParentLeft="true"
+ android:scaleType="fitCenter" />
+
+ <TextView
+ android:id="@+id/account_type"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/perms_dangerous_perm_color"
+ android:textStyle="bold"
+ android:paddingLeft="6dip"
+ android:layout_toRightOf="@id/permission_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/account_name"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="@color/perms_dangerous_perm_color"
+ android:layout_marginTop="-4dip"
+ android:paddingBottom="8dip"
+ android:paddingLeft="6dip"
+ android:layout_below="@id/account_type"
+ android:layout_toRightOf="@id/permission_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/authtoken_type"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/perms_dangerous_perm_color"
+ android:textStyle="bold"
+ android:layout_marginTop="-4dip"
+ android:paddingBottom="8dip"
+ android:paddingLeft="6dip"
+ android:layout_below="@id/account_name"
+ android:layout_toRightOf="@id/permission_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </RelativeLayout>
+
+ <TextView
+ android:id="@+id/grant_credentials_permission_message_footer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/grant_credentials_permission_message_footer"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:paddingLeft="19dip"
+ android:paddingBottom="12dip" />
+ </LinearLayout>
+ </ScrollView>
+
+ <!-- The buttons to allow or deny -->
+ <LinearLayout
+ android:id="@+id/buttons"
+ android:layout_width="fill_parent"
+ android:layout_height="52dip"
+ android:background="@drawable/bottom_bar"
+ android:paddingTop="4dip"
+ android:paddingLeft="2dip"
+ android:paddingRight="2dip">
+
+ <Button
+ android:id="@+id/allow_button"
+ android:text="@string/allow"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="2" />
+
+ <Button
+ android:id="@+id/deny_button"
+ android:text="@string/deny"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="2" />
+
+ </LinearLayout>
</LinearLayout>
+
diff --git a/core/res/res/layout/permissions_account_and_authtokentype.xml b/core/res/res/layout/permissions_account_and_authtokentype.xml
new file mode 100644
index 0000000..4494a2c5
--- /dev/null
+++ b/core/res/res/layout/permissions_account_and_authtokentype.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+ Defines the layout of an account and authtoken type permission item.
+ Contains an icon, the account type and name and the authtoken type.
+-->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/permission_icon"
+ android:layout_width="30dip"
+ android:layout_height="30dip"
+ android:drawable="@drawable/ic_bullet_key_permission"
+ android:layout_alignParentLeft="true"
+ android:scaleType="fitCenter" />
+
+
+ <TextView
+ android:id="@+id/account_type"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="bold"
+ android:paddingLeft="6dip"
+ android:layout_toRightOf="@id/permission_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/account_name"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_marginTop="-4dip"
+ android:paddingBottom="8dip"
+ android:paddingLeft="6dip"
+ android:layout_below="@id/account_type"
+ android:layout_toRightOf="@id/permission_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/authtoken_type"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_marginTop="-4dip"
+ android:paddingBottom="8dip"
+ android:paddingLeft="6dip"
+ android:layout_below="@id/account_name"
+ android:layout_toRightOf="@id/permission_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+</RelativeLayout>
diff --git a/core/res/res/layout/permissions_package_list_item.xml b/core/res/res/layout/permissions_package_list_item.xml
new file mode 100644
index 0000000..1bffe51
--- /dev/null
+++ b/core/res/res/layout/permissions_package_list_item.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+ Defines the layout of a single package item.
+ Contains a bullet point icon and the name of the package.
+-->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/package_icon"
+ android:layout_width="30dip"
+ android:layout_height="30dip"
+ android:layout_alignParentLeft="true"
+ android:src="@drawable/ic_text_dot"
+ android:scaleType="fitCenter" />
+
+
+ <TextView
+ android:id="@+id/package_label"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="bold"
+ android:paddingLeft="6dip"
+ android:layout_toRightOf="@id/package_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+</RelativeLayout>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index fba6c08..67fac9f 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"Sleduje vaši fyzickou polohu"</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"Síťová komunikace"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"Umožňuje aplikacím získat přístup k různým funkcím sítě."</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"Vaše účty Google"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"Přístup k dostupným účtům Google."</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"Řízení hardwaru"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"Přímý přístup k hardwaru telefonu."</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"Telefonní hovory"</string>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Zadejte kód PIN"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Nesprávný kód PIN"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Chcete-li telefon odemknout, stiskněte Menu a poté 0."</string>
@@ -447,6 +553,8 @@
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Zkuste to prosím znovu"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Nabíjení (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"Nabito."</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Připojte dobíjecí zařízení."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Není vložena SIM karta."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"V telefonu není žádná karta SIM."</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"Nabíjení..."</string>
<string name="battery_low_title" msgid="7923774589611311406">"Prosím připojte dobíjecí zařízení"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Baterie je vybitá:"</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"zbývá méně než <xliff:g id="NUMBER">%d%%</xliff:g>."</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"Využití baterie"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Test továrního nastavení se nezdařil"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Test FACTORY_TEST lze provést pouze u balíčků nainstalovaných ve složce /system/app."</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Chcete opustit tuto stránku?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Vyberte OK, chcete-li pokračovat, nebo Zrušit, chcete-li na stránce zůstat."</string>
<string name="save_password_label" msgid="6860261758665825069">"Potvrdit"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"čtení historie a záložek Prohlížeče"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Umožňuje aplikaci číst všechny navštívené adresy URL a záložky Prohlížeče."</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"zápis do historie a záložek Prohlížeče"</string>
@@ -726,8 +837,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"Vytvořit kontakt"\n"pro <xliff:g id="NUMBER">%s</xliff:g>."</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"Zaškrtnuto"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"Nezaškrtnuto"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"Uvedené aplikace od <xliff:g id="APPLICATION">%2$s</xliff:g> požadují oprávnění přistupovat k přihlašovacím údajům účtu <xliff:g id="ACCOUNT">%1$s</xliff:g>. Chcete toto oprávnění udělit? Pokud je udělíte, vaše odpověď se uloží a tato výzva se již nebude zobrazovat."</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"Uvedené aplikace požadují od <xliff:g id="APPLICATION">%3$s</xliff:g> oprávnění přistupovat k přihlašovacím údajům (typ: <xliff:g id="TYPE">%1$s</xliff:g>) účtu <xliff:g id="ACCOUNT">%2$s</xliff:g>. Chcete toto oprávnění udělit? Pokud je udělíte, vaše odpověď se uloží a tato výzva se již nebude zobrazovat."</string>
<string name="allow" msgid="7225948811296386551">"Povolit"</string>
<string name="deny" msgid="2081879885755434506">"Odepřít"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"Požadováno oprávnění"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Usnadnění"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Tapeta"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Změnit tapetu"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 99f8ec9..7b37709 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"Overvåg din fysiske placering"</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"Netværkskommunikation"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"Tillader programmer at få adgang til forskellige netværksfunktioner."</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"Dine Google-konti"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"Få adgang til tilgængelige Google-konti."</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"Hardwarekontroller"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"Direkte adgang til hardware på håndsættet."</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"Telefonopkald"</string>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Indtast PIN-kode"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Forkert PIN-kode!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Tryk på Menu og dernæst på 0 for at låse op."</string>
@@ -447,6 +553,8 @@
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Beklager! Prøv igen"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Oplader (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"Opladt."</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Tilslut din oplader."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Der er ikke noget SIM-kort."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"Der er ikke noget SIM-kort i telefonen."</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"Oplader ..."</string>
<string name="battery_low_title" msgid="7923774589611311406">"Forbind oplader"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Batteriet er ved at blive tomt:"</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"mindre end <xliff:g id="NUMBER">%d%%</xliff:g> tilbage."</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"Batteriforbrug"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Fabrikstest mislykkedes"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Handlingen FACTORY_TEST understøttes kun af pakker installeret i /system/app."</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"Javascript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Naviger væk fra denne side?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n" Vælg OK for at fortsætte eller Annuller for at blive på den aktuelle side."</string>
<string name="save_password_label" msgid="6860261758665825069">"Bekræft"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"læs browserens oversigt og bogmærker"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Tillader programmet at læse alle de webadresser, browseren har besøgt, og alle browserens bogmærker."</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skriv browserens oversigt og bogmærker"</string>
@@ -726,8 +837,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"Opret kontakt"\n"ved hjælp af <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"kontrolleret"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"ikke kontrolleret"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"De nævnte programmer beder om tilladelse til at få adgang til loginoplysningerne til kontoen <xliff:g id="ACCOUNT">%1$s</xliff:g> fra <xliff:g id="APPLICATION">%2$s</xliff:g>. Ønsker du at give denne tilladelse? Hvis ja, så huskes dit svar, og du vil ikke blive spurgt om det igen."</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"De nævnte programmer beder om tilladelse til at få adgang til <xliff:g id="TYPE">%1$s</xliff:g>-loginoplysningerne til kontoen <xliff:g id="ACCOUNT">%2$s</xliff:g> fra <xliff:g id="APPLICATION">%3$s</xliff:g>. Vil du give denne tilladelse? Hvis ja, så huskes dit svar, og du vil ikke blive spurgt om det igen."</string>
<string name="allow" msgid="7225948811296386551">"Tillad"</string>
<string name="deny" msgid="2081879885755434506">"Afvis"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"Der er anmodet om tilladelse"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Tilgængelighed"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Tapet"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Skift tapet"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 02f685a..af68303c 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"Ihren physischen Standort überwachen"</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"Netzwerkkommunikation"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"Ermöglicht Anwendungen den Zugriff auf verschiedene Netzwerkfunktionen."</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"Ihre Google-Konten"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"Greift auf verfügbare Google-Konten zu."</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"Hardware-Steuerelemente"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"Direkter Zugriff auf Hardware über Headset"</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"Anrufe"</string>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN-Code eingeben"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Falscher PIN-Code!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Drücken Sie zum Entsperren die Menütaste und dann auf \"0\"."</string>
@@ -447,6 +553,8 @@
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Tut uns leid. Versuchen Sie es noch einmal."</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Wird geladen (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"Aufgeladen"</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Bitte Ladegerät anschließen"</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Keine SIM-Karte."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"Keine SIM-Karte im Telefon."</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"Wird aufgeladen..."</string>
<string name="battery_low_title" msgid="7923774589611311406">"Ladegerät anschließen"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Akku ist fast leer."</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"Nur noch weniger als <xliff:g id="NUMBER">%d%%</xliff:g> vorhanden."</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"Akkuverbrauch"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Werkstest fehlgeschlagen"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Die Aktion FACTORY_TEST wird nur für unter \"/system/app\" gespeicherte Pakete unterstützt."</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Von dieser Seite navigieren?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Wählen Sie \"OK\", um fortzufahren, oder wählen Sie \"Abbrechen\", um auf der aktuellen Seite zu bleiben."</string>
<string name="save_password_label" msgid="6860261758665825069">"Bestätigen"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"Browserverlauf und Lesezeichen lesen"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Ermöglicht der Anwendung, alle URLs, die mit dem Browser besucht wurden, sowie alle Lesezeichen des Browsers zu lesen."</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"Browserverlauf und Lesezeichen schreiben"</string>
@@ -726,8 +837,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"Neuer Kontakt"\n"mit <xliff:g id="NUMBER">%s</xliff:g> erstellen"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"aktiviert"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"nicht aktiviert"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"Die aufgelisteten Anwendungen fordern eine Berechtigung zum Zugriff auf die Anmeldeinformationen für das Konto <xliff:g id="ACCOUNT">%1$s</xliff:g> von <xliff:g id="APPLICATION">%2$s</xliff:g> an. Möchten Sie diese Berechtigung erteilen? Wenn ja, wird Ihre Antwort gespeichert und Sie erhalten keine erneute Aufforderung."</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"Die aufgelisteten Anwendungen fordern eine Berechtigung zum Zugriff auf die <xliff:g id="TYPE">%1$s</xliff:g>-Anmeldeinformationen für das Konto <xliff:g id="APPLICATION">%3$s</xliff:g> von <xliff:g id="ACCOUNT">%2$s</xliff:g> an. Möchten Sie diese Berechtigung erteilen? Wenn ja, wird Ihre Antwort gespeichert und Sie erhalten keine erneute Aufforderung."</string>
<string name="allow" msgid="7225948811296386551">"Zulassen"</string>
<string name="deny" msgid="2081879885755434506">"Ablehnen"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"Berechtigung angefordert"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Eingabehilfen"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Hintergrund"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Hintergrundbild ändern"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 23c6565..a4ec8c1 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"Παρακολούθηση της φυσικής τοποθεσίας σας"</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"Επικοινωνία δικτύου"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"Επιτρέπει σε εφαρμογές να αποκτήσουν πρόσβαση σε διάφορες λειτουργίες δικτύου."</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"Οι λογαριασμοί σας Google"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"Πρόσβαση στους διαθέσιμους λογαριασμούς Google."</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"Στοιχεία ελέγχου υλικού"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"Άμεση πρόσβαση στο υλικό της συσκευής τηλεφώνου."</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"Τηλεφωνικές κλήσεις"</string>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Πληκτρολογήστε τον κωδικό αριθμό PIN"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Εσφαλμένος κωδικός αριθμός PIN!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Για ξεκλείδωμα, πατήστε το πλήκτρο Menu και, στη συνέχεια, το πλήκτρο 0."</string>
@@ -447,6 +553,8 @@
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Προσπαθήστε αργότερα"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Φόρτιση (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"Φορτίστηκε."</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Συνδέστε τον φορτιστή."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Δεν υπάρχει κάρτα SIM."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"Δεν υπάρχει κάρτα SIM στο τηλέφωνο."</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"Φόρτιση..."</string>
<string name="battery_low_title" msgid="7923774589611311406">"Συνδέστε τον φορτιστή"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Η στάθμη της μπαταρίας είναι χαμηλή:"</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"απομένουν λιγότερο από <xliff:g id="NUMBER">%d%%</xliff:g>."</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"Χρήση μπαταρίας"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Η εργοστασιακή δοκιμή απέτυχε"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Η ενέργεια FACTORY_TEST υποστηρίζεται μόνο για πακέτα που είναι εγκατεστημένα στον κατάλογο /system/app."</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Απομάκρυνση από αυτή τη σελίδα;"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Επιλέξτε OK για συνέχεια, ή Ακύρωση για παραμονή στην τρέχουσα σελίδα."</string>
<string name="save_password_label" msgid="6860261758665825069">"Επιβεβαίωση"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ανάγνωση ιστορικού και σελιδοδεικτών προγράμματος περιήγησης"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Επιτρέπει στην εφαρμογή την ανάγνωση όλων των διευθύνσεων URL που το πρόγραμμα περιήγησης έχει επισκεφθεί και όλων των σελιδοδεικτών του προγράμματος περιήγησης."</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"εγγραφή ιστορικού και σελιδοδεικτών προγράμματος περιήγησης"</string>
@@ -726,8 +837,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"Δημιουργία επαφής"\n"με τη χρήση του <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"επιλεγμένο"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"δεν ελέγχθηκε"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"Οι εφαρμογές που παραθέτονται στη λίστα ζητούν άδεια για να αποκτήσουν πρόσβαση στα διαπιστευτήρια σύνδεσης για τον λογαριασμό <xliff:g id="ACCOUNT">%1$s</xliff:g> από <xliff:g id="APPLICATION">%2$s</xliff:g>. Θα αποδεχτείτε το αίτημα; Εάν το αποδεχτείτε, η απάντησή σας θα αποθηκευτεί και δεν θα ερωτηθείτε ξανά."</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"Οι εφαρμογές που παραθέτονται στη λίστα ζητούν άδεια για να αποκτήσουν πρόσβαση στα διαπιστευτήρια σύνδεσης <xliff:g id="TYPE">%1$s</xliff:g> για τον λογαριασμό <xliff:g id="ACCOUNT">%2$s</xliff:g> από <xliff:g id="APPLICATION">%3$s</xliff:g>. Θα αποδεχτείτε το αίτημα; Εάν το αποδεχτείτε, η απάντησή σας θα αποθηκευτεί και δεν θα ερωτηθείτε ξανά."</string>
<string name="allow" msgid="7225948811296386551">"Να επιτρέπεται"</string>
<string name="deny" msgid="2081879885755434506">"Άρνηση"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"Απαιτείται άδεια"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Προσβασιμότητα"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Ταπετσαρία"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Αλλαγή ταπετσαρίας"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 88f16a9..025ae60 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -777,8 +777,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"Crear contacto "\n"con <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"verificado"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"no verificado"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"Las aplicaciones enumeradas requieren permiso para acceder a las credenciales de inicio de sesión para la cuenta <xliff:g id="ACCOUNT">%1$s</xliff:g> desde <xliff:g id="APPLICATION">%2$s</xliff:g> ¿Deseas otorgar este permiso? Si es así, el sistema recordará tu respuesta y no volverá a solicitarla."</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"Las aplicaciones enumeradas requieren permiso para acceder a las <xliff:g id="TYPE">%1$s</xliff:g> credenciales de inicio de sesión para la cuenta <xliff:g id="ACCOUNT">%2$s</xliff:g> desde <xliff:g id="APPLICATION">%3$s</xliff:g>.¿Deseas otorgar este permiso? Si es así, el sistema recordará tu respuesta y no volverá a solicitarla."</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Denegar"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"Permiso solicitado"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 74f7e11..ebe1bb0 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"Controlar su ubicación física"</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"Comunicación de red"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"Permite que las aplicaciones accedan a distintas funciones de red."</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"Tus cuentas de Google"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"Acceder a las cuentas de Google disponibles"</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"Controles de hardware"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"Acceso directo al hardware del móvil"</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"Llamadas de teléfono"</string>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Introduce el código PIN"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"El código PIN es incorrecto."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear el teléfono, pulsa la tecla de menú y, a continuación, pulsa 0."</string>
@@ -447,6 +553,8 @@
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Inténtalo de nuevo"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Cargando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"Cargado"</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Conecta el cargador"</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Falta la tarjeta SIM"</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"No se ha insertado ninguna tarjeta SIM en el teléfono."</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"Cargando..."</string>
<string name="battery_low_title" msgid="7923774589611311406">"Conecta el cargador"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Se está agotando la batería:"</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"menos del <xliff:g id="NUMBER">%d%%</xliff:g> disponible."</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"Uso de la batería"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Fallo en la prueba de fábrica"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"La acción FACTORY_TEST sólo es compatible con los paquetes instalados en /system/app."</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"¿Quieres salir de esta página?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Selecciona \"Aceptar\" para continuar o \"Cancelar\" para permanecer en la página actual."</string>
<string name="save_password_label" msgid="6860261758665825069">"Confirmar"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"leer información de marcadores y del historial del navegador"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite que la aplicación lea todas las URL que ha visitado el navegador y todos sus marcadores."</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"escribir en marcadores y en el historial del navegador"</string>
@@ -726,8 +837,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"Crear un contacto"\n"a partir de <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"seleccionado"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"no seleccionado"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"Las aplicaciones de la lista están solicitando permiso para acceder a las credenciales de acceso de la cuenta <xliff:g id="ACCOUNT">%1$s</xliff:g> desde <xliff:g id="APPLICATION">%2$s</xliff:g>. ¿Deseas conceder este permiso? En tal caso, se recordará tu respuesta y no se te volverá a preguntar."</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"Las aplicaciones de la lista están solicitando permiso para acceder a las credenciales de acceso (<xliff:g id="TYPE">%1$s</xliff:g>) de la cuenta <xliff:g id="ACCOUNT">%2$s</xliff:g> desde <xliff:g id="APPLICATION">%3$s</xliff:g>. ¿Deseas conceder este permiso? En tal caso, se recordará tu respuesta y no se te volverá a preguntar."</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Denegar"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"Permiso solicitado"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Accesibilidad"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fondo de pantalla"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Cambiar fondo de pantalla"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c0f59f2..5647063 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"Suivre votre position géographique"</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"Communications réseau"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"Permet à des applications d\'accéder à différentes fonctionnalités du réseau."</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"Vos comptes Google"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"Accédez aux comptes Google disponibles."</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"Commandes du matériel"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"Permet d\'accéder directement au matériel de l\'appareil."</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"Appels"</string>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Saisissez le code PIN"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Le code PIN est incorrect !"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Pour débloquer le clavier, appuyez sur \"Menu\" puis sur 0."</string>
@@ -447,6 +553,8 @@
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Désolé. Merci de réessayer."</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Chargement (<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>)"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"Chargé"</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Branchez votre chargeur."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Aucune carte SIM n\'a été trouvée."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"Aucune carte SIM n\'est insérée dans le téléphone."</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"Chargement..."</string>
<string name="battery_low_title" msgid="7923774589611311406">"Branchez le chargeur"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Le niveau de la batterie est bas :"</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"Batterie restante inférieure à <xliff:g id="NUMBER">%d%%</xliff:g>."</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"Utilisation de la batterie"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Échec du test usine"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"L\'action FACTORY_TEST est uniquement prise en charge pour les paquets de données installés dans in/system/app."</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Vous souhaitez quitter cette page ?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Sélectionnez OK pour continuer ou Annuler pour rester sur la page actuelle."</string>
<string name="save_password_label" msgid="6860261758665825069">"Confirmer"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"lire l\'historique et les favoris du navigateur"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Autorise l\'application à lire toutes les URL auxquelles le navigateur a accédé et tous ses favoris."</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"écrire dans l\'historique et les favoris du navigateur"</string>
@@ -726,8 +837,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"Ajouter un contact"\n"en utilisant <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"sélectionné"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"non sélectionné"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"Les applications répertoriées demandent l\'autorisation d\'accéder aux informations d\'identification du compte <xliff:g id="ACCOUNT">%1$s</xliff:g> depuis <xliff:g id="APPLICATION">%2$s</xliff:g>. Souhaitez-vous accorder cette autorisation ? Si vous acceptez, votre choix sera enregistré et cette question ne vous sera plus posée."</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"Les applications répertoriées demandent l\'autorisation d\'accéder aux informations d\'identification des <xliff:g id="TYPE">%1$s</xliff:g> associés au compte <xliff:g id="ACCOUNT">%2$s</xliff:g> depuis <xliff:g id="APPLICATION">%3$s</xliff:g>. Souhaitez-vous accorder cette autorisation ? Si vous acceptez, votre choix sera enregistré et cette question ne vous sera plus posée."</string>
<string name="allow" msgid="7225948811296386551">"Autoriser"</string>
<string name="deny" msgid="2081879885755434506">"Refuser"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"Autorisation demandée"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Accessibilité"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fond d\'écran"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Changer de fond d\'écran"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index d62625f..f0647e7 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -133,7 +133,7 @@
<string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Modalità silenziosa"</string>
<string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Audio non attivo"</string>
<string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"Audio attivo"</string>
- <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modalità aereo attiva"</string>
+ <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modalità aereo"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Modalità aereo attiva"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Modalità aereo non attiva"</string>
<string name="safeMode" msgid="2788228061547930246">"Modalità provvisoria"</string>
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"Monitorare la posizione fisica dell\'utente"</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"Comunicazione di rete"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"Consentono l\'accesso delle applicazioni a varie funzionalità di rete."</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"I tuoi account Google"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"Accedere agli account Google disponibili."</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"Controlli hardware"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"Accedere direttamente all\'hardware del ricevitore."</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"Telefonate"</string>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Inserisci il PIN"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Codice PIN errato."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Per sbloccare, premi Menu, poi 0."</string>
@@ -447,6 +553,8 @@
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Riprova"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"In carica (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"Carico."</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Collegare il caricabatterie."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Nessuna SIM presente."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"Nessuna SIM presente nel telefono."</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"In carica..."</string>
<string name="battery_low_title" msgid="7923774589611311406">"Collegare il caricabatterie"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Batteria quasi scarica:"</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"energia residua inferiore a <xliff:g id="NUMBER">%d%%</xliff:g>."</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"Utilizzo batteria"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Test di fabbrica non riuscito"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"L\'azione FACTORY_TEST è supportata soltanto per i pacchetti installati in /system/app."</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Uscire da questa pagina?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Seleziona OK per continuare o Annulla per rimanere nella pagina corrente."</string>
<string name="save_password_label" msgid="6860261758665825069">"Conferma"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"lettura cronologia e segnalibri del browser"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Consente all\'applicazione di leggere tutti gli URL visitati e tutti i segnalibri del browser."</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"creazione cronologia e segnalibri del browser"</string>
@@ -726,8 +837,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"Crea contatto"\n"utilizzando <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"selezionato"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"non selezionato"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"Le applicazioni elencate richiedono l\'autorizzazione per accedere alle credenziali di accesso per l\'account <xliff:g id="ACCOUNT">%1$s</xliff:g> da <xliff:g id="APPLICATION">%2$s</xliff:g>. Concedere questa autorizzazione? In tal caso la tua risposta verrà memorizzata e questa domanda non ti verrà più posta."</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"Le applicazioni elencate richiedono l\'autorizzazione per accedere alle credenziali di accesso <xliff:g id="TYPE">%1$s</xliff:g> per l\'account <xliff:g id="ACCOUNT">%2$s</xliff:g> da <xliff:g id="APPLICATION">%3$s</xliff:g>. Concedere questa autorizzazione? In tal caso la tua risposta verrà memorizzata e questa domanda non ti verrà più posta."</string>
<string name="allow" msgid="7225948811296386551">"Consenti"</string>
<string name="deny" msgid="2081879885755434506">"Nega"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"Autorizzazione richiesta"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Accesso facilitato"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Sfondo"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Cambia sfondo"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 55e6b50..9517af5 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"現在地を追跡"</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"ネットワーク通信"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"ネットワークのさまざまな機能へのアクセスをアプリケーションに許可します。"</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"Googleアカウント"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"利用可能なGoogleアカウントへのアクセス"</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"ハードウェアの制御"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"携帯電話のハードウェアに直接アクセスします。"</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"電話/通話"</string>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PINコードを入力"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"PINコードが正しくありません。"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"MENU、0キーでロック解除"</string>
@@ -447,6 +553,8 @@
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"やり直してください"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"充電中(<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"充電完了。"</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"充電してください。"</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"SIMカードが挿入されていません"</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"SIMカードが挿入されていません"</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"充電中..."</string>
<string name="battery_low_title" msgid="7923774589611311406">"充電してください"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"電池が残り少なくなっています:"</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"残量<xliff:g id="NUMBER">%d%%</xliff:g>以下"</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"電池使用量"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"出荷時試験が失敗"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST操作は、/system/appにインストールされたパッケージのみが対象です。"</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"このページから移動しますか?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"移動する場合は[OK]、今のページに残る場合は[キャンセル]を選択してください。"</string>
<string name="save_password_label" msgid="6860261758665825069">"確認"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ブラウザの履歴とブックマークを読み取る"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"ブラウザでアクセスしたすべてのURLおよびブラウザのすべてのブックマークの読み取りをアプリケーションに許可します。"</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"ブラウザの履歴とブックマークを書き込む"</string>
@@ -726,8 +837,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"<xliff:g id="NUMBER">%s</xliff:g>を使って"\n"連絡先を新規登録"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"オン"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"オフ"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"リストされているアプリケーションが、アカウント<xliff:g id="ACCOUNT">%1$s</xliff:g>のログイン認証情報に<xliff:g id="APPLICATION">%2$s</xliff:g>からアクセスする権限をリクエストしています。この権限を許可しますか?許可すると、入力が記録され、次回以降はこのメッセージが表示されなくなります。"</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"リストされているアプリケーションが、アカウント<xliff:g id="ACCOUNT">%2$s</xliff:g>の<xliff:g id="TYPE">%1$s</xliff:g>ログイン認証情報に<xliff:g id="APPLICATION">%3$s</xliff:g>からアクセスする権限をリクエストしています。この権限を許可しますか?許可すると、入力は記録され、次回以降はこのメッセージが表示されなくなります。"</string>
<string name="allow" msgid="7225948811296386551">"許可"</string>
<string name="deny" msgid="2081879885755434506">"拒否"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"リクエスト済み権限"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"アクセシビリティ"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"壁紙"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"壁紙を変更"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 3db36fc..0ef3824 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"실제 위치 모니터링"</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"네트워크 통신"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"응용프로그램이 다양한 네트워크 기능에 액세스할 수 있도록 합니다."</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"Google 계정"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"사용 가능한 Google 계정에 액세스합니다."</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"하드웨어 제어"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"휴대전화의 하드웨어에 직접 액세스합니다."</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"전화 통화"</string>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN 코드 입력"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"PIN 코드가 잘못되었습니다."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"잠금해제하려면 메뉴를 누른 다음 0을 누릅니다."</string>
@@ -447,6 +553,8 @@
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"죄송합니다. 다시 시도하세요."</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"충전 중(<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"충전되었습니다."</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"충전기를 연결하세요."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"SIM 카드가 없습니다."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"휴대전화에 SIM 카드가 없습니다."</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"충전 중..."</string>
<string name="battery_low_title" msgid="7923774589611311406">"충전기를 연결하세요."</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"배터리 전원이 부족합니다."</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"<xliff:g id="NUMBER">%d%%</xliff:g> 미만 남음"</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"배터리 사용"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"출고 테스트 불합격"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST 작업은 /system/app 디렉토리에 설치된 패키지에 대해서만 지원됩니다."</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"자바스크립트"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"다른 페이지를 탐색하시겠습니까?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"계속하려면 \'확인\'을 선택하고 현재 페이지에 그대로 있으려면 \'취소\'를 선택하세요."</string>
<string name="save_password_label" msgid="6860261758665825069">"확인"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"브라우저의 기록 및 북마크 읽기"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"응용프로그램이 브라우저로 방문한 모든 URL과 브라우저의 모든 북마크를 읽도록 허용합니다."</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"브라우저의 기록 및 북마크 쓰기"</string>
@@ -726,8 +837,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"전화번호부에"\n"<xliff:g id="NUMBER">%s</xliff:g> 추가"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"선택함"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"선택 안함"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"나열된 응용프로그램이 <xliff:g id="APPLICATION">%2$s</xliff:g>에서 <xliff:g id="ACCOUNT">%1$s</xliff:g> 계정의 로그인 자격증명에 액세스할 수 있는 권한을 요청 중입니다. 권한을 부여하시겠습니까? 권한을 부여하면 응답 내용이 저장되며 메시지가 다시 표시되지 않습니다."</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"나열된 응용프로그램이 <xliff:g id="APPLICATION">%3$s</xliff:g>에서 <xliff:g id="ACCOUNT">%2$s</xliff:g> 계정의 <xliff:g id="TYPE">%1$s</xliff:g> 로그인 자격증명에 액세스할 수 있는 권한을 요청 중입니다. 권한을 부여하시겠습니까? 권한을 부여하면 응답 내용이 저장되며 메시지가 다시 표시되지 않습니다."</string>
<string name="allow" msgid="7225948811296386551">"허용"</string>
<string name="deny" msgid="2081879885755434506">"거부"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"권한 요청"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"접근성"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"배경화면"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"배경화면 변경"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 19524d8..aa15ad1 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"Overvåking av telefonens fysiske plassering"</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"Nettverkstilgang"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"Gir applikasjoner tilgang til diverse nettverksfunksjoner."</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"Google-kontoer"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"Tilgang til tilgjengelige Google-kontoer."</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"Maskinvarekontroll"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"Direkte tilgang til maskinvaren på telefonen."</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"Telefonsamtaler"</string>
@@ -196,7 +198,7 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Lar applikasjonen sette aktivitetshåndtereren i avslutningstilstand. Slår ikke systemet helt av."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"forhindre applikasjonsbytte"</string>
<string name="permdesc_stopAppSwitches" msgid="3857886086919033794">"Lar applikasjonen forhindre brukeren fra å bytte til en annen applikasjon."</string>
- <string name="permlab_runSetActivityWatcher" msgid="7811586187574696296">"overvåke og kontrollere all applikasjonsoppstart"</string>
+ <string name="permlab_runSetActivityWatcher" msgid="7811586187574696296">"Blokker popup-vinduer"</string>
<string name="permdesc_runSetActivityWatcher" msgid="3228701938345388092">"Lar applikasjonen overvåke og kontrollere hvordan systemet starter applikasjoner. Ondsinnede applikasjoner kan ta over systemet helt. Denne rettigheten behøves bare for utvikling, aldri for vanlig bruk av telefonen."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"kringkaste melding om fjernet pakke"</string>
<string name="permdesc_broadcastPackageRemoved" msgid="3453286591439891260">"Lar applikasjonen kringkaste en melding om at en applikasjonspakke er blitt fjernet. Ondsinnede applikasjoner kan bruke dette til å drepe vilkårlige andre kjørende applikasjoner."</string>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Skriv inn PIN-kode:"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Gal PIN-kode!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"For å låse opp, trykk på menyknappen og deretter 0."</string>
@@ -447,6 +553,8 @@
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Beklager, prøv igjen:"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Lader (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"Fullt ladet"</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Koble til en batterilader."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Mangler SIM-kort."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"Ikke noe SIM-kort i telefonen."</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"Lader…"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Koble til en lader"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Batteriet er nesten tomt:"</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"mindre enn <xliff:g id="NUMBER">%d%%</xliff:g> igjen."</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"Batteribruk"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Factory test failed"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"The FACTORY_TEST action is only supported for packages installed in /system/app."</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Naviger bort fra denne siden?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Velg OK for å fortsette, eller Avbryt for å forbli på denne siden."</string>
<string name="save_password_label" msgid="6860261758665825069">"Bekreft"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"lese nettleserens logg og bokmerker"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Lar applikasjonen lese alle adresser nettleseren har besøkt, og alle nettleserens bokmerker."</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skrive til nettleserens logg og bokmerker"</string>
@@ -726,8 +837,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"Lag kontakt"\n"med nummeret <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"valgt"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"ikke valgt"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"Det nevnte programmet ber om tilgangstillatelse til påloggingsopplysningene for konto <xliff:g id="ACCOUNT">%1$s</xliff:g> fra <xliff:g id="APPLICATION">%2$s</xliff:g>. Vil du gi denne tillatelsen? I så fall vil svaret ditt bli lagret, og du vil ikke bli spurt flere ganger."</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"Det nevnte programmet ber om tilgangstillatelse til <xliff:g id="TYPE">%1$s</xliff:g>-påloggingsopplysningene for konto <xliff:g id="ACCOUNT">%2$s</xliff:g> fra <xliff:g id="APPLICATION">%3$s</xliff:g>. Vil du gi denne tillatelsen? I så fall vil svaret ditt bli lagret, og du vil ikke bli spurt flere ganger."</string>
<string name="allow" msgid="7225948811296386551">"Tillat"</string>
<string name="deny" msgid="2081879885755434506">"Avslå"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"Tillatelse forespurt"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Tilgjengelighet"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Bakgrunnsbilde"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Endre bakgrunnsbilde"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 085a99c..c299ef8 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"Uw fysieke locatie bijhouden"</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"Netwerkcommunicatie"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"Toepassingen toestaan verschillende netwerkfuncties te openen."</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"Uw Google-accounts"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"Toegang tot de beschikbare Google-accounts."</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"Bedieningselementen hardware"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"Rechtstreekse toegang tot hardware op de handset."</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"Telefoonoproepen"</string>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN-code invoeren"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Onjuiste PIN-code!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Druk op \'Menu\' en vervolgens op 0 om te ontgrendelen."</string>
@@ -447,6 +553,8 @@
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Probeer het opnieuw"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Opladen (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"Opgeladen."</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Sluit de oplader aan."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Geen SIM-kaart."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"Geen SIM-kaart in telefoon."</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"Opladen..."</string>
<string name="battery_low_title" msgid="7923774589611311406">"Sluit de oplader aan"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"De accu raakt op:"</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"minder dan <xliff:g id="NUMBER">%d%%</xliff:g> resterend."</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"Accugebruik"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Fabriekstest mislukt"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"De actie FACTORY_TEST wordt alleen ondersteund voor pakketten die zijn geïnstalleerd in /system/app."</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Wilt u deze pagina verlaten?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Kies OK om door te gaan of Annuleren om op de huidige pagina te blijven."</string>
<string name="save_password_label" msgid="6860261758665825069">"Bevestigen"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"browsergeschiedenis en bladwijzers lezen"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Hiermee kan een toepassing de URL\'s lezen die u via de browser heeft bezocht, evenals alle bladwijzers van de browser."</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"browsergeschiedenis en bladwijzers schrijven"</string>
@@ -726,8 +837,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"Contact maken"\n"met <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"aangevinkt"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"niet aangevinkt"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"De weergegeven toepassingen vragen toestemming voor toegang tot de aanmeldingsgegevens voor account \'<xliff:g id="ACCOUNT">%1$s</xliff:g>\' van <xliff:g id="APPLICATION">%2$s</xliff:g>. Wilt u deze toestemming verlenen? Als u dit wilt doen, wordt uw antwoord onthouden en wordt dit niet opnieuw gevraagd."</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"De weergegeven toepassingen vragen toestemming voor toegang tot de aanmeldingsgegevens voor <xliff:g id="TYPE">%1$s</xliff:g> van het account \'<xliff:g id="ACCOUNT">%2$s</xliff:g>\' van <xliff:g id="APPLICATION">%3$s</xliff:g>. Wilt u deze toestemming verlenen? Als u dit wilt doen, wordt uw antwoord onthouden en wordt dit niet opnieuw gevraagd."</string>
<string name="allow" msgid="7225948811296386551">"Toestaan"</string>
<string name="deny" msgid="2081879885755434506">"Weigeren"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"Toestemming gevraagd"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Toegankelijkheid"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Achtergrond"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Achtergrond wijzigen"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 145deda..f53bd4c5 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"Monitorowanie fizycznej lokalizacji"</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"Połączenia sieciowe"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"Pozwól aplikacjom na dostęp do różnych funkcji sieci."</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"Twoje konta Google"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"Uzyskaj dostęp do dostępnych kont Google."</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"Sterowanie sprzętowe"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"Bezpośredni dostęp do elementów sprzętowych telefonu."</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"Połączenia telefoniczne"</string>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Wprowadź kod PIN"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Błędny kod PIN!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Aby odblokować, naciśnij Menu, a następnie 0."</string>
@@ -447,6 +553,8 @@
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Niestety, spróbuj ponownie"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Ładowanie (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"Naładowany."</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Podłącz ładowarkę."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Brak karty SIM."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"Brak karty SIM w telefonie."</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"Ładowanie..."</string>
<string name="battery_low_title" msgid="7923774589611311406">"Podłącz ładowarkę"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Bateria się rozładowuje:"</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"pozostało mniej niż <xliff:g id="NUMBER">%d%%</xliff:g>."</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"Użycie baterii"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Nieudany test fabryczny"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Czynność FACTORY_TEST jest obsługiwana tylko dla pakietów zainstalowanych w katalogu /system/app."</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Czy opuścić tę stronę?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Wybierz opcję OK, aby kontynuować, lub opcję Anuluj, aby pozostać na tej stronie."</string>
<string name="save_password_label" msgid="6860261758665825069">"Potwierdź"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"odczyt historii i zakładek przeglądarki"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Umożliwia aplikacji odczyt wszystkich adresów URL odwiedzonych przez przeglądarkę, a także wszystkich zakładek przeglądarki."</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"zapis historii i zakładek przeglądarki"</string>
@@ -726,8 +837,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"Utwórz kontakt"\n"dla numeru <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"zaznaczone"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"niezaznaczone"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"Wymienione aplikacje żądają pozwolenia na dostęp do danych logowania dla konta <xliff:g id="ACCOUNT">%1$s</xliff:g> powiązanego z aplikacją <xliff:g id="APPLICATION">%2$s</xliff:g>. Czy chcesz udzielić takiego pozwolenia? Jeśli tak, odpowiedź zostanie zapamiętana i to pytanie nie będzie już wyświetlane."</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"Wymienione aplikacje żądają pozwolenia na dostęp do danych logowania dotyczących funkcji <xliff:g id="TYPE">%1$s</xliff:g> dla konta <xliff:g id="ACCOUNT">%2$s</xliff:g> powiązanego z aplikacją <xliff:g id="APPLICATION">%3$s</xliff:g>. Czy chcesz udzielić takiego pozwolenia? Jeśli tak, odpowiedź zostanie zapamiętana i to pytanie nie będzie już wyświetlane."</string>
<string name="allow" msgid="7225948811296386551">"Zezwól"</string>
<string name="deny" msgid="2081879885755434506">"Odmów"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"Żądane pozwolenie"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Ułatwienia dostępu"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Tapeta"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Zmień tapetę"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 4e9a0a8..891e7ce 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"Monitorizar a sua localização física"</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"Comunicação de rede"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"Permite o acesso de aplicações a várias funcionalidades de rede."</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"As suas Contas Google"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"Aceda às Contas Google disponíveis."</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"Controlos de hardware"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"Aceda directamente ao hardware no telefone."</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"Chamadas"</string>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Introduzir código PIN"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Código PIN incorrecto!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, prima Menu e, em seguida, 0."</string>
@@ -447,6 +553,8 @@
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Lamentamos, tente novamente"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"A carregar (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"Carregado."</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Ligue o carregador."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Nenhum cartão SIM."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"Nenhum cartão SIM no telefone."</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"A carregar..."</string>
<string name="battery_low_title" msgid="7923774589611311406">"Ligue o carregador"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"A bateria está a ficar fraca:"</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"resta menos de <xliff:g id="NUMBER">%d%%</xliff:g>."</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"Utilização da bateria"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"O teste de fábrica falhou"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"A acção FACTORY_TEST apenas é suportada para pacotes instalados em /system/app."</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Navegar para outra página?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Seleccione OK para continuar ou Cancelar para permanecer na página actual."</string>
<string name="save_password_label" msgid="6860261758665825069">"Confirmar"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ler histórico e marcadores do browser"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite que a aplicação leia todos os URLs visitados pelo browser e todos os marcadores do browser."</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"gravar histórico e marcadores do browser"</string>
@@ -726,8 +837,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"Criar contacto"\n"utilizando <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"verificado"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"não verificado"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"As aplicações listadas estão a pedir autorização para aceder às credenciais de início de sessão da conta <xliff:g id="ACCOUNT">%1$s</xliff:g> a partir de <xliff:g id="APPLICATION">%2$s</xliff:g>. Pretende conceder esta autorização? Em caso afirmativo, a sua resposta será memorizada e não lhe será solicitado que responda novamente."</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"As aplicações listadas estão a pedir autorização para aceder às credenciais de início de sessão <xliff:g id="TYPE">%1$s</xliff:g> para a conta <xliff:g id="ACCOUNT">%2$s</xliff:g> a partir de <xliff:g id="APPLICATION">%3$s</xliff:g>. Pretende conceder esta autorização? Em caso afirmativo, a sua resposta será memorizada e não lhe será solicitado que responda novamente."</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Recusar"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"Autorização Solicitada"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Acessibilidade"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Imagem de fundo"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Alterar imagem de fundo"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 72990a1..265fb62 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"Monitora o seu local físico."</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"Comunicação da rede"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"Permite que os aplicativos acessem diversos recursos de rede."</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"Suas Contas do Google"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"Acessar as Contas do Google disponíveis."</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"Controles de hardware"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"Acessa o hardware diretamente no aparelho."</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"Chamadas telefônicas"</string>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Digite o código PIN"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Código PIN incorreto!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, pressione Menu e, em seguida, 0."</string>
@@ -447,6 +553,8 @@
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Tente novamente"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Carregando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"Carregado."</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Conecte o seu carregador."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Sem cartão SIM."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"Não há um cartão SIM no telefone."</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"Carregando..."</string>
<string name="battery_low_title" msgid="7923774589611311406">"Conecte o carregador"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"A bateria está ficando baixa:"</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"menos de <xliff:g id="NUMBER">%d%%</xliff:g> restantes."</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"Uso da bateria"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Falha no teste de fábrica"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"A ação FACTORY_TEST é suportada apenas para pacotes instalados em /system/app."</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Deseja sair desta página?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Selecione OK para continuar ou Cancelar para permanecer na página atual."</string>
<string name="save_password_label" msgid="6860261758665825069">"Confirmar"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ler histórico e favoritos do Navegador"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite que o aplicativo leia todos os URLs visitados pelo Navegador e todos os favoritos do Navegador."</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"gravar histórico e favoritos do Navegador"</string>
@@ -585,7 +696,7 @@
<string name="weeks" msgid="6509623834583944518">"semanas"</string>
<string name="year" msgid="4001118221013892076">"ano"</string>
<string name="years" msgid="6881577717993213522">"anos"</string>
- <string name="every_weekday" msgid="8777593878457748503">"Todos os dias da semana (de segunda a sexta)"</string>
+ <string name="every_weekday" msgid="8777593878457748503">"De segunda a sexta-feira"</string>
<string name="daily" msgid="5738949095624133403">"Diariamente"</string>
<string name="weekly" msgid="983428358394268344">"Semanalmente na <xliff:g id="DAY">%s</xliff:g>"</string>
<string name="monthly" msgid="2667202947170988834">"Mensalmente"</string>
@@ -719,15 +830,13 @@
<string name="ime_action_go" msgid="8320845651737369027">"Ir"</string>
<string name="ime_action_search" msgid="658110271822807811">"Pesquisar"</string>
<string name="ime_action_send" msgid="2316166556349314424">"Enviar"</string>
- <string name="ime_action_next" msgid="3138843904009813834">"Próximo"</string>
+ <string name="ime_action_next" msgid="3138843904009813834">"Avançar"</string>
<string name="ime_action_done" msgid="8971516117910934605">"Concluído"</string>
<string name="ime_action_default" msgid="2840921885558045721">"Executar"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Discar número"\n"usando <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Criar contato "\n"usando <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"selecionado"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"não selecionado"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"Os aplicativos listados estão solicitando autorização para acessar as credenciais de login para a conta <xliff:g id="ACCOUNT">%1$s</xliff:g> de <xliff:g id="APPLICATION">%2$s</xliff:g>. Deseja conceder essa autorização? Em caso afirmativo, sua resposta será lembrada e essa pergunta não será feita novamente."</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"Os aplicativos listados estão solicitando autorização para acessar as credenciais de login <xliff:g id="TYPE">%1$s</xliff:g> para a conta <xliff:g id="ACCOUNT">%2$s</xliff:g> de <xliff:g id="APPLICATION">%3$s</xliff:g>. Deseja conceder essa autorização? Em caso afirmativo, sua resposta será lembrada e essa pergunta não será feita novamente."</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Negar"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"Autorização solicitada"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Acessibilidade"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Papel de parede"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Alterar papel de parede"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index a286312..373c45e 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"Отслеживание физического местоположения"</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"Сетевой обмен данными"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"Позволяет приложениям получать доступ к различным сетевым функциям."</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"Ваши аккаунты Google"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"Входить в доступные аккаунты Google."</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"Элементы управления аппаратным обеспечением"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"Прямой доступ к аппаратному обеспечению телефона."</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"Телефонные вызовы"</string>
@@ -388,7 +390,7 @@
<string name="permdesc_readDictionary" msgid="1082972603576360690">"Позволяет приложению считывать любые слова, имена и фразы личного пользования, которые могут храниться в пользовательском словаре."</string>
<string name="permlab_writeDictionary" msgid="6703109511836343341">"записывать в пользовательский словарь"</string>
<string name="permdesc_writeDictionary" msgid="2241256206524082880">"Позволяет приложению записывать новые слова в пользовательский словарь."</string>
- <string name="permlab_sdcardWrite" msgid="8079403759001777291">"изменить/удалить содержание SD-карты"</string>
+ <string name="permlab_sdcardWrite" msgid="8079403759001777291">"изменять/удалять содержание SD-карты"</string>
<string name="permdesc_sdcardWrite" msgid="6643963204976471878">"Разрешает приложению запись на SD-карту"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Домашний"</item>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Введите PIN-код"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Неверный PIN-код!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Для разблокировки нажмите \"Меню\", а затем 0."</string>
@@ -447,6 +553,8 @@
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Повторите попытку"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Идет зарядка (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"Заряжена."</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Подключите зарядное устройство."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Нет SIM-карты."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"SIM-карта не установлена."</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"Идет зарядка..."</string>
<string name="battery_low_title" msgid="7923774589611311406">"Подключите зарядное устройство"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Батарея разряжена:"</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"осталось менее <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"Расход заряда батареи"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Не удалось провести стандартный тест"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Действие FACTORY_TEST поддерживается только для пакетов, установленных в /system/app."</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Перейти с этой страницы?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Нажмите \"ОК\", чтобы продолжить, или \"Отмена\", чтобы остаться на текущей странице."</string>
<string name="save_password_label" msgid="6860261758665825069">"Подтвердите"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"считывать историю и закладки браузера"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Разрешает приложению считывать все URL, посещенные браузером, и все его закладки."</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"записывать историю и закладки браузера"</string>
@@ -726,8 +837,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"Создать контакт"\n"с номером <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"отмечено"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"не проверено"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"Перечисленные приложения запрашивают разрешение на доступ к регистрационном данным аккаунта <xliff:g id="ACCOUNT">%1$s</xliff:g> из <xliff:g id="APPLICATION">%2$s</xliff:g>. Разрешить доступ? Если да, ваш ответ будет сохранен и это сообщение больше не будет выводиться."</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"Перечисленные приложения запрашивают разрешение на доступ к регистрационном данным <xliff:g id="TYPE">%1$s</xliff:g> аккаунта <xliff:g id="ACCOUNT">%2$s</xliff:g> из <xliff:g id="APPLICATION">%3$s</xliff:g>. Разрешить доступ? Если да, ваш ответ будет сохранен и это сообщение больше не будет выводиться."</string>
<string name="allow" msgid="7225948811296386551">"Разрешить"</string>
<string name="deny" msgid="2081879885755434506">"Отклонить"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"Разрешение запрошено"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Специальные возможности"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Фоновый рисунок"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Изменить фоновый рисунок"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 0f0d966..db77ee1 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"Övervaka din fysiska plats"</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"Nätverkskommunikation"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"Tillåt att program kommer åt olika nätverksfunktioner."</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"Dina Google-konton"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"Få åtkomst till tillgängliga Google-konton."</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"Kontroller för maskinvara"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"Direkt åtkomst till maskinvara på handenheten."</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"Telefonsamtal"</string>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Ange PIN-kod"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Fel PIN-kod!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Tryck på Meny och sedan på 0 om du vill låsa upp."</string>
@@ -447,6 +553,8 @@
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Försök igen"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Laddar (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"Laddad."</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Anslut din laddare."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Inget SIM-kort."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"Inget SIM-kort i telefonen."</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"Laddar…"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Anslut laddaren"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Batteriet håller på att ta slut:"</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"Mindre än <xliff:g id="NUMBER">%d%%</xliff:g> återstår."</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"Batteriförbrukning"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Det gick fel vid fabrikstestet"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Åtgärden FACTORY_TEST stöds endast för paket som har installerats i /system/app."</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Vill du lämna den här den här sidan?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Tryck på OK om du vill fortsätta eller på Avbryt om du vill vara kvar på den aktuella sidan."</string>
<string name="save_password_label" msgid="6860261758665825069">"Bekräfta"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"läsa webbläsarhistorik och bokmärken"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Tillåter att program läser alla webbadresser som webbläsaren har öppnat och alla webbläsarens bokmärken."</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skriva webbläsarhistorik och bokmärken"</string>
@@ -726,8 +837,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"Skapa kontakt"\n"med <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"markerad"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"inte markerad"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"Programmen begär åtkomst till inloggningsuppgifterna för kontot <xliff:g id="ACCOUNT">%1$s</xliff:g> från <xliff:g id="APPLICATION">%2$s</xliff:g>. Vill du bevilja behörighet? Om du gör det kommer vi ihåg det och du blir inte tillfrågad igen."</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"Programmen begär åtkomst till inloggningsuppgifterna <xliff:g id="TYPE">%1$s</xliff:g> för kontot <xliff:g id="ACCOUNT">%2$s</xliff:g> från <xliff:g id="APPLICATION">%3$s</xliff:g>. Vill du bevilja behörighet? Om du gör det kommer vi ihåg det och du blir inte tillfrågad igen."</string>
<string name="allow" msgid="7225948811296386551">"Tillåt"</string>
<string name="deny" msgid="2081879885755434506">"Neka"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"Begärd behörighet"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Tillgänglighet"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Bakgrund"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Ändra bakgrund"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index a63a387..503ad7d 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"Fiziksel konumunuzu izleyin"</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"Ağ iletişimi"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"Uygulamaların çeşitli ağ özelliklerine erişmesine izin verir."</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"Google hesaplarınız"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"Kullanılabilir Google hesaplarına erişin."</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"Donanım denetimleri"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"Telefon donanımına doğrudan erişim."</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"Telefon çağrıları"</string>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN kodunu gir"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Yanlış PIN kodu!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Kilidi açmak için önce Menü\'ye, sonra 0\'a basın."</string>
@@ -447,6 +553,8 @@
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Üzgünüz, lütfen yeniden deneyin"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Şarj oluyor (<xliff:g id="PERCENT">%%</xliff:g><xliff:g id="NUMBER">%d</xliff:g>)"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"Şarj oldu."</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Şarj cihazınızı bağlayın."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"SIM kart yok."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"Telefonda SIM kart yok."</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"Şarj oluyor…"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Lütfen şarj cihazını takın"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Pil tükeniyor:"</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"<xliff:g id="NUMBER">%d%%</xliff:g> adetten daha az kaldı."</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"Pil kullanımı"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"Fabrika testi yapılamadı"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST işlemi yalnızca /system/app dizinine yüklenmiş paketler için desteklenir."</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Bu sayfadan ayrılıyor musunuz?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Devam etmek için Tamam\'ı, sayfada kalmak için İptal\'i tıklatın."</string>
<string name="save_password_label" msgid="6860261758665825069">"Onayla"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"Tarayıcı geçmişini ve favorileri oku"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Uygulamaya Tarayıcının ziyaret etmiş olduğu tüm URL\'leri ve Tarayıcının tüm favorilerini okuma izni verir."</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"Tarayıcı geçmişini ve favorileri yaz"</string>
@@ -726,8 +837,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"<xliff:g id="NUMBER">%s</xliff:g>"\n" ile kişi oluştur"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"seçildi"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"seçilmedi"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"Listelenen uygulamalar, <xliff:g id="APPLICATION">%2$s</xliff:g> uygulamasının <xliff:g id="ACCOUNT">%1$s</xliff:g> hesabı için giriş bilgilerine erişim izni istiyor. Bu izni vermek istiyor musunuz? İstiyorsanız yanıtınız kaydedilecek ve tekrar sorulmayacaktır."</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"Listelenen uygulamalar, <xliff:g id="APPLICATION">%3$s</xliff:g> uygulamasının <xliff:g id="ACCOUNT">%2$s</xliff:g> hesabı için <xliff:g id="TYPE">%1$s</xliff:g> girişi bilgilerine erişim izni istiyor. Bu izni vermek istiyor musunuz? İstiyorsanız yanıtınız kaydedilecek ve tekrar sorulmayacaktır."</string>
<string name="allow" msgid="7225948811296386551">"İzin Ver"</string>
<string name="deny" msgid="2081879885755434506">"Reddet"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"İzin İstendi"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Erişebilirlik"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Duvar Kağıdı"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Duvar kağıdını değiştir"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 2974203..474fffc 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -29,9 +29,9 @@
<string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"语音信箱"</string>
<string name="defaultMsisdnAlphaTag" msgid="2850889754919584674">"MSISDN1"</string>
<string name="mmiError" msgid="5154499457739052907">"出现连接问题或 MMI 码无效。"</string>
- <string name="serviceEnabled" msgid="8147278346414714315">"服务已启用。"</string>
+ <string name="serviceEnabled" msgid="8147278346414714315">"已启用服务。"</string>
<string name="serviceEnabledFor" msgid="6856228140453471041">"已针对以下内容启用了服务:"</string>
- <string name="serviceDisabled" msgid="1937553226592516411">"服务已被停用。"</string>
+ <string name="serviceDisabled" msgid="1937553226592516411">"已停用服务。"</string>
<string name="serviceRegistered" msgid="6275019082598102493">"注册成功。"</string>
<string name="serviceErased" msgid="1288584695297200972">"清除成功。"</string>
<string name="passwordIncorrect" msgid="7612208839450128715">"密码不正确。"</string>
@@ -42,25 +42,25 @@
<string name="invalidPin" msgid="3850018445187475377">"输入一个 4 至 8 位数的 PIN。"</string>
<string name="needPuk" msgid="919668385956251611">"您的 SIM 卡被 PUK 锁定。请输入 PUK 码进行解锁。"</string>
<string name="needPuk2" msgid="4526033371987193070">"输入 PUK2 以解锁 SIM 卡。"</string>
- <string name="ClipMmi" msgid="6952821216480289285">"来电显示"</string>
- <string name="ClirMmi" msgid="7784673673446833091">"外拨电话显示"</string>
+ <string name="ClipMmi" msgid="6952821216480289285">"来电者信息"</string>
+ <string name="ClirMmi" msgid="7784673673446833091">"外拨者信息"</string>
<string name="CfMmi" msgid="5123218989141573515">"呼叫转接"</string>
<string name="CwMmi" msgid="9129678056795016867">"呼叫等待"</string>
<string name="BaMmi" msgid="455193067926770581">"呼叫限制"</string>
<string name="PwdMmi" msgid="7043715687905254199">"密码更改"</string>
<string name="PinMmi" msgid="3113117780361190304">"PIN 码更改"</string>
<string name="CnipMmi" msgid="3110534680557857162">"来电显示"</string>
- <string name="CnirMmi" msgid="3062102121430548731">"来电显示已禁用"</string>
+ <string name="CnirMmi" msgid="3062102121430548731">"来电显示受限制"</string>
<string name="ThreeWCMmi" msgid="9051047170321190368">"三方通话"</string>
<string name="RuacMmi" msgid="7827887459138308886">"拒绝不想接听的骚扰电话"</string>
<string name="CndMmi" msgid="3116446237081575808">"主叫号码传送"</string>
<string name="DndMmi" msgid="1265478932418334331">"请勿打扰"</string>
- <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"来电显示默认设置为受限制。下一个呼叫:受限制"</string>
- <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"来电显示默认设置为受限制。下一个呼叫:不受限制"</string>
- <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"来电显示默认设置为不受限制。下一个呼叫:受限制"</string>
- <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"来电显示默认设置为不受限制。下一个呼叫:不受限制"</string>
+ <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"来电者信息在默认情况下受限制。在下一次通话中:受限制"</string>
+ <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"来电者信息在默认情况受限制。在下一次通话中:不受限制"</string>
+ <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"来电者信息在默认情况下不受限制。在下一次通话中:受限制"</string>
+ <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"来电者信息在默认情况下不受限制。在下一次通话中:不受限制"</string>
<string name="serviceNotProvisioned" msgid="8614830180508686666">"未提供服务。"</string>
- <string name="CLIRPermanent" msgid="5460892159398802465">"无法更改来电显示设置。"</string>
+ <string name="CLIRPermanent" msgid="5460892159398802465">"无法更改来电者信息设置。"</string>
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"访问受限情况已发生变化"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"数据服务已禁用。"</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"紧急服务已禁用。"</string>
@@ -116,7 +116,7 @@
<string name="contentServiceSync" msgid="8353523060269335667">"同步"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"同步"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"太多<xliff:g id="CONTENT_TYPE">%s</xliff:g>删除项。"</string>
- <string name="low_memory" msgid="6632412458436461203">"手机存储空间已满!请删除一些文件来腾出空间。"</string>
+ <string name="low_memory" msgid="6632412458436461203">"手机内存已用完!请删除一些文件来腾出空间。"</string>
<string name="me" msgid="6545696007631404292">"我"</string>
<string name="power_dialog" msgid="1319919075463988638">"手机选项"</string>
<string name="silent_mode" msgid="7167703389802618663">"静音模式"</string>
@@ -131,8 +131,8 @@
<string name="global_action_lock" msgid="2844945191792119712">"屏幕锁定"</string>
<string name="global_action_power_off" msgid="4471879440839879722">"关机"</string>
<string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"静音模式"</string>
- <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"声音已“关闭”"</string>
- <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"声音已“开启”"</string>
+ <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"已关闭声音"</string>
+ <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"已打开声音"</string>
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"飞行模式"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"飞行模式已开启"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"飞行模式已关闭"</string>
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"监视您的物理位置"</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"网络通信"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"允许应用程序访问各种网络功能。"</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"您的 Google 帐户"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"访问可用的 Google 帐户。"</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"硬件控制"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"直接访问手机上的硬件。"</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"手机通话"</string>
@@ -164,8 +166,8 @@
<string name="permdesc_statusBar" msgid="1365473595331989732">"允许应用程序停用状态栏,或者添加和删除系统图标。"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"展开/折叠状态栏"</string>
<string name="permdesc_expandStatusBar" msgid="7088604400110768665">"允许应用程序展开或折叠状态栏。"</string>
- <string name="permlab_processOutgoingCalls" msgid="1136262550878335980">"拦截对外呼叫"</string>
- <string name="permdesc_processOutgoingCalls" msgid="2228988201852654461">"允许应用程序处理对外呼叫和更改要拨打的号码。恶意应用程序可借此监视、重定向或阻止对外呼叫。"</string>
+ <string name="permlab_processOutgoingCalls" msgid="1136262550878335980">"拦截外拨电话"</string>
+ <string name="permdesc_processOutgoingCalls" msgid="2228988201852654461">"允许应用程序处理外拨电话和更改要拨打的号码。恶意应用程序可借此监视、重定向或阻止外拨电话。"</string>
<string name="permlab_receiveSms" msgid="2697628268086208535">"接收短信"</string>
<string name="permdesc_receiveSms" msgid="6298292335965966117">"允许应用程序接收和处理短信。恶意应用程序可借此监视您的信息,或者将信息删除而不向您显示。"</string>
<string name="permlab_receiveMms" msgid="8894700916188083287">"接收彩信"</string>
@@ -178,7 +180,7 @@
<string name="permdesc_writeSms" msgid="6299398896177548095">"允许应用程序写入手机或 SIM 卡中存储的短信。恶意应用程序可借此删除您的信息。"</string>
<string name="permlab_receiveWapPush" msgid="8258226427716551388">"接收 WAP"</string>
<string name="permdesc_receiveWapPush" msgid="5979623826128082171">"允许应用程序接收和处理 WAP 消息。恶意应用程序可借此监视您的消息,或者将消息删除而不向您显示。"</string>
- <string name="permlab_getTasks" msgid="5005277531132573353">"检索所运行的应用程序"</string>
+ <string name="permlab_getTasks" msgid="5005277531132573353">"检索当前运行的应用程序"</string>
<string name="permdesc_getTasks" msgid="7048711358713443341">"允许应用程序检索有关当前和最近运行的任务的信息。恶意应用程序可借此发现有关其他应用程序的私有信息。"</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"对正在运行的应用程序重新排序"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"允许应用程序将任务移至前台和后台。恶意应用程序可借此强行进到前台,而不受您的控制。"</string>
@@ -204,11 +206,11 @@
<string name="permdesc_broadcastSmsReceived" msgid="9122419277306740155">"允许应用程序广播已收到短信的通知。恶意应用程序可借此伪造收到的短信。"</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"发送 WAP 一键接收广播"</string>
<string name="permdesc_broadcastWapPush" msgid="3955303669461378091">"允许应用程序广播收到 WAP 一键信息的通知。恶意应用程序可借此乱发彩信或暗中用恶意内容替换任意网页中的内容。"</string>
- <string name="permlab_setProcessLimit" msgid="2451873664363662666">"限制所运行进程的数量"</string>
+ <string name="permlab_setProcessLimit" msgid="2451873664363662666">"限制运行的进程个数"</string>
<string name="permdesc_setProcessLimit" msgid="7824786028557379539">"允许应用程序控制将运行的最大进程数。普通应用程序从不需要使用此权限。"</string>
<string name="permlab_setAlwaysFinish" msgid="5342837862439543783">"关闭所有后台应用程序"</string>
<string name="permdesc_setAlwaysFinish" msgid="8773936403987091620">"允许应用程序控制活动是否始终是一转至后台就完成。普通应用程序从不需要使用此权限。"</string>
- <string name="permlab_batteryStats" msgid="7863923071360031652">"修改电池使用情况统计信息"</string>
+ <string name="permlab_batteryStats" msgid="7863923071360031652">"修改电池统计信息"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"允许修改收集的电池使用情况统计信息。普通应用程序不能使用此权限。"</string>
<string name="permlab_backup" msgid="470013022865453920">"控制系统备份和还原"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"允许应用程序控制系统的备份和还原机制。不适用于普通应用程序。"</string>
@@ -221,7 +223,7 @@
<string name="permlab_setAnimationScale" msgid="2805103241153907174">"修改全局动画速度"</string>
<string name="permdesc_setAnimationScale" msgid="7181522138912391988">"允许应用程序随时更改全局动画速度(加快或放慢动画)。"</string>
<string name="permlab_manageAppTokens" msgid="17124341698093865">"管理应用程序令牌"</string>
- <string name="permdesc_manageAppTokens" msgid="977127907524195988">"允许应用程序创建和管理自己的令牌,从而绕开正常的 Z 排序方式。普通应用程序从不需要使用此权限。"</string>
+ <string name="permdesc_manageAppTokens" msgid="977127907524195988">"允许应用程序创建和管理自己的令牌,从而绕开正常的 Z 方向排序。普通应用程序从不需要使用此权限。"</string>
<string name="permlab_injectEvents" msgid="1378746584023586600">"按键和控制按钮"</string>
<string name="permdesc_injectEvents" msgid="3946098050410874715">"允许应用程序将其自己的输入活动(按键等)提供给其他应用程序。恶意应用程序可借此掌控手机。"</string>
<string name="permlab_readInputState" msgid="469428900041249234">"记录您输入的内容和采取的操作"</string>
@@ -230,7 +232,7 @@
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"允许手机用户绑定至输入法的顶级界面。普通应用程序从不需要使用此权限。"</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"绑定到壁纸"</string>
<string name="permdesc_bindWallpaper" msgid="5287754520361915347">"允许手机用户绑定到壁纸的顶级界面。应该从不需要将此权限授予普通应用程序。"</string>
- <string name="permlab_setOrientation" msgid="3365947717163866844">"更改屏幕浏览模式"</string>
+ <string name="permlab_setOrientation" msgid="3365947717163866844">"更改屏幕显示方向"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"允许应用程序随时更改屏幕的旋转方向。普通应用程序从不需要使用此权限。"</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"向应用程序发送 Linux 信号"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"允许应用程序请求将提供的信号发送给所有持续的进程。"</string>
@@ -247,7 +249,7 @@
<string name="permlab_installPackages" msgid="335800214119051089">"直接安装应用程序"</string>
<string name="permdesc_installPackages" msgid="526669220850066132">"允许应用程序安装新的或更新的 Android 包。恶意应用程序可借此添加具有极大权限的新应用程序。"</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"删除所有应用程序缓存数据"</string>
- <string name="permdesc_clearAppCache" msgid="7740465694193671402">"允许应用程序通过删除应用程序缓存目录中的文件释放手机存储空间。对系统进程的访问通常受到严格限制。"</string>
+ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"允许应用程序通过删除应用程序缓存目录中的文件释放手机内存。通常严格限制访问系统进程。"</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"读取系统日志文件"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"允许应用程序读取系统的各日志文件。这样应用程序可以发现有关您操作手机的一般信息,但这些信息不应包含任何个人信息或私有信息。"</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"读取/写入诊断所拥有的资源"</string>
@@ -264,7 +266,7 @@
<string name="permdesc_writeGservices" msgid="6602362746516676175">"允许应用程序修改 Google 服务地图。普通应用程序不能使用此权限。"</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"开机时自动启动"</string>
<string name="permdesc_receiveBootCompleted" msgid="698336728415008796">"允许应用程序在系统完成启动后即自行启动。这样会延长手机的启动时间,而且如果应用程序一直运行,会降低手机的整体速度。"</string>
- <string name="permlab_broadcastSticky" msgid="7919126372606881614">"发送顽固广播"</string>
+ <string name="permlab_broadcastSticky" msgid="7919126372606881614">"发送置顶广播"</string>
<string name="permdesc_broadcastSticky" msgid="1920045289234052219">"允许应用程序发送顽固广播,这些广播在结束后仍会保留。恶意应用程序可能会借此使手机耗用太多内存,从而降低其速度或稳定性。"</string>
<string name="permlab_readContacts" msgid="6219652189510218240">"读取联系人数据"</string>
<string name="permdesc_readContacts" msgid="3371591512896545975">"允许应用程序读取您手机上存储的所有联系人(地址)数据。恶意应用程序可借此将您的数据发送给其他人。"</string>
@@ -278,11 +280,11 @@
<string name="permdesc_readCalendar" msgid="5533029139652095734">"允许应用程序读取您手机上存储的所有日历活动。恶意应用程序可借此将您的日历活动发送给其他人。"</string>
<string name="permlab_writeCalendar" msgid="377926474603567214">"写入日历数据"</string>
<string name="permdesc_writeCalendar" msgid="8674240662630003173">"允许应用程序修改您手机上存储的日历活动。恶意应用程序可借此清除或修改您的日历数据。"</string>
- <string name="permlab_accessMockLocation" msgid="8688334974036823330">"用于测试的模拟位置源"</string>
+ <string name="permlab_accessMockLocation" msgid="8688334974036823330">"禁止使用位置来源进行测试"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"创建用于测试的模拟位置源。恶意应用程序可借此替代真正的位置源(如 GPS 或网络提供商)返回的位置和/或状态。"</string>
- <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"接收额外的位置提供者命令"</string>
+ <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"访问更多位置提供者的命令"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="1948144701382451721">"访问额外的位置提供程序命令。恶意应用程序可借此干扰 GPS 或其他位置源的运作。"</string>
- <string name="permlab_installLocationProvider" msgid="6578101199825193873">"安装地点信息提供程序的权限"</string>
+ <string name="permlab_installLocationProvider" msgid="6578101199825193873">"安装位置信息提供程序"</string>
<string name="permdesc_installLocationProvider" msgid="5449175116732002106">"创建用于测试的模拟地点信息源。恶意应用程序可借此覆盖真正的地点信息源(如 GPS 或网络提供商)返回的地点和/或状态,或者监视您的地点并向外部信息源报告。"</string>
<string name="permlab_accessFineLocation" msgid="8116127007541369477">"精准位置 (GPS)"</string>
<string name="permdesc_accessFineLocation" msgid="7411213317434337331">"访问精准的位置源,例如手机上的全球定位系统(如果适用)。恶意应用程序可能借此确定您所处的位置,并消耗额外的电池电量。"</string>
@@ -313,9 +315,9 @@
<string name="permlab_hardware_test" msgid="4148290860400659146">"测试硬件"</string>
<string name="permdesc_hardware_test" msgid="3668894686500081699">"允许应用程序控制各种用于硬件测试的外围设备。"</string>
<string name="permlab_callPhone" msgid="3925836347681847954">"直接拨打电话号码"</string>
- <string name="permdesc_callPhone" msgid="3369867353692722456">"允许应用程序在没有您干预的情况下呼叫电话号码。恶意应用程序可借此在您的话费单上产生意外通话费。请注意,此权限不允许应用程序呼叫紧急电话号码。"</string>
+ <string name="permdesc_callPhone" msgid="3369867353692722456">"允许应用程序在无人操作的情况下拨打电话。恶意应用程序可借此在您的话费单上产生意外通话费。请注意,此权限不允许应用程序拨打紧急电话。"</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"直接呼叫任何电话号码"</string>
- <string name="permdesc_callPrivileged" msgid="244405067160028452">"允许应用程序在没有您干预的情况下呼叫任何电话号码(包括紧急电话号码)。恶意应用程序可借此对紧急服务拨打骚扰电话和非法电话。"</string>
+ <string name="permdesc_callPrivileged" msgid="244405067160028452">"允许应用程序在无人操作的情况下拨打任何电话(包括紧急电话)。恶意应用程序可借此向紧急服务拨打骚扰电话和非法电话。"</string>
<string name="permlab_performCdmaProvisioning" msgid="5604848095315421425">"直接启动 CDMA 电话设置"</string>
<string name="permdesc_performCdmaProvisioning" msgid="6457447676108355905">"允许应用程序启动 CDMA 服务。恶意应用程序可能会无端启动 CDMA 服务"</string>
<string name="permlab_locationUpdates" msgid="7785408253364335740">"控制位置更新通知"</string>
@@ -354,7 +356,7 @@
<string name="permdesc_useCredentials" msgid="7416570544619546974">"允许应用程序请求身份验证标记。"</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"查看网络状态"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"允许应用程序查看所有网络的状态。"</string>
- <string name="permlab_createNetworkSockets" msgid="9121633680349549585">"互联网完全访问"</string>
+ <string name="permlab_createNetworkSockets" msgid="9121633680349549585">"完全访问互联网"</string>
<string name="permdesc_createNetworkSockets" msgid="4593339106921772192">"允许应用程序创建网络套接字。"</string>
<string name="permlab_writeApnSettings" msgid="7823599210086622545">"写入接入点名称设置"</string>
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"允许应用程序修改 APN 设置,例如任何 APN 的代理和端口。"</string>
@@ -407,10 +409,10 @@
<item msgid="2374913952870110618">"自定义邮箱"</item>
</string-array>
<string-array name="postalAddressTypes">
- <item msgid="6880257626740047286">"住宅地址"</item>
- <item msgid="5629153956045109251">"单位地址"</item>
- <item msgid="4966604264500343469">"其他地址"</item>
- <item msgid="4932682847595299369">"自定义地址"</item>
+ <item msgid="6880257626740047286">"住宅"</item>
+ <item msgid="5629153956045109251">"单位"</item>
+ <item msgid="4966604264500343469">"其他"</item>
+ <item msgid="4932682847595299369">"自定义"</item>
</string-array>
<string-array name="imAddressTypes">
<item msgid="1738585194601476694">"住宅聊天工具"</item>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"输入 PIN 码"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"PIN 码不正确!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"要解锁,请先按 MENU 再按 0。"</string>
@@ -446,15 +552,17 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"正确!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"很抱歉,请重试"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"正在充电 (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
- <string name="lockscreen_charged" msgid="4938930459620989972">"电已充满。"</string>
+ <string name="lockscreen_charged" msgid="4938930459620989972">"已充满。"</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"连接您的充电器。"</string>
- <string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"没有 SIM 卡。"</string>
- <string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"手机中无 SIM 卡。"</string>
- <string name="lockscreen_missing_sim_instructions" msgid="8874620818937719067">"请插入 SIM 卡。"</string>
+ <string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"没有 SIM 卡"</string>
+ <string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"手机中无 SIM 卡"</string>
+ <string name="lockscreen_missing_sim_instructions" msgid="8874620818937719067">"请插入 SIM 卡"</string>
<string name="lockscreen_network_locked_message" msgid="143389224986028501">"网络已锁定"</string>
- <string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"SIM 卡被 PUK 锁定。"</string>
+ <string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"SIM 卡被 PUK 锁定"</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="635967534992394321">"请参阅《用户指南》或联系客服人员。"</string>
- <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM 卡被锁定。"</string>
+ <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM 卡被锁定"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"正在解锁 SIM 卡..."</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地绘制了解锁图案。"\n\n"请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string>
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地绘制了解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统会要求您使用自己的 Google 登录信息解锁手机。"\n\n"请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"正在充电..."</string>
<string name="battery_low_title" msgid="7923774589611311406">"请连接充电器"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"电量在减少:"</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"剩余电量不足 <xliff:g id="NUMBER">%d%%</xliff:g>。"</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"电量使用情况"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"出厂测试失败"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"只有 /system/app 中安装的包支持 FACTORY_TEST 操作。"</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"是否从该页面导航至它处?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"选择“确定”继续,或选择“取消”留在当前页面。"</string>
<string name="save_password_label" msgid="6860261758665825069">"确认"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"读取浏览器的历史记录和书签"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"允许应用程序读取用浏览器访问过的所有网址,以及浏览器的所有书签。"</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"写入浏览器的历史记录和书签"</string>
@@ -614,16 +725,16 @@
<string name="addToDictionary" msgid="8793624991686948709">"将“<xliff:g id="WORD">%s</xliff:g>”添加到词典"</string>
<string name="editTextMenuTitle" msgid="1672989176958581452">"编辑文字"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"空间不足"</string>
- <string name="low_internal_storage_view_text" msgid="635106544616378836">"手机存储空间正在减少。"</string>
+ <string name="low_internal_storage_view_text" msgid="635106544616378836">"手机内存空间正在减少。"</string>
<string name="ok" msgid="5970060430562524910">"确定"</string>
<string name="cancel" msgid="6442560571259935130">"取消"</string>
<string name="yes" msgid="5362982303337969312">"确定"</string>
<string name="no" msgid="5141531044935541497">"取消"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"注意事项"</string>
- <string name="capital_on" msgid="1544682755514494298">"开启"</string>
+ <string name="capital_on" msgid="1544682755514494298">"打开"</string>
<string name="capital_off" msgid="6815870386972805832">"关闭"</string>
- <string name="whichApplication" msgid="4533185947064773386">"使用以下内容完成操作"</string>
- <string name="alwaysUse" msgid="4583018368000610438">"默认用于执行此操作。"</string>
+ <string name="whichApplication" msgid="4533185947064773386">"使用以下方式发送"</string>
+ <string name="alwaysUse" msgid="4583018368000610438">"默认使用此方式发送。"</string>
<string name="clearDefaultHintMsg" msgid="4815455344600932173">"清除“主屏幕设置”>“应用程序”>“管理应用程序”中的默认设置。"</string>
<string name="chooseActivity" msgid="1009246475582238425">"选择一项操作"</string>
<string name="noApplications" msgid="1691104391758345586">"没有应用程序可执行此操作。"</string>
@@ -680,7 +791,7 @@
<string name="usb_storage_button_unmount" msgid="6092146330053864766">"不安装"</string>
<string name="usb_storage_error_message" msgid="2534784751603345363">"使用 SD 卡进行 USB 存储时出现问题。"</string>
<string name="usb_storage_notification_title" msgid="8175892554757216525">"USB 已连接"</string>
- <string name="usb_storage_notification_message" msgid="7380082404288219341">"选择以将文件复制到计算机/从计算机复制文件。"</string>
+ <string name="usb_storage_notification_message" msgid="7380082404288219341">"选择将文件复制到计算机/从计算机复制到存储设备。"</string>
<string name="usb_storage_stop_notification_title" msgid="2336058396663516017">"关闭 USB 存储设备"</string>
<string name="usb_storage_stop_notification_message" msgid="2591813490269841539">"选中以关闭 USB 存储设备。"</string>
<string name="usb_storage_stop_title" msgid="6014127947456185321">"关闭 USB 存储设备"</string>
@@ -691,7 +802,7 @@
<string name="extmedia_format_title" msgid="8663247929551095854">"格式化 SD 卡"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"您确定要格式化 SD 卡?卡上的所有数据都会丢失。"</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"格式化"</string>
- <string name="adb_active_notification_title" msgid="6729044778949189918">"已连接 USB 调试接口"</string>
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"已连接 USB 调试"</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
<skip />
<!-- no translation found for select_input_method (6865512749462072765) -->
@@ -726,8 +837,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"创建电话号码为"\n"<xliff:g id="NUMBER">%s</xliff:g> 的联系人"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"已选中"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"未选中"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"所列应用程序正在请求相应的权限,以便从<xliff:g id="APPLICATION">%2$s</xliff:g>访问 <xliff:g id="ACCOUNT">%1$s</xliff:g> 帐户的登录凭据。是否要授予这种权限?如果授予,系统会记住您所做的选择,且不再提示。"</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"所列应用程序正在请求相应的权限,以便从<xliff:g id="APPLICATION">%3$s</xliff:g>访问 <xliff:g id="ACCOUNT">%2$s</xliff:g> 帐户的<xliff:g id="TYPE">%1$s</xliff:g>登录凭据。是否要授予这种权限?如果授予,系统会记住您所做的选择,且不再提示。"</string>
<string name="allow" msgid="7225948811296386551">"允许"</string>
<string name="deny" msgid="2081879885755434506">"拒绝"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"已请求权限"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"辅助功能"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"壁纸"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"更改壁纸"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 8f9f310..3542536 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -148,8 +148,10 @@
<string name="permgroupdesc_location" msgid="2430258821648348660">"監視實際位置"</string>
<string name="permgrouplab_network" msgid="5808983377727109831">"網路通訊"</string>
<string name="permgroupdesc_network" msgid="5035763698958415998">"允許應用程式存取多項網路功能。"</string>
- <string name="permgrouplab_accounts" msgid="7140261692496314430">"您的 Google 帳戶"</string>
- <string name="permgroupdesc_accounts" msgid="6735915929704895193">"存取可用 Google 帳戶。"</string>
+ <!-- no translation found for permgrouplab_accounts (3359646291125325519) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_accounts (4948732641827091312) -->
+ <skip />
<string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"硬體控制"</string>
<string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"在免持設備上直接存取硬體。"</string>
<string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"撥打電話"</string>
@@ -433,6 +435,110 @@
<item msgid="2506857312718630823">"ICQ"</item>
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
+ <!-- no translation found for phoneTypeCustom (1644738059053355820) -->
+ <skip />
+ <!-- no translation found for phoneTypeHome (2570923463033985887) -->
+ <skip />
+ <!-- no translation found for phoneTypeMobile (6501463557754751037) -->
+ <skip />
+ <!-- no translation found for phoneTypeWork (8863939667059911633) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxWork (3517792160008890912) -->
+ <skip />
+ <!-- no translation found for phoneTypeFaxHome (2067265972322971467) -->
+ <skip />
+ <!-- no translation found for phoneTypePager (7582359955394921732) -->
+ <skip />
+ <!-- no translation found for phoneTypeOther (1544425847868765990) -->
+ <skip />
+ <!-- no translation found for phoneTypeCallback (2712175203065678206) -->
+ <skip />
+ <!-- no translation found for phoneTypeCar (8738360689616716982) -->
+ <skip />
+ <!-- no translation found for phoneTypeCompanyMain (540434356461478916) -->
+ <skip />
+ <!-- no translation found for phoneTypeIsdn (8022453193171370337) -->
+ <skip />
+ <!-- no translation found for phoneTypeMain (6766137010628326916) -->
+ <skip />
+ <!-- no translation found for phoneTypeOtherFax (8587657145072446565) -->
+ <skip />
+ <!-- no translation found for phoneTypeRadio (4093738079908667513) -->
+ <skip />
+ <!-- no translation found for phoneTypeTelex (3367879952476250512) -->
+ <skip />
+ <!-- no translation found for phoneTypeTtyTdd (8606514378585000044) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkMobile (1311426989184065709) -->
+ <skip />
+ <!-- no translation found for phoneTypeWorkPager (649938731231157056) -->
+ <skip />
+ <!-- no translation found for phoneTypeAssistant (5596772636128562884) -->
+ <skip />
+ <!-- no translation found for phoneTypeMms (7254492275502768992) -->
+ <skip />
+ <!-- no translation found for eventTypeBirthday (2813379844211390740) -->
+ <skip />
+ <!-- no translation found for eventTypeAnniversary (3876779744518284000) -->
+ <skip />
+ <!-- no translation found for eventTypeOther (5834288791948564594) -->
+ <skip />
+ <!-- no translation found for emailTypeCustom (8525960257804213846) -->
+ <skip />
+ <!-- no translation found for emailTypeHome (449227236140433919) -->
+ <skip />
+ <!-- no translation found for emailTypeWork (3548058059601149973) -->
+ <skip />
+ <!-- no translation found for emailTypeOther (2923008695272639549) -->
+ <skip />
+ <!-- no translation found for emailTypeMobile (119919005321166205) -->
+ <skip />
+ <!-- no translation found for postalTypeCustom (8903206903060479902) -->
+ <skip />
+ <!-- no translation found for postalTypeHome (8165756977184483097) -->
+ <skip />
+ <!-- no translation found for postalTypeWork (5268172772387694495) -->
+ <skip />
+ <!-- no translation found for postalTypeOther (2726111966623584341) -->
+ <skip />
+ <!-- no translation found for imTypeCustom (2074028755527826046) -->
+ <skip />
+ <!-- no translation found for imTypeHome (6241181032954263892) -->
+ <skip />
+ <!-- no translation found for imTypeWork (1371489290242433090) -->
+ <skip />
+ <!-- no translation found for imTypeOther (5377007495735915478) -->
+ <skip />
+ <!-- no translation found for imProtocolCustom (6919453836618749992) -->
+ <skip />
+ <!-- no translation found for imProtocolAim (7050360612368383417) -->
+ <skip />
+ <!-- no translation found for imProtocolMsn (144556545420769442) -->
+ <skip />
+ <!-- no translation found for imProtocolYahoo (8271439408469021273) -->
+ <skip />
+ <!-- no translation found for imProtocolSkype (9019296744622832951) -->
+ <skip />
+ <!-- no translation found for imProtocolQq (8887484379494111884) -->
+ <skip />
+ <!-- no translation found for imProtocolGoogleTalk (3808393979157698766) -->
+ <skip />
+ <!-- no translation found for imProtocolIcq (1574870433606517315) -->
+ <skip />
+ <!-- no translation found for imProtocolJabber (2279917630875771722) -->
+ <skip />
+ <!-- no translation found for imProtocolNetMeeting (8287625655986827971) -->
+ <skip />
+ <!-- no translation found for orgTypeWork (29268870505363872) -->
+ <skip />
+ <!-- no translation found for orgTypeOther (3951781131570124082) -->
+ <skip />
+ <!-- no translation found for orgTypeCustom (225523415372088322) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution (5112589886094402795) -->
+ <skip />
+ <!-- no translation found for contact_status_update_attribution_with_date (5945386376369979909) -->
+ <skip />
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"輸入 PIN 碼"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"PIN 碼錯誤!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"如要解鎖,請按 Menu 鍵,然後按 0。"</string>
@@ -447,6 +553,8 @@
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"很抱歉,請再試一次"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"正在充電 (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"充電完成。"</string>
+ <!-- no translation found for lockscreen_battery_short (3617549178603354656) -->
+ <skip />
<string name="lockscreen_low_battery" msgid="1482873981919249740">"請連接充電器。"</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"沒有 SIM 卡。"</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"手機未插入 SIM 卡。"</string>
@@ -478,7 +586,8 @@
<string name="battery_status_charging" msgid="756617993998772213">"充電中"</string>
<string name="battery_low_title" msgid="7923774589611311406">"請連接充電器"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"電池電量即將不足:"</string>
- <string name="battery_low_percent_format" msgid="6564958083485073855">"電池電量不到 <xliff:g id="NUMBER">%d%%</xliff:g>。"</string>
+ <!-- no translation found for battery_low_percent_format (696154104579022959) -->
+ <skip />
<string name="battery_low_why" msgid="7279169609518386372">"電池使用狀況"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"出廠測試失敗"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"只有安裝在 /system/app 裡的程式才能支援 FACTORY_TEST 操作。"</string>
@@ -488,6 +597,8 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"離開此頁?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n" 選取 [確定] 離開此頁;或 [取消] 留在此頁。"</string>
<string name="save_password_label" msgid="6860261758665825069">"確認"</string>
+ <!-- no translation found for double_tap_toast (1068216937244567247) -->
+ <skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"讀取瀏覽器的記錄與書籤"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"允許應用程式讀取瀏覽器曾經造訪過的所有網址,以及瀏覽器的所有書籤。"</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"寫入瀏覽器的記錄與書籤"</string>
@@ -726,8 +837,6 @@
<string name="create_contact_using" msgid="4947405226788104538">"建立手機號碼為 <xliff:g id="NUMBER">%s</xliff:g>"\n"的聯絡人"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"已勾選"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"未勾選"</string>
- <string name="grant_credentials_permission_message_desc" msgid="6883276587034335667">"這些應用程式要求存取「<xliff:g id="APPLICATION">%2$s</xliff:g>」的 <xliff:g id="ACCOUNT">%1$s</xliff:g> 帳戶登入認證的權限,您要授予此權限嗎?如果確定授予,系統會記住您的選擇,不會再次詢問您。"</string>
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc" msgid="3159007601893584687">"這些應用程式要求存取「<xliff:g id="APPLICATION">%3$s</xliff:g>」的 <xliff:g id="ACCOUNT">%2$s</xliff:g> 帳戶「<xliff:g id="TYPE">%1$s</xliff:g>」登入認證的權限,您要授予此權限嗎?如果確定授予,系統會記住您的選擇,不會再次詢問您。"</string>
<string name="allow" msgid="7225948811296386551">"允許"</string>
<string name="deny" msgid="2081879885755434506">"拒絕"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"已要求權限"</string>
@@ -738,4 +847,12 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"協助工具"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"桌布"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"變更桌布"</string>
+ <!-- no translation found for pptp_vpn_description (2688045385181439401) -->
+ <skip />
+ <!-- no translation found for l2tp_vpn_description (3750692169378923304) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_psk_vpn_description (3945043564008303239) -->
+ <skip />
+ <!-- no translation found for l2tp_ipsec_crt_vpn_description (5382714073103653577) -->
+ <skip />
</resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 5d3069b..a506c9a 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -78,6 +78,7 @@
<!-- For security permissions -->
<color name="perms_dangerous_grp_color">#dd6826</color>
<color name="perms_dangerous_perm_color">#dd6826</color>
+ <color name="shadow">#cc222222</color>
<!-- For search-related UIs -->
<color name="search_url_text_normal">#7fa87f</color>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index de30fe7..83a6dc5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2067,16 +2067,9 @@
<!-- Title for the unselected state of a CompoundButton. -->
<string name="accessibility_compound_button_unselected">not checked</string>
- <string name="grant_credentials_permission_message_desc">The
- listed applications are requesting permission to access the login credentials for account <xliff:g id="account" example="foo@gmail.com">%1$s</xliff:g> from
- <xliff:g id="application" example="Google Apps">%2$s</xliff:g>. Do you wish to grant this permission? If so, your answer will be remembered and you will not be prompted
- again.</string>
-
- <string name="grant_credentials_permission_message_with_authtokenlabel_desc">The
- listed applications are requesting permission to access the <xliff:g id="type" example="Contacts">%1$s</xliff:g> login credentials for account <xliff:g id="account" example="foo@gmail.com">%2$s</xliff:g> from
- <xliff:g id="application" example="Google Apps">%3$s</xliff:g>. Do you wish to grant this permission? If so, your answer will be remembered and you will not be prompted
- again.</string>
-
+ <string name="grant_credentials_permission_message_header">The following one or more applications request permission to access your account, now and in the future.</string>
+ <string name="grant_credentials_permission_message_footer">Do you want to allow this request?</string>
+ <string name="grant_permissions_header_text">Access Request</string>
<string name="allow">Allow</string>
<string name="deny">Deny</string>
<string name="permission_request_notification_title">Permission Requested</string>
@@ -2100,4 +2093,6 @@
<string name="l2tp_vpn_description">Layer 2 Tunneling Protocol</string>
<string name="l2tp_ipsec_psk_vpn_description">Pre-shared key based L2TP/IPSec VPN</string>
<string name="l2tp_ipsec_crt_vpn_description">Certificate based L2TP/IPSec VPN</string>
+ <!-- Label for button in a WebView that will open a chooser to choose a file to upload -->
+ <string name="upload_file">Choose file</string>
</resources>
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index 303444c..d5c1594 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -42,7 +42,7 @@
virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length) = 0;
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
- virtual sp<IOMX> createOMX() = 0;
+ virtual sp<IOMX> getOMX() = 0;
// Take a peek at currently playing audio, for visualization purposes.
// This returns a buffer of 16 bit mono PCM data, or NULL if no visualization buffer is currently available.
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index 10e0197..6f3ba1c 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -42,57 +42,57 @@
typedef void *buffer_id;
typedef void *node_id;
- virtual status_t list_nodes(List<String8> *list) = 0;
+ virtual status_t listNodes(List<String8> *list) = 0;
- virtual status_t allocate_node(const char *name, node_id *node) = 0;
- virtual status_t free_node(node_id node) = 0;
+ virtual status_t allocateNode(
+ const char *name, const sp<IOMXObserver> &observer,
+ node_id *node) = 0;
- virtual status_t send_command(
+ virtual status_t freeNode(node_id node) = 0;
+
+ virtual status_t sendCommand(
node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) = 0;
- virtual status_t get_parameter(
+ virtual status_t getParameter(
node_id node, OMX_INDEXTYPE index,
void *params, size_t size) = 0;
- virtual status_t set_parameter(
+ virtual status_t setParameter(
node_id node, OMX_INDEXTYPE index,
const void *params, size_t size) = 0;
- virtual status_t get_config(
+ virtual status_t getConfig(
node_id node, OMX_INDEXTYPE index,
void *params, size_t size) = 0;
- virtual status_t set_config(
+ virtual status_t setConfig(
node_id node, OMX_INDEXTYPE index,
const void *params, size_t size) = 0;
- virtual status_t use_buffer(
+ virtual status_t useBuffer(
node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
buffer_id *buffer) = 0;
- virtual status_t allocate_buffer(
+ virtual status_t allocateBuffer(
node_id node, OMX_U32 port_index, size_t size,
buffer_id *buffer) = 0;
- virtual status_t allocate_buffer_with_backup(
+ virtual status_t allocateBufferWithBackup(
node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
buffer_id *buffer) = 0;
- virtual status_t free_buffer(
+ virtual status_t freeBuffer(
node_id node, OMX_U32 port_index, buffer_id buffer) = 0;
- virtual status_t observe_node(
- node_id node, const sp<IOMXObserver> &observer) = 0;
+ virtual status_t fillBuffer(node_id node, buffer_id buffer) = 0;
- virtual void fill_buffer(node_id node, buffer_id buffer) = 0;
-
- virtual void empty_buffer(
+ virtual status_t emptyBuffer(
node_id node,
buffer_id buffer,
OMX_U32 range_offset, OMX_U32 range_length,
OMX_U32 flags, OMX_TICKS timestamp) = 0;
- virtual status_t get_extension_index(
+ virtual status_t getExtensionIndex(
node_id node,
const char *parameter_name,
OMX_INDEXTYPE *index) = 0;
@@ -162,7 +162,7 @@
public:
DECLARE_META_INTERFACE(OMXObserver);
- virtual void on_message(const omx_message &msg) = 0;
+ virtual void onMessage(const omx_message &msg) = 0;
};
class IOMXRenderer : public IInterface {
diff --git a/include/media/stagefright/ColorConverter.h b/include/media/stagefright/ColorConverter.h
new file mode 100644
index 0000000..1e341b9
--- /dev/null
+++ b/include/media/stagefright/ColorConverter.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COLOR_CONVERTER_H_
+
+#define COLOR_CONVERTER_H_
+
+#include <sys/types.h>
+
+#include <stdint.h>
+
+#include <OMX_Video.h>
+
+namespace android {
+
+struct ColorConverter {
+ ColorConverter(OMX_COLOR_FORMATTYPE from, OMX_COLOR_FORMATTYPE to);
+ ~ColorConverter();
+
+ bool isValid() const;
+
+ void convert(
+ size_t width, size_t height,
+ const void *srcBits, size_t srcSkip,
+ void *dstBits, size_t dstSkip);
+
+private:
+ OMX_COLOR_FORMATTYPE mSrcFormat, mDstFormat;
+ uint8_t *mClip;
+
+ uint8_t *initClip();
+
+ void convertCbYCrY(
+ size_t width, size_t height,
+ const void *srcBits, size_t srcSkip,
+ void *dstBits, size_t dstSkip);
+
+ void convertYUV420Planar(
+ size_t width, size_t height,
+ const void *srcBits, size_t srcSkip,
+ void *dstBits, size_t dstSkip);
+
+ void convertQCOMYUV420SemiPlanar(
+ size_t width, size_t height,
+ const void *srcBits, size_t srcSkip,
+ void *dstBits, size_t dstSkip);
+
+ ColorConverter(const ColorConverter &);
+ ColorConverter &operator=(const ColorConverter &);
+};
+
+} // namespace android
+
+#endif // COLOR_CONVERTER_H_
diff --git a/include/media/stagefright/HTTPDataSource.h b/include/media/stagefright/HTTPDataSource.h
index 0587c7c..02d95fd 100644
--- a/include/media/stagefright/HTTPDataSource.h
+++ b/include/media/stagefright/HTTPDataSource.h
@@ -19,10 +19,11 @@
#define HTTP_DATASOURCE_H_
#include <media/stagefright/DataSource.h>
-#include <media/stagefright/HTTPStream.h>
namespace android {
+class HTTPStream;
+
class HTTPDataSource : public DataSource {
public:
HTTPDataSource(const char *host, int port, const char *path);
@@ -40,7 +41,7 @@
kBufferSize = 64 * 1024
};
- HTTPStream mHttp;
+ HTTPStream *mHttp;
char *mHost;
int mPort;
char *mPath;
diff --git a/include/media/stagefright/MediaErrors.h b/include/media/stagefright/MediaErrors.h
index 2bb0ed6..73d0f77 100644
--- a/include/media/stagefright/MediaErrors.h
+++ b/include/media/stagefright/MediaErrors.h
@@ -36,6 +36,9 @@
ERROR_BUFFER_TOO_SMALL = MEDIA_ERROR_BASE - 9,
ERROR_UNSUPPORTED = MEDIA_ERROR_BASE - 10,
ERROR_END_OF_STREAM = MEDIA_ERROR_BASE - 11,
+
+ // Not technically an error.
+ INFO_FORMAT_CHANGED = MEDIA_ERROR_BASE - 12,
};
} // namespace android
diff --git a/include/media/stagefright/MediaSource.h b/include/media/stagefright/MediaSource.h
index d1fa114..96d57e7 100644
--- a/include/media/stagefright/MediaSource.h
+++ b/include/media/stagefright/MediaSource.h
@@ -51,6 +51,9 @@
// buffer is available, an error is encountered of the end of the stream
// is reached.
// End of stream is signalled by a result of ERROR_END_OF_STREAM.
+ // A result of INFO_FORMAT_CHANGED indicates that the format of this
+ // MediaSource has changed mid-stream, the client can continue reading
+ // but should be prepared for buffers of the new configuration.
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL) = 0;
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index abb45a9..d48ea41 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -27,23 +27,23 @@
namespace android {
+// The following keys map to int32_t data unless indicated otherwise.
enum {
- kKeyMIMEType = 'mime',
+ kKeyMIMEType = 'mime', // cstring
kKeyWidth = 'widt',
kKeyHeight = 'heig',
kKeyChannelCount = '#chn',
kKeySampleRate = 'srte',
kKeyBitRate = 'brte',
- kKeyESDS = 'esds',
- kKeyAVCC = 'avcc',
- kKeyTimeUnits = '#tim',
- kKeyTimeScale = 'scal',
+ kKeyESDS = 'esds', // raw data
+ kKeyAVCC = 'avcc', // raw data
kKeyWantsNALFragments = 'NALf',
kKeyIsSyncFrame = 'sync',
- kKeyDuration = 'dura',
+ kKeyTime = 'time', // int64_t (usecs)
+ kKeyDuration = 'dura', // int64_t (usecs)
kKeyColorFormat = 'colf',
- kKeyPlatformPrivate = 'priv',
- kKeyDecoderComponent = 'decC',
+ kKeyPlatformPrivate = 'priv', // pointer
+ kKeyDecoderComponent = 'decC', // cstring
kKeyBufferID = 'bfID',
kKeyMaxInputSize = 'inpS',
};
@@ -62,6 +62,7 @@
TYPE_NONE = 'none',
TYPE_C_STRING = 'cstr',
TYPE_INT32 = 'in32',
+ TYPE_INT64 = 'in64',
TYPE_FLOAT = 'floa',
TYPE_POINTER = 'ptr ',
};
@@ -71,11 +72,13 @@
bool setCString(uint32_t key, const char *value);
bool setInt32(uint32_t key, int32_t value);
+ bool setInt64(uint32_t key, int64_t value);
bool setFloat(uint32_t key, float value);
bool setPointer(uint32_t key, void *value);
bool findCString(uint32_t key, const char **value);
bool findInt32(uint32_t key, int32_t *value);
+ bool findInt64(uint32_t key, int64_t *value);
bool findFloat(uint32_t key, float *value);
bool findPointer(uint32_t key, void **value);
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index ff7e34a..ec043a9 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -124,6 +124,7 @@
bool mInitialBufferSubmit;
bool mSignalledEOS;
bool mNoMoreOutputData;
+ bool mOutputPortSettingsHaveChanged;
int64_t mSeekTimeUs;
Mutex mLock;
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index e80d8aa..1713324 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -137,11 +137,17 @@
cursor = res.query(uri, MEDIA_COLUMNS, null, null, null);
}
- if (cursor != null && cursor.getCount() == 1) {
- cursor.moveToFirst();
- return cursor.getString(2);
- } else {
- title = uri.getLastPathSegment();
+ try {
+ if (cursor != null && cursor.getCount() == 1) {
+ cursor.moveToFirst();
+ return cursor.getString(2);
+ } else {
+ title = uri.getLastPathSegment();
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
}
}
}
diff --git a/media/java/android/media/ThumbnailUtil.java b/media/java/android/media/ThumbnailUtil.java
index f9d69fb..7c6bca3 100644
--- a/media/java/android/media/ThumbnailUtil.java
+++ b/media/java/android/media/ThumbnailUtil.java
@@ -33,6 +33,7 @@
import android.graphics.Matrix;
import android.graphics.Rect;
import android.media.MediaMetadataRetriever;
+import android.media.MediaFile.MediaFileType;
import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
@@ -305,8 +306,12 @@
ThumbnailUtil.THUMBNAIL_TARGET_SIZE : ThumbnailUtil.MINI_THUMB_TARGET_SIZE;
int maxPixels = wantMini ?
ThumbnailUtil.THUMBNAIL_MAX_NUM_PIXELS : ThumbnailUtil.MINI_THUMB_MAX_NUM_PIXELS;
- byte[] thumbData = createThumbnailFromEXIF(filePath, targetSize);
+ byte[] thumbData = null;
Bitmap bitmap = null;
+ MediaFileType fileType = MediaFile.getFileType(filePath);
+ if (fileType != null && fileType.fileType == MediaFile.FILE_TYPE_JPEG) {
+ thumbData = createThumbnailFromEXIF(filePath, targetSize);
+ }
if (thumbData != null) {
BitmapFactory.Options options = new BitmapFactory.Options();
@@ -454,6 +459,7 @@
Cursor c = cr.query(thumbUri, THUMB_PROJECTION,
Thumbnails.IMAGE_ID + "=?",
new String[]{String.valueOf(origId)}, null);
+ if (c == null) return null;
try {
if (c.moveToNext()) {
return ContentUris.withAppendedId(thumbUri, c.getLong(0));
@@ -482,6 +488,7 @@
if (thumb == null) return false;
try {
Uri uri = getImageThumbnailUri(cr, origId, thumb.getWidth(), thumb.getHeight());
+ if (uri == null) return false;
OutputStream thumbOut = cr.openOutputStream(uri);
thumb.compress(Bitmap.CompressFormat.JPEG, 85, thumbOut);
thumbOut.close();
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index 98f7ef1..cca3e9b 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -35,7 +35,7 @@
DECODE_FD,
CREATE_MEDIA_RECORDER,
CREATE_METADATA_RETRIEVER,
- CREATE_OMX,
+ GET_OMX,
SNOOP
};
@@ -123,10 +123,10 @@
return interface_cast<IMemory>(reply.readStrongBinder());
}
- virtual sp<IOMX> createOMX() {
+ virtual sp<IOMX> getOMX() {
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
- remote()->transact(CREATE_OMX, data, &reply);
+ remote()->transact(GET_OMX, data, &reply);
return interface_cast<IOMX>(reply.readStrongBinder());
}
};
@@ -207,9 +207,9 @@
reply->writeStrongBinder(retriever->asBinder());
return NO_ERROR;
} break;
- case CREATE_OMX: {
+ case GET_OMX: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
- sp<IOMX> omx = createOMX();
+ sp<IOMX> omx = getOMX();
reply->writeStrongBinder(omx->asBinder());
return NO_ERROR;
} break;
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index 0cec7bb..76a9e7d 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -24,7 +24,6 @@
ALLOC_BUFFER,
ALLOC_BUFFER_WITH_BACKUP,
FREE_BUFFER,
- OBSERVE_NODE,
FILL_BUFFER,
EMPTY_BUFFER,
GET_EXTENSION_INDEX,
@@ -76,7 +75,7 @@
: BpInterface<IOMX>(impl) {
}
- virtual status_t list_nodes(List<String8> *list) {
+ virtual status_t listNodes(List<String8> *list) {
list->clear();
Parcel data, reply;
@@ -93,10 +92,12 @@
return OK;
}
- virtual status_t allocate_node(const char *name, node_id *node) {
+ virtual status_t allocateNode(
+ const char *name, const sp<IOMXObserver> &observer, node_id *node) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
data.writeCString(name);
+ data.writeStrongBinder(observer->asBinder());
remote()->transact(ALLOCATE_NODE, data, &reply);
status_t err = reply.readInt32();
@@ -109,7 +110,7 @@
return err;
}
- virtual status_t free_node(node_id node) {
+ virtual status_t freeNode(node_id node) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
data.writeIntPtr((intptr_t)node);
@@ -118,7 +119,7 @@
return reply.readInt32();
}
- virtual status_t send_command(
+ virtual status_t sendCommand(
node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
@@ -130,7 +131,7 @@
return reply.readInt32();
}
- virtual status_t get_parameter(
+ virtual status_t getParameter(
node_id node, OMX_INDEXTYPE index,
void *params, size_t size) {
Parcel data, reply;
@@ -151,7 +152,7 @@
return OK;
}
- virtual status_t set_parameter(
+ virtual status_t setParameter(
node_id node, OMX_INDEXTYPE index,
const void *params, size_t size) {
Parcel data, reply;
@@ -165,7 +166,7 @@
return reply.readInt32();
}
- virtual status_t get_config(
+ virtual status_t getConfig(
node_id node, OMX_INDEXTYPE index,
void *params, size_t size) {
Parcel data, reply;
@@ -186,7 +187,7 @@
return OK;
}
- virtual status_t set_config(
+ virtual status_t setConfig(
node_id node, OMX_INDEXTYPE index,
const void *params, size_t size) {
Parcel data, reply;
@@ -200,7 +201,7 @@
return reply.readInt32();
}
- virtual status_t use_buffer(
+ virtual status_t useBuffer(
node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
buffer_id *buffer) {
Parcel data, reply;
@@ -222,7 +223,7 @@
return err;
}
- virtual status_t allocate_buffer(
+ virtual status_t allocateBuffer(
node_id node, OMX_U32 port_index, size_t size,
buffer_id *buffer) {
Parcel data, reply;
@@ -244,7 +245,7 @@
return err;
}
- virtual status_t allocate_buffer_with_backup(
+ virtual status_t allocateBufferWithBackup(
node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
buffer_id *buffer) {
Parcel data, reply;
@@ -266,7 +267,7 @@
return err;
}
- virtual status_t free_buffer(
+ virtual status_t freeBuffer(
node_id node, OMX_U32 port_index, buffer_id buffer) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
@@ -278,26 +279,17 @@
return reply.readInt32();
}
- virtual status_t observe_node(
- node_id node, const sp<IOMXObserver> &observer) {
- Parcel data, reply;
- data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
- data.writeIntPtr((intptr_t)node);
- data.writeStrongBinder(observer->asBinder());
- remote()->transact(OBSERVE_NODE, data, &reply);
-
- return reply.readInt32();
- }
-
- virtual void fill_buffer(node_id node, buffer_id buffer) {
+ virtual status_t fillBuffer(node_id node, buffer_id buffer) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
data.writeIntPtr((intptr_t)node);
data.writeIntPtr((intptr_t)buffer);
- remote()->transact(FILL_BUFFER, data, &reply, IBinder::FLAG_ONEWAY);
+ remote()->transact(FILL_BUFFER, data, &reply);
+
+ return reply.readInt32();
}
- virtual void empty_buffer(
+ virtual status_t emptyBuffer(
node_id node,
buffer_id buffer,
OMX_U32 range_offset, OMX_U32 range_length,
@@ -310,10 +302,12 @@
data.writeInt32(range_length);
data.writeInt32(flags);
data.writeInt64(timestamp);
- remote()->transact(EMPTY_BUFFER, data, &reply, IBinder::FLAG_ONEWAY);
+ remote()->transact(EMPTY_BUFFER, data, &reply);
+
+ return reply.readInt32();
}
- virtual status_t get_extension_index(
+ virtual status_t getExtensionIndex(
node_id node,
const char *parameter_name,
OMX_INDEXTYPE *index) {
@@ -375,7 +369,7 @@
CHECK_INTERFACE(IOMX, data, reply);
List<String8> list;
- list_nodes(&list);
+ listNodes(&list);
reply->writeInt32(list.size());
for (List<String8>::iterator it = list.begin();
@@ -390,8 +384,14 @@
{
CHECK_INTERFACE(IOMX, data, reply);
+ const char *name = data.readCString();
+
+ sp<IOMXObserver> observer =
+ interface_cast<IOMXObserver>(data.readStrongBinder());
+
node_id node;
- status_t err = allocate_node(data.readCString(), &node);
+
+ status_t err = allocateNode(name, observer, &node);
reply->writeInt32(err);
if (err == OK) {
reply->writeIntPtr((intptr_t)node);
@@ -406,7 +406,7 @@
node_id node = (void*)data.readIntPtr();
- reply->writeInt32(free_node(node));
+ reply->writeInt32(freeNode(node));
return NO_ERROR;
}
@@ -421,7 +421,7 @@
static_cast<OMX_COMMANDTYPE>(data.readInt32());
OMX_S32 param = data.readInt32();
- reply->writeInt32(send_command(node, cmd, param));
+ reply->writeInt32(sendCommand(node, cmd, param));
return NO_ERROR;
}
@@ -439,7 +439,7 @@
void *params = malloc(size);
data.read(params, size);
- status_t err = get_parameter(node, index, params, size);
+ status_t err = getParameter(node, index, params, size);
reply->writeInt32(err);
@@ -463,7 +463,7 @@
size_t size = data.readInt32();
void *params = const_cast<void *>(data.readInplace(size));
- reply->writeInt32(set_parameter(node, index, params, size));
+ reply->writeInt32(setParameter(node, index, params, size));
return NO_ERROR;
}
@@ -481,7 +481,7 @@
void *params = malloc(size);
data.read(params, size);
- status_t err = get_config(node, index, params, size);
+ status_t err = getConfig(node, index, params, size);
reply->writeInt32(err);
@@ -505,7 +505,7 @@
size_t size = data.readInt32();
void *params = const_cast<void *>(data.readInplace(size));
- reply->writeInt32(set_config(node, index, params, size));
+ reply->writeInt32(setConfig(node, index, params, size));
return NO_ERROR;
}
@@ -520,7 +520,7 @@
interface_cast<IMemory>(data.readStrongBinder());
buffer_id buffer;
- status_t err = use_buffer(node, port_index, params, &buffer);
+ status_t err = useBuffer(node, port_index, params, &buffer);
reply->writeInt32(err);
if (err == OK) {
@@ -539,7 +539,7 @@
size_t size = data.readInt32();
buffer_id buffer;
- status_t err = allocate_buffer(node, port_index, size, &buffer);
+ status_t err = allocateBuffer(node, port_index, size, &buffer);
reply->writeInt32(err);
if (err == OK) {
@@ -559,7 +559,7 @@
interface_cast<IMemory>(data.readStrongBinder());
buffer_id buffer;
- status_t err = allocate_buffer_with_backup(
+ status_t err = allocateBufferWithBackup(
node, port_index, params, &buffer);
reply->writeInt32(err);
@@ -578,19 +578,7 @@
node_id node = (void*)data.readIntPtr();
OMX_U32 port_index = data.readInt32();
buffer_id buffer = (void*)data.readIntPtr();
- reply->writeInt32(free_buffer(node, port_index, buffer));
-
- return NO_ERROR;
- }
-
- case OBSERVE_NODE:
- {
- CHECK_INTERFACE(IOMX, data, reply);
-
- node_id node = (void*)data.readIntPtr();
- sp<IOMXObserver> observer =
- interface_cast<IOMXObserver>(data.readStrongBinder());
- reply->writeInt32(observe_node(node, observer));
+ reply->writeInt32(freeBuffer(node, port_index, buffer));
return NO_ERROR;
}
@@ -601,7 +589,7 @@
node_id node = (void*)data.readIntPtr();
buffer_id buffer = (void*)data.readIntPtr();
- fill_buffer(node, buffer);
+ reply->writeInt32(fillBuffer(node, buffer));
return NO_ERROR;
}
@@ -617,9 +605,10 @@
OMX_U32 flags = data.readInt32();
OMX_TICKS timestamp = data.readInt64();
- empty_buffer(
- node, buffer, range_offset, range_length,
- flags, timestamp);
+ reply->writeInt32(
+ emptyBuffer(
+ node, buffer, range_offset, range_length,
+ flags, timestamp));
return NO_ERROR;
}
@@ -632,7 +621,7 @@
const char *parameter_name = data.readCString();
OMX_INDEXTYPE index;
- status_t err = get_extension_index(node, parameter_name, &index);
+ status_t err = getExtensionIndex(node, parameter_name, &index);
reply->writeInt32(err);
@@ -683,7 +672,7 @@
: BpInterface<IOMXObserver>(impl) {
}
- virtual void on_message(const omx_message &msg) {
+ virtual void onMessage(const omx_message &msg) {
Parcel data, reply;
data.writeInterfaceToken(IOMXObserver::getInterfaceDescriptor());
data.write(&msg, sizeof(msg));
@@ -705,7 +694,7 @@
data.read(&msg, sizeof(msg));
// XXX Could use readInplace maybe?
- on_message(msg);
+ onMessage(msg);
return NO_ERROR;
}
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index f21eb73..6fc9fa2 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -18,8 +18,9 @@
ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
-LOCAL_SRC_FILES += \
- StagefrightPlayer.cpp
+LOCAL_SRC_FILES += \
+ StagefrightPlayer.cpp \
+ StagefrightMetadataRetriever.cpp
LOCAL_CFLAGS += -DBUILD_WITH_FULL_STAGEFRIGHT=1
@@ -50,7 +51,7 @@
$(JNI_H_INCLUDE) \
$(call include-path-for, graphics corecg) \
$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
- $(TOP)/frameworks/base/media/libstagefright/omx
+ $(TOP)/frameworks/base/media/libstagefright/include
LOCAL_MODULE:= libmediaplayerservice
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 84be874..ab759df 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -274,8 +274,14 @@
return c;
}
-sp<IOMX> MediaPlayerService::createOMX() {
- return new OMX;
+sp<IOMX> MediaPlayerService::getOMX() {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mOMX.get() == NULL) {
+ mOMX = new OMX;
+ }
+
+ return mOMX;
}
status_t MediaPlayerService::AudioCache::dump(int fd, const Vector<String16>& args) const
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 7d2e611..931667e 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -183,7 +183,7 @@
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
virtual sp<IMemory> snoop();
- virtual sp<IOMX> createOMX();
+ virtual sp<IOMX> getOMX();
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -281,6 +281,7 @@
mutable Mutex mLock;
SortedVector< wp<Client> > mClients;
int32_t mNextConnId;
+ sp<IOMX> mOMX;
};
// ----------------------------------------------------------------------------
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index ddd4e24..9a0d692 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -37,6 +37,7 @@
#include "VorbisMetadataRetriever.h"
#include "MidiMetadataRetriever.h"
#include "MetadataRetrieverClient.h"
+#include "StagefrightMetadataRetriever.h"
namespace android {
@@ -105,9 +106,15 @@
LOGV("create midi metadata retriever");
p = new MidiMetadataRetriever();
break;
+#if BUILD_WITH_FULL_STAGEFRIGHT
+ case STAGEFRIGHT_PLAYER:
+ LOGV("create StagefrightMetadataRetriever");
+ p = new StagefrightMetadataRetriever;
+ break;
+#endif
default:
// TODO:
- // support for STAGEFRIGHT_PLAYER and TEST_PLAYER
+ // support for TEST_PLAYER
LOGE("player type %d is not supported", playerType);
break;
}
diff --git a/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp b/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
new file mode 100644
index 0000000..0e92162
--- /dev/null
+++ b/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
@@ -0,0 +1,197 @@
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "StagefrightMetadataRetriever"
+#include <utils/Log.h>
+
+#include "StagefrightMetadataRetriever.h"
+
+#include <media/stagefright/CachingDataSource.h>
+#include <media/stagefright/ColorConverter.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/HTTPDataSource.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MmapSource.h>
+#include <media/stagefright/OMXCodec.h>
+
+namespace android {
+
+StagefrightMetadataRetriever::StagefrightMetadataRetriever() {
+ LOGV("StagefrightMetadataRetriever()");
+
+ DataSource::RegisterDefaultSniffers();
+ CHECK_EQ(mClient.connect(), OK);
+}
+
+StagefrightMetadataRetriever::~StagefrightMetadataRetriever() {
+ LOGV("~StagefrightMetadataRetriever()");
+ mClient.disconnect();
+}
+
+status_t StagefrightMetadataRetriever::setDataSource(const char *uri) {
+ LOGV("setDataSource(%s)", uri);
+
+ sp<DataSource> source;
+ if (!strncasecmp("file://", uri, 7)) {
+ sp<MmapSource> mmapSource = new MmapSource(uri + 7);
+ if (mmapSource->InitCheck() != OK) {
+ return ERROR_IO;
+ }
+ source = mmapSource;
+ } else if (!strncasecmp("http://", uri, 7)) {
+ source = new HTTPDataSource(uri);
+ source = new CachingDataSource(source, 64 * 1024, 10);
+ } else {
+ // Assume it's a filename.
+ sp<MmapSource> mmapSource = new MmapSource(uri);
+ if (mmapSource->InitCheck() != OK) {
+ return ERROR_IO;
+ }
+ source = mmapSource;
+ }
+
+ mExtractor = MediaExtractor::Create(source);
+
+ return mExtractor.get() != NULL ? OK : UNKNOWN_ERROR;
+}
+
+status_t StagefrightMetadataRetriever::setDataSource(
+ int fd, int64_t offset, int64_t length) {
+ LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
+
+ mExtractor = MediaExtractor::Create(
+ new MmapSource(fd, offset, length));
+
+ return OK;
+}
+
+VideoFrame *StagefrightMetadataRetriever::captureFrame() {
+ LOGV("captureFrame");
+
+ if (mExtractor.get() == NULL) {
+ LOGE("no extractor.");
+ return NULL;
+ }
+
+ size_t n = mExtractor->countTracks();
+ size_t i;
+ for (i = 0; i < n; ++i) {
+ sp<MetaData> meta = mExtractor->getTrackMetaData(i);
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ if (!strncasecmp(mime, "video/", 6)) {
+ break;
+ }
+ }
+
+ if (i == n) {
+ LOGE("no video track found.");
+ return NULL;
+ }
+
+ sp<MediaSource> source = mExtractor->getTrack(i);
+
+ if (source.get() == NULL) {
+ LOGE("unable to instantiate video track.");
+ return NULL;
+ }
+
+ sp<MetaData> meta = source->getFormat();
+
+ sp<MediaSource> decoder =
+ OMXCodec::Create(
+ mClient.interface(), meta, false, source);
+
+ if (decoder.get() == NULL) {
+ LOGE("unable to instantiate video decoder.");
+
+ return NULL;
+ }
+
+ decoder->start();
+
+ MediaBuffer *buffer;
+ status_t err;
+ do {
+ err = decoder->read(&buffer);
+ } while (err == INFO_FORMAT_CHANGED);
+
+ if (err != OK) {
+ CHECK_EQ(buffer, NULL);
+
+ LOGE("decoding frame failed.");
+ decoder->stop();
+
+ return NULL;
+ }
+
+ LOGI("successfully decoded video frame.");
+
+ meta = decoder->getFormat();
+
+ int32_t width, height;
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+
+ VideoFrame *frame = new VideoFrame;
+ frame->mWidth = width;
+ frame->mHeight = height;
+ frame->mDisplayWidth = width;
+ frame->mDisplayHeight = height;
+ frame->mSize = width * height * 2;
+ frame->mData = new uint8_t[frame->mSize];
+
+ int32_t srcFormat;
+ CHECK(meta->findInt32(kKeyColorFormat, &srcFormat));
+
+ ColorConverter converter(
+ (OMX_COLOR_FORMATTYPE)srcFormat, OMX_COLOR_Format16bitRGB565);
+ CHECK(converter.isValid());
+
+ converter.convert(
+ width, height,
+ (const uint8_t *)buffer->data() + buffer->range_offset(),
+ 0,
+ frame->mData, width * 2);
+
+ buffer->release();
+ buffer = NULL;
+
+ decoder->stop();
+
+ return frame;
+}
+
+MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() {
+ LOGV("extractAlbumArt (extractor: %s)", mExtractor.get() != NULL ? "YES" : "NO");
+
+ return NULL;
+}
+
+const char *StagefrightMetadataRetriever::extractMetadata(int keyCode) {
+ LOGV("extractMetadata %d (extractor: %s)",
+ keyCode, mExtractor.get() != NULL ? "YES" : "NO");
+
+ return NULL;
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/StagefrightMetadataRetriever.h b/media/libmediaplayerservice/StagefrightMetadataRetriever.h
new file mode 100644
index 0000000..16127d7
--- /dev/null
+++ b/media/libmediaplayerservice/StagefrightMetadataRetriever.h
@@ -0,0 +1,53 @@
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef STAGEFRIGHT_METADATA_RETRIEVER_H_
+
+#define STAGEFRIGHT_METADATA_RETRIEVER_H_
+
+#include <media/MediaMetadataRetrieverInterface.h>
+
+#include <media/stagefright/OMXClient.h>
+
+namespace android {
+
+class MediaExtractor;
+
+struct StagefrightMetadataRetriever : public MediaMetadataRetrieverInterface {
+ StagefrightMetadataRetriever();
+ virtual ~StagefrightMetadataRetriever();
+
+ virtual status_t setDataSource(const char *url);
+ virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
+
+ virtual VideoFrame *captureFrame();
+ virtual MediaAlbumArt *extractAlbumArt();
+ virtual const char *extractMetadata(int keyCode);
+
+private:
+ OMXClient mClient;
+ sp<MediaExtractor> mExtractor;
+
+ StagefrightMetadataRetriever(const StagefrightMetadataRetriever &);
+
+ StagefrightMetadataRetriever &operator=(
+ const StagefrightMetadataRetriever &);
+};
+
+} // namespace android
+
+#endif // STAGEFRIGHT_METADATA_RETRIEVER_H_
diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp
index 8d85ce2..1660351 100644
--- a/media/libstagefright/AMRExtractor.cpp
+++ b/media/libstagefright/AMRExtractor.cpp
@@ -201,10 +201,7 @@
}
buffer->set_range(0, frameSize);
- buffer->meta_data()->setInt32(
- kKeyTimeUnits, (mCurrentTimeUs + 500) / 1000);
- buffer->meta_data()->setInt32(
- kKeyTimeScale, 1000);
+ buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs);
mOffset += frameSize;
mCurrentTimeUs += 20000; // Each frame is 20ms
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 319488e..7b4d178 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -209,15 +209,9 @@
break;
}
- int32_t units, scale;
- bool success =
- mInputBuffer->meta_data()->findInt32(kKeyTimeUnits, &units);
- success = success &&
- mInputBuffer->meta_data()->findInt32(kKeyTimeScale, &scale);
- CHECK(success);
-
Mutex::Autolock autoLock(mLock);
- mPositionTimeMediaUs = (int64_t)units * 1000000 / scale;
+ CHECK(mInputBuffer->meta_data()->findInt64(
+ kKeyTime, &mPositionTimeMediaUs));
mPositionTimeRealUs =
((mNumFramesPlayed + size_done / mFrameSize) * 1000000)
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 596ab67..40028a5 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -191,8 +191,7 @@
*buffer = new CameraBuffer(frame);
(*buffer)->meta_data()->clear();
- (*buffer)->meta_data()->setInt32(kKeyTimeScale, 15);
- (*buffer)->meta_data()->setInt32(kKeyTimeUnits, count);
+ (*buffer)->meta_data()->setInt64(kKeyTime, (count * 1000000) / 15);
(*buffer)->add_ref();
(*buffer)->setObserver(this);
diff --git a/media/libstagefright/ESDS.cpp b/media/libstagefright/ESDS.cpp
index 53b92a0..28d338c 100644
--- a/media/libstagefright/ESDS.cpp
+++ b/media/libstagefright/ESDS.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <media/stagefright/ESDS.h>
+#include "include/ESDS.h"
#include <string.h>
diff --git a/media/libstagefright/HTTPDataSource.cpp b/media/libstagefright/HTTPDataSource.cpp
index 698223b..cc25886 100644
--- a/media/libstagefright/HTTPDataSource.cpp
+++ b/media/libstagefright/HTTPDataSource.cpp
@@ -14,17 +14,19 @@
* limitations under the License.
*/
+#include "include/stagefright_string.h"
+#include "include/HTTPStream.h"
+
#include <stdlib.h>
#include <media/stagefright/HTTPDataSource.h>
-#include <media/stagefright/HTTPStream.h>
#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/string.h>
namespace android {
HTTPDataSource::HTTPDataSource(const char *uri)
- : mHost(NULL),
+ : mHttp(new HTTPStream),
+ mHost(NULL),
mPort(0),
mPath(NULL),
mBuffer(malloc(kBufferSize)),
@@ -65,33 +67,38 @@
mPort = port;
mPath = strdup(path.c_str());
- status_t err = mHttp.connect(mHost, mPort);
+ status_t err = mHttp->connect(mHost, mPort);
CHECK_EQ(err, OK);
}
HTTPDataSource::HTTPDataSource(const char *host, int port, const char *path)
- : mHost(strdup(host)),
+ : mHttp(new HTTPStream),
+ mHost(strdup(host)),
mPort(port),
mPath(strdup(path)),
mBuffer(malloc(kBufferSize)),
mBufferLength(0),
mBufferOffset(0) {
- status_t err = mHttp.connect(mHost, mPort);
+ status_t err = mHttp->connect(mHost, mPort);
CHECK_EQ(err, OK);
}
HTTPDataSource::~HTTPDataSource() {
- mHttp.disconnect();
+ mHttp->disconnect();
free(mBuffer);
mBuffer = NULL;
free(mPath);
mPath = NULL;
+
+ delete mHttp;
+ mHttp = NULL;
}
ssize_t HTTPDataSource::read_at(off_t offset, void *data, size_t size) {
- if (offset >= mBufferOffset && offset < mBufferOffset + mBufferLength) {
+ if (offset >= mBufferOffset
+ && offset < (off_t)(mBufferOffset + mBufferLength)) {
size_t num_bytes_available = mBufferLength - (offset - mBufferOffset);
size_t copy = num_bytes_available;
@@ -119,19 +126,19 @@
status_t err;
int attempt = 1;
for (;;) {
- if ((err = mHttp.send("GET ")) != OK
- || (err = mHttp.send(mPath)) != OK
- || (err = mHttp.send(" HTTP/1.1\r\n")) != OK
- || (err = mHttp.send(host)) != OK
- || (err = mHttp.send(range)) != OK
- || (err = mHttp.send("\r\n")) != OK
- || (err = mHttp.receive_header(&http_status)) != OK) {
+ if ((err = mHttp->send("GET ")) != OK
+ || (err = mHttp->send(mPath)) != OK
+ || (err = mHttp->send(" HTTP/1.1\r\n")) != OK
+ || (err = mHttp->send(host)) != OK
+ || (err = mHttp->send(range)) != OK
+ || (err = mHttp->send("\r\n")) != OK
+ || (err = mHttp->receive_header(&http_status)) != OK) {
if (attempt == 3) {
return err;
}
- mHttp.connect(mHost, mPort);
+ mHttp->connect(mHost, mPort);
++attempt;
} else {
break;
@@ -143,14 +150,14 @@
}
string value;
- if (!mHttp.find_header_value("Content-Length", &value)) {
+ if (!mHttp->find_header_value("Content-Length", &value)) {
return UNKNOWN_ERROR;
}
char *end;
unsigned long contentLength = strtoul(value.c_str(), &end, 10);
- ssize_t num_bytes_received = mHttp.receive(mBuffer, contentLength);
+ ssize_t num_bytes_received = mHttp->receive(mBuffer, contentLength);
if (num_bytes_received <= 0) {
return num_bytes_received;
diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp
index 6af7df9..02f9439 100644
--- a/media/libstagefright/HTTPStream.cpp
+++ b/media/libstagefright/HTTPStream.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "include/HTTPStream.h"
+
#include <sys/socket.h>
#include <arpa/inet.h>
@@ -25,7 +27,6 @@
#include <string.h>
#include <unistd.h>
-#include <media/stagefright/HTTPStream.h>
#include <media/stagefright/MediaDebug.h>
namespace android {
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 7fd699f..b7dd9ba 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -147,7 +147,12 @@
*out_bitrate = bitrate;
}
- *frame_size = 144000 * bitrate / sampling_rate + padding;
+ if (version == 3 /* V1 */) {
+ *frame_size = 144000 * bitrate / sampling_rate + padding;
+ } else {
+ // V2 or V2.5
+ *frame_size = 72000 * bitrate / sampling_rate + padding;
+ }
}
if (out_sampling_rate) {
@@ -166,6 +171,33 @@
static bool Resync(
const sp<DataSource> &source, uint32_t match_header,
off_t *inout_pos, uint32_t *out_header) {
+ if (*inout_pos == 0) {
+ // Skip an optional ID3 header if syncing at the very beginning
+ // of the datasource.
+
+ uint8_t id3header[10];
+ if (source->read_at(0, id3header, sizeof(id3header))
+ < (ssize_t)sizeof(id3header)) {
+ // If we can't even read these 10 bytes, we might as well bail out,
+ // even if there _were_ 10 bytes of valid mp3 audio data...
+ return false;
+ }
+
+ if (id3header[0] == 'I' && id3header[1] == 'D' && id3header[2] == '3') {
+ // Skip the ID3v2 header.
+
+ size_t len =
+ ((id3header[6] & 0x7f) << 21)
+ | ((id3header[7] & 0x7f) << 14)
+ | ((id3header[8] & 0x7f) << 7)
+ | (id3header[9] & 0x7f);
+
+ len += 10;
+
+ *inout_pos += len;
+ }
+ }
+
// Everything must match except for
// protection, bitrate, padding, private bits and mode extension.
const uint32_t kMask = 0xfffe0ccf;
@@ -338,10 +370,9 @@
off_t fileSize;
if (mDataSource->getSize(&fileSize) == OK) {
- mMeta->setInt32(
+ mMeta->setInt64(
kKeyDuration,
- 8 * (fileSize - mFirstFramePos) / bitrate);
- mMeta->setInt32(kKeyTimeScale, 1000);
+ 8000 * (fileSize - mFirstFramePos) / bitrate);
}
}
}
@@ -492,8 +523,7 @@
buffer->set_range(0, frame_size);
- buffer->meta_data()->setInt32(kKeyTimeUnits, mCurrentTimeUs / 1000);
- buffer->meta_data()->setInt32(kKeyTimeScale, 1000);
+ buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs);
mCurrentPos += frame_size;
mCurrentTimeUs += 1152 * 1000000 / 44100;
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 9174d19..da714f8 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "MPEG4Extractor"
#include <utils/Log.h>
+#include "include/SampleTable.h"
+
#include <arpa/inet.h>
#include <ctype.h>
@@ -32,7 +34,6 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
-#include <media/stagefright/SampleTable.h>
#include <media/stagefright/Utils.h>
#include <utils/String8.h>
@@ -43,6 +44,7 @@
// Caller retains ownership of both "dataSource" and "sampleTable".
MPEG4Source(const sp<MetaData> &format,
const sp<DataSource> &dataSource,
+ int32_t timeScale,
const sp<SampleTable> &sampleTable);
virtual status_t start(MetaData *params = NULL);
@@ -390,7 +392,6 @@
}
mLastTrack->timescale = ntohl(timescale);
- mLastTrack->meta->setInt32(kKeyTimeScale, mLastTrack->timescale);
int64_t duration;
if (version == 1) {
@@ -409,7 +410,8 @@
}
duration = ntohl(duration32);
}
- mLastTrack->meta->setInt32(kKeyDuration, duration);
+ mLastTrack->meta->setInt64(
+ kKeyDuration, (duration * 1000000) / mLastTrack->timescale);
*offset += chunk_size;
break;
@@ -722,7 +724,7 @@
}
return new MPEG4Source(
- track->meta, mDataSource, track->sampleTable);
+ track->meta, mDataSource, track->timescale, track->sampleTable);
}
////////////////////////////////////////////////////////////////////////////////
@@ -730,10 +732,11 @@
MPEG4Source::MPEG4Source(
const sp<MetaData> &format,
const sp<DataSource> &dataSource,
+ int32_t timeScale,
const sp<SampleTable> &sampleTable)
: mFormat(format),
mDataSource(dataSource),
- mTimescale(0),
+ mTimescale(timeScale),
mSampleTable(sampleTable),
mCurrentSampleIndex(0),
mIsAVC(false),
@@ -746,9 +749,6 @@
bool success = mFormat->findCString(kKeyMIMEType, &mime);
CHECK(success);
- success = mFormat->findInt32(kKeyTimeScale, &mTimescale);
- CHECK(success);
-
mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
}
@@ -879,8 +879,8 @@
mBuffer->set_range(0, size);
mBuffer->meta_data()->clear();
- mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
- mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
+ mBuffer->meta_data()->setInt64(
+ kKeyTime, ((int64_t)dts * 1000000) / mTimescale);
++mCurrentSampleIndex;
}
@@ -959,8 +959,8 @@
mBuffer->set_range(0, dstOffset);
mBuffer->meta_data()->clear();
- mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
- mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
+ mBuffer->meta_data()->setInt64(
+ kKeyTime, ((int64_t)dts * 1000000) / mTimescale);
++mCurrentSampleIndex;
*out = mBuffer;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index fa35768..9a7a873 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -399,15 +399,11 @@
info.size = buffer->range_length();
info.offset = offset;
- int32_t units, scale;
- bool success =
- buffer->meta_data()->findInt32(kKeyTimeUnits, &units);
- CHECK(success);
- success =
- buffer->meta_data()->findInt32(kKeyTimeScale, &scale);
- CHECK(success);
+ int64_t timestampUs;
+ CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs));
- info.timestamp = (int64_t)units * 1000 / scale;
+ // Our timestamp is in ms.
+ info.timestamp = (timestampUs + 500) / 1000;
mSampleInfos.push_back(info);
diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp
index f3c0e73..b973745b 100644
--- a/media/libstagefright/MediaBuffer.cpp
+++ b/media/libstagefright/MediaBuffer.cpp
@@ -108,10 +108,10 @@
}
void MediaBuffer::set_range(size_t offset, size_t length) {
- if (offset < 0 || offset + length > mSize) {
+ if (offset + length > mSize) {
LOGE("offset = %d, length = %d, mSize = %d", offset, length, mSize);
}
- CHECK(offset >= 0 && offset + length <= mSize);
+ CHECK(offset + length <= mSize);
mRangeOffset = offset;
mRangeLength = length;
diff --git a/media/libstagefright/MediaPlayerImpl.cpp b/media/libstagefright/MediaPlayerImpl.cpp
index 2e609e3..4e0e996 100644
--- a/media/libstagefright/MediaPlayerImpl.cpp
+++ b/media/libstagefright/MediaPlayerImpl.cpp
@@ -18,6 +18,9 @@
#define LOG_TAG "MediaPlayerImpl"
#include "utils/Log.h"
+#include "include/stagefright_string.h"
+#include "include/HTTPStream.h"
+
#include <OMX_Component.h>
#include <unistd.h>
@@ -26,7 +29,6 @@
#include <media/stagefright/CachingDataSource.h>
// #include <media/stagefright/CameraSource.h>
#include <media/stagefright/HTTPDataSource.h>
-#include <media/stagefright/HTTPStream.h>
#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MediaPlayerImpl.h>
@@ -250,6 +252,13 @@
status_t err = mVideoDecoder->read(&buffer, &options);
CHECK((err == OK && buffer != NULL) || (err != OK && buffer == NULL));
+ if (err == INFO_FORMAT_CHANGED) {
+ LOGI("format changed.");
+ depopulateISurface();
+ populateISurface();
+ continue;
+ }
+
if (err == ERROR_END_OF_STREAM || err != OK) {
eof = true;
continue;
@@ -261,15 +270,9 @@
continue;
}
- int32_t units, scale;
- bool success =
- buffer->meta_data()->findInt32(kKeyTimeUnits, &units);
- CHECK(success);
- success =
- buffer->meta_data()->findInt32(kKeyTimeScale, &scale);
- CHECK(success);
+ int64_t pts_us;
+ CHECK(buffer->meta_data()->findInt64(kKeyTime, &pts_us));
- int64_t pts_us = (int64_t)units * 1000000 / scale;
{
Mutex::Autolock autoLock(mLock);
mVideoPosition = pts_us;
@@ -379,12 +382,10 @@
sp<MediaSource> source = mExtractor->getTrack(i);
- int32_t units, scale;
- if (meta->findInt32(kKeyDuration, &units)
- && meta->findInt32(kKeyTimeScale, &scale)) {
- int64_t duration_us = (int64_t)units * 1000000 / scale;
- if (duration_us > mDuration) {
- mDuration = duration_us;
+ int64_t durationUs;
+ if (meta->findInt64(kKeyDuration, &durationUs)) {
+ if (durationUs > mDuration) {
+ mDuration = durationUs;
}
}
@@ -609,6 +610,9 @@
success = success && meta->findInt32(kKeyHeight, &decodedHeight);
CHECK(success);
+ LOGI("mVideoWidth=%d, mVideoHeight=%d, decodedWidth=%d, decodedHeight=%d",
+ mVideoWidth, mVideoHeight, decodedWidth, decodedHeight);
+
if (mSurface.get() != NULL) {
mVideoRenderer =
mClient.interface()->createRenderer(
diff --git a/media/libstagefright/MetaData.cpp b/media/libstagefright/MetaData.cpp
index 6b067cb..63b476e 100644
--- a/media/libstagefright/MetaData.cpp
+++ b/media/libstagefright/MetaData.cpp
@@ -58,6 +58,10 @@
return setData(key, TYPE_INT32, &value, sizeof(value));
}
+bool MetaData::setInt64(uint32_t key, int64_t value) {
+ return setData(key, TYPE_INT64, &value, sizeof(value));
+}
+
bool MetaData::setFloat(uint32_t key, float value) {
return setData(key, TYPE_FLOAT, &value, sizeof(value));
}
@@ -94,6 +98,21 @@
return true;
}
+bool MetaData::findInt64(uint32_t key, int64_t *value) {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_INT64) {
+ return false;
+ }
+
+ CHECK_EQ(size, sizeof(*value));
+
+ *value = *(int64_t *)data;
+
+ return true;
+}
+
bool MetaData::findFloat(uint32_t key, float *value) {
uint32_t type;
const void *data;
diff --git a/media/libstagefright/MmapSource.cpp b/media/libstagefright/MmapSource.cpp
index 47d95f9..8277c15 100644
--- a/media/libstagefright/MmapSource.cpp
+++ b/media/libstagefright/MmapSource.cpp
@@ -34,7 +34,10 @@
mBase(NULL),
mSize(0) {
LOGV("MmapSource '%s'", filename);
- CHECK(mFd >= 0);
+
+ if (mFd < 0) {
+ return;
+ }
off_t size = lseek(mFd, 0, SEEK_END);
mSize = (size_t)size;
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index dba7a2a..9de873e 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -35,7 +35,7 @@
CHECK(service.get() != NULL);
- mOMX = service->createOMX();
+ mOMX = service->getOMX();
CHECK(mOMX.get() != NULL);
return OK;
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index c4c6149..264271d 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -18,11 +18,12 @@
#define LOG_TAG "OMXCodec"
#include <utils/Log.h>
+#include "include/ESDS.h"
+
#include <binder/IServiceManager.h>
#include <binder/MemoryDealer.h>
#include <binder/ProcessState.h>
#include <media/IMediaPlayerService.h>
-#include <media/stagefright/ESDS.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDebug.h>
@@ -39,6 +40,8 @@
namespace android {
+static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
+
struct CodecInfo {
const char *mime;
const char *codec;
@@ -85,12 +88,15 @@
#define CODEC_LOGV(x, ...) LOGV("[%s] "x, mComponentName, ##__VA_ARGS__)
struct OMXCodecObserver : public BnOMXObserver {
- OMXCodecObserver(const wp<OMXCodec> &target)
- : mTarget(target) {
+ OMXCodecObserver() {
+ }
+
+ void setCodec(const sp<OMXCodec> &target) {
+ mTarget = target;
}
// from IOMXObserver
- virtual void on_message(const omx_message &msg) {
+ virtual void onMessage(const omx_message &msg) {
sp<OMXCodec> codec = mTarget.promote();
if (codec.get() != NULL) {
@@ -177,6 +183,7 @@
CHECK(success);
const char *componentName = NULL;
+ sp<OMXCodecObserver> observer = new OMXCodecObserver;
IOMX::node_id node = 0;
for (int index = 0;; ++index) {
if (createEncoder) {
@@ -200,9 +207,9 @@
LOGV("Attempting to allocate OMX node '%s'", componentName);
- status_t err = omx->allocate_node(componentName, &node);
+ status_t err = omx->allocateNode(componentName, observer, &node);
if (err == OK) {
- LOGI("Successfully allocated OMX node '%s'", componentName);
+ LOGV("Successfully allocated OMX node '%s'", componentName);
break;
}
}
@@ -217,11 +224,6 @@
if (!strcmp(componentName, "OMX.TI.AAC.decode")) {
quirks |= kNeedsFlushBeforeDisable;
quirks |= kRequiresFlushCompleteEmulation;
-
- // The following is currently necessary for proper shutdown
- // behaviour, but NOT enabled by default in order to make the
- // bug reproducible...
- // quirks |= kRequiresFlushBeforeShutdown;
}
if (!strncmp(componentName, "OMX.qcom.video.encoder.", 23)) {
quirks |= kRequiresLoadedToIdleAfterAllocation;
@@ -247,6 +249,8 @@
omx, node, quirks, createEncoder, mime, componentName,
source);
+ observer->setCodec(codec);
+
uint32_t type;
const void *data;
size_t size;
@@ -324,12 +328,14 @@
size -= length;
}
- LOGI("AVC profile = %d (%s), level = %d",
+ LOGV("AVC profile = %d (%s), level = %d",
(int)profile, AVCProfileToString(profile), (int)level / 10);
if (!strcmp(componentName, "OMX.TI.Video.Decoder")
&& (profile != kAVCProfileBaseline || level > 39)) {
- // This stream exceeds the decoder's capabilities.
+ // This stream exceeds the decoder's capabilities. The decoder
+ // does not handle this gracefully and would clobber the heap
+ // and wreak havoc instead...
LOGE("Profile and/or level exceed the decoder's capabilities.");
return NULL;
@@ -404,7 +410,7 @@
InitOMXParams(&def);
def.nPortIndex = portIndex;
- status_t err = mOMX->get_parameter(
+ status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
@@ -413,7 +419,7 @@
}
- err = mOMX->set_parameter(
+ err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
}
@@ -431,7 +437,7 @@
OMX_U32 index = 0;
for (;;) {
format.nIndex = index;
- status_t err = mOMX->get_parameter(
+ status_t err = mOMX->getParameter(
mNode, OMX_IndexParamVideoPortFormat,
&format, sizeof(format));
@@ -443,7 +449,7 @@
// CHECK_EQ(format.nIndex, index);
#if 1
- CODEC_LOGI("portIndex: %ld, index: %ld, eCompressionFormat=%d eColorFormat=%d",
+ CODEC_LOGV("portIndex: %ld, index: %ld, eCompressionFormat=%d eColorFormat=%d",
portIndex,
index, format.eCompressionFormat, format.eColorFormat);
#endif
@@ -476,8 +482,8 @@
return UNKNOWN_ERROR;
}
- CODEC_LOGI("found a match.");
- status_t err = mOMX->set_parameter(
+ CODEC_LOGV("found a match.");
+ status_t err = mOMX->setParameter(
mNode, OMX_IndexParamVideoPortFormat,
&format, sizeof(format));
@@ -486,7 +492,7 @@
void OMXCodec::setVideoInputFormat(
const char *mime, OMX_U32 width, OMX_U32 height) {
- CODEC_LOGI("setVideoInputFormat width=%ld, height=%ld", width, height);
+ CODEC_LOGV("setVideoInputFormat width=%ld, height=%ld", width, height);
OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
@@ -520,7 +526,7 @@
OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
- status_t err = mOMX->get_parameter(
+ status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
@@ -532,7 +538,7 @@
video_def->eCompressionFormat = compressionFormat;
video_def->eColorFormat = OMX_COLOR_FormatUnused;
- err = mOMX->set_parameter(
+ err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
@@ -541,12 +547,12 @@
InitOMXParams(&def);
def.nPortIndex = kPortIndexInput;
- err = mOMX->get_parameter(
+ err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
def.nBufferSize = (width * height * 2); // (width * height * 3) / 2;
- CODEC_LOGI("Setting nBufferSize = %ld", def.nBufferSize);
+ CODEC_LOGV("Setting nBufferSize = %ld", def.nBufferSize);
CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
@@ -555,14 +561,14 @@
video_def->eCompressionFormat = OMX_VIDEO_CodingUnused;
video_def->eColorFormat = colorFormat;
- err = mOMX->set_parameter(
+ err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
}
void OMXCodec::setVideoOutputFormat(
const char *mime, OMX_U32 width, OMX_U32 height) {
- CODEC_LOGI("setVideoOutputFormat width=%ld, height=%ld", width, height);
+ CODEC_LOGV("setVideoOutputFormat width=%ld, height=%ld", width, height);
OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
@@ -586,7 +592,7 @@
format.nPortIndex = kPortIndexOutput;
format.nIndex = 0;
- status_t err = mOMX->get_parameter(
+ status_t err = mOMX->getParameter(
mNode, OMX_IndexParamVideoPortFormat,
&format, sizeof(format));
CHECK_EQ(err, OK);
@@ -599,7 +605,7 @@
|| format.eColorFormat == OMX_COLOR_FormatCbYCrY
|| format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar);
- err = mOMX->set_parameter(
+ err = mOMX->setParameter(
mNode, OMX_IndexParamVideoPortFormat,
&format, sizeof(format));
CHECK_EQ(err, OK);
@@ -612,7 +618,7 @@
OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
- status_t err = mOMX->get_parameter(
+ status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
@@ -632,7 +638,7 @@
video_def->eColorFormat = OMX_COLOR_FormatUnused;
- err = mOMX->set_parameter(
+ err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
@@ -641,7 +647,7 @@
InitOMXParams(&def);
def.nPortIndex = kPortIndexOutput;
- err = mOMX->get_parameter(
+ err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
@@ -654,7 +660,7 @@
video_def->nFrameWidth = width;
video_def->nFrameHeight = height;
- err = mOMX->set_parameter(
+ err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
}
@@ -678,13 +684,11 @@
mInitialBufferSubmit(true),
mSignalledEOS(false),
mNoMoreOutputData(false),
+ mOutputPortSettingsHaveChanged(false),
mSeekTimeUs(-1) {
mPortStatus[kPortIndexInput] = ENABLED;
mPortStatus[kPortIndexOutput] = ENABLED;
- mObserver = new OMXCodecObserver(this);
- mOMX->observe_node(mNode, mObserver);
-
setComponentRole();
}
@@ -742,7 +746,7 @@
roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
- status_t err = omx->set_parameter(
+ status_t err = omx->setParameter(
node, OMX_IndexParamStandardComponentRole,
&roleParams, sizeof(roleParams));
@@ -759,10 +763,7 @@
OMXCodec::~OMXCodec() {
CHECK(mState == LOADED || mState == ERROR);
- status_t err = mOMX->observe_node(mNode, NULL);
- CHECK_EQ(err, OK);
-
- err = mOMX->free_node(mNode);
+ status_t err = mOMX->freeNode(mNode);
CHECK_EQ(err, OK);
mNode = NULL;
@@ -784,7 +785,7 @@
status_t err;
if (!(mQuirks & kRequiresLoadedToIdleAfterAllocation)) {
- err = mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
+ err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
CHECK_EQ(err, OK);
setState(LOADED_TO_IDLE);
}
@@ -793,7 +794,7 @@
CHECK_EQ(err, OK);
if (mQuirks & kRequiresLoadedToIdleAfterAllocation) {
- err = mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
+ err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
CHECK_EQ(err, OK);
setState(LOADED_TO_IDLE);
@@ -830,7 +831,7 @@
InitOMXParams(&def);
def.nPortIndex = portIndex;
- status_t err = mOMX->get_parameter(
+ status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
if (err != OK) {
@@ -847,14 +848,14 @@
IOMX::buffer_id buffer;
if (portIndex == kPortIndexInput
&& (mQuirks & kRequiresAllocateBufferOnInputPorts)) {
- err = mOMX->allocate_buffer_with_backup(
+ err = mOMX->allocateBufferWithBackup(
mNode, portIndex, mem, &buffer);
} else if (portIndex == kPortIndexOutput
&& (mQuirks & kRequiresAllocateBufferOnOutputPorts)) {
- err = mOMX->allocate_buffer_with_backup(
+ err = mOMX->allocateBufferWithBackup(
mNode, portIndex, mem, &buffer);
} else {
- err = mOMX->use_buffer(mNode, portIndex, mem, &buffer);
+ err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
}
if (err != OK) {
@@ -921,7 +922,7 @@
CODEC_LOGV("Port is disabled, freeing buffer %p", buffer);
status_t err =
- mOMX->free_buffer(mNode, kPortIndexInput, buffer);
+ mOMX->freeBuffer(mNode, kPortIndexInput, buffer);
CHECK_EQ(err, OK);
buffers->removeAt(i);
@@ -967,7 +968,7 @@
CODEC_LOGV("Port is disabled, freeing buffer %p", buffer);
status_t err =
- mOMX->free_buffer(mNode, kPortIndexOutput, buffer);
+ mOMX->freeBuffer(mNode, kPortIndexOutput, buffer);
CHECK_EQ(err, OK);
buffers->removeAt(i);
@@ -987,12 +988,8 @@
buffer->meta_data()->clear();
- buffer->meta_data()->setInt32(
- kKeyTimeUnits,
- (msg.u.extended_buffer_data.timestamp + 500) / 1000);
-
- buffer->meta_data()->setInt32(
- kKeyTimeScale, 1000);
+ buffer->meta_data()->setInt64(
+ kKeyTime, msg.u.extended_buffer_data.timestamp);
if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_SYNCFRAME) {
buffer->meta_data()->setInt32(kKeyIsSyncFrame, true);
@@ -1061,6 +1058,71 @@
}
}
+// Has the format changed in any way that the client would have to be aware of?
+static bool formatHasNotablyChanged(
+ const sp<MetaData> &from, const sp<MetaData> &to) {
+ if (from.get() == NULL && to.get() == NULL) {
+ return false;
+ }
+
+ if (from.get() == NULL && to.get() != NULL
+ || from.get() != NULL && to.get() == NULL) {
+ return true;
+ }
+
+ const char *mime_from, *mime_to;
+ CHECK(from->findCString(kKeyMIMEType, &mime_from));
+ CHECK(to->findCString(kKeyMIMEType, &mime_to));
+
+ if (strcasecmp(mime_from, mime_to)) {
+ return true;
+ }
+
+ if (!strcasecmp(mime_from, MEDIA_MIMETYPE_VIDEO_RAW)) {
+ int32_t colorFormat_from, colorFormat_to;
+ CHECK(from->findInt32(kKeyColorFormat, &colorFormat_from));
+ CHECK(to->findInt32(kKeyColorFormat, &colorFormat_to));
+
+ if (colorFormat_from != colorFormat_to) {
+ return true;
+ }
+
+ int32_t width_from, width_to;
+ CHECK(from->findInt32(kKeyWidth, &width_from));
+ CHECK(to->findInt32(kKeyWidth, &width_to));
+
+ if (width_from != width_to) {
+ return true;
+ }
+
+ int32_t height_from, height_to;
+ CHECK(from->findInt32(kKeyHeight, &height_from));
+ CHECK(to->findInt32(kKeyHeight, &height_to));
+
+ if (height_from != height_to) {
+ return true;
+ }
+ } else if (!strcasecmp(mime_from, MEDIA_MIMETYPE_AUDIO_RAW)) {
+ int32_t numChannels_from, numChannels_to;
+ CHECK(from->findInt32(kKeyChannelCount, &numChannels_from));
+ CHECK(to->findInt32(kKeyChannelCount, &numChannels_to));
+
+ if (numChannels_from != numChannels_to) {
+ return true;
+ }
+
+ int32_t sampleRate_from, sampleRate_to;
+ CHECK(from->findInt32(kKeySampleRate, &sampleRate_from));
+ CHECK(to->findInt32(kKeySampleRate, &sampleRate_to));
+
+ if (sampleRate_from != sampleRate_to) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) {
switch (cmd) {
case OMX_CommandStateSet:
@@ -1083,6 +1145,15 @@
if (mState == RECONFIGURING) {
CHECK_EQ(portIndex, kPortIndexOutput);
+ sp<MetaData> oldOutputFormat = mOutputFormat;
+ initOutputFormat(mSource->getFormat());
+
+ // Don't notify clients if the output port settings change
+ // wasn't of importance to them, i.e. it may be that just the
+ // number of buffers has changed and nothing else.
+ mOutputPortSettingsHaveChanged =
+ formatHasNotablyChanged(oldOutputFormat, mOutputFormat);
+
enablePortAsync(portIndex);
status_t err = allocateBuffersOnPort(portIndex);
@@ -1137,7 +1208,7 @@
mPortStatus[kPortIndexOutput] = SHUTTING_DOWN;
status_t err =
- mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
+ mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
CHECK_EQ(err, OK);
}
} else {
@@ -1177,7 +1248,7 @@
{
CODEC_LOGV("Now Idle.");
if (mState == LOADED_TO_IDLE) {
- status_t err = mOMX->send_command(
+ status_t err = mOMX->sendCommand(
mNode, OMX_CommandStateSet, OMX_StateExecuting);
CHECK_EQ(err, OK);
@@ -1194,7 +1265,7 @@
countBuffersWeOwn(mPortBuffers[kPortIndexOutput]),
mPortBuffers[kPortIndexOutput].size());
- status_t err = mOMX->send_command(
+ status_t err = mOMX->sendCommand(
mNode, OMX_CommandStateSet, OMX_StateLoaded);
CHECK_EQ(err, OK);
@@ -1277,7 +1348,7 @@
CODEC_LOGV("freeing buffer %p on port %ld", info->mBuffer, portIndex);
status_t err =
- mOMX->free_buffer(mNode, portIndex, info->mBuffer);
+ mOMX->freeBuffer(mNode, portIndex, info->mBuffer);
if (err != OK) {
stickyErr = err;
@@ -1337,7 +1408,7 @@
}
status_t err =
- mOMX->send_command(mNode, OMX_CommandFlush, portIndex);
+ mOMX->sendCommand(mNode, OMX_CommandFlush, portIndex);
CHECK_EQ(err, OK);
return true;
@@ -1350,7 +1421,7 @@
mPortStatus[portIndex] = DISABLING;
status_t err =
- mOMX->send_command(mNode, OMX_CommandPortDisable, portIndex);
+ mOMX->sendCommand(mNode, OMX_CommandPortDisable, portIndex);
CHECK_EQ(err, OK);
freeBuffersOnPort(portIndex, true);
@@ -1363,7 +1434,7 @@
mPortStatus[portIndex] = ENABLING;
status_t err =
- mOMX->send_command(mNode, OMX_CommandPortEnable, portIndex);
+ mOMX->sendCommand(mNode, OMX_CommandPortEnable, portIndex);
CHECK_EQ(err, OK);
}
@@ -1415,10 +1486,11 @@
memcpy(info->mMem->pointer(), specific->mData, specific->mSize);
}
- mOMX->empty_buffer(
+ status_t err = mOMX->emptyBuffer(
mNode, info->mBuffer, 0, size,
OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG,
0);
+ CHECK_EQ(err, OK);
info->mOwnedByComponent = true;
@@ -1439,7 +1511,7 @@
}
OMX_U32 flags = OMX_BUFFERFLAG_ENDOFFRAME;
- OMX_TICKS timestamp = 0;
+ OMX_TICKS timestampUs = 0;
size_t srcLength = 0;
if (err != OK) {
@@ -1459,28 +1531,29 @@
(const uint8_t *)srcBuffer->data() + srcBuffer->range_offset(),
srcLength);
- int32_t units, scale;
- if (srcBuffer->meta_data()->findInt32(kKeyTimeUnits, &units)
- && srcBuffer->meta_data()->findInt32(kKeyTimeScale, &scale)) {
- timestamp = ((OMX_TICKS)units * 1000000) / scale;
-
- CODEC_LOGV("Calling empty_buffer on buffer %p (length %d)",
+ if (srcBuffer->meta_data()->findInt64(kKeyTime, ×tampUs)) {
+ CODEC_LOGV("Calling emptyBuffer on buffer %p (length %d)",
info->mBuffer, srcLength);
- CODEC_LOGV("Calling empty_buffer with timestamp %lld us (%.2f secs)",
- timestamp, timestamp / 1E6);
+ CODEC_LOGV("Calling emptyBuffer with timestamp %lld us (%.2f secs)",
+ timestampUs, timestampUs / 1E6);
}
}
- mOMX->empty_buffer(
- mNode, info->mBuffer, 0, srcLength,
- flags, timestamp);
-
- info->mOwnedByComponent = true;
-
if (srcBuffer != NULL) {
srcBuffer->release();
srcBuffer = NULL;
}
+
+ err = mOMX->emptyBuffer(
+ mNode, info->mBuffer, 0, srcLength,
+ flags, timestampUs);
+
+ if (err != OK) {
+ setState(ERROR);
+ return;
+ }
+
+ info->mOwnedByComponent = true;
}
void OMXCodec::fillOutputBuffer(BufferInfo *info) {
@@ -1493,7 +1566,8 @@
}
CODEC_LOGV("Calling fill_buffer on buffer %p", info->mBuffer);
- mOMX->fill_buffer(mNode, info->mBuffer);
+ status_t err = mOMX->fillBuffer(mNode, info->mBuffer);
+ CHECK_EQ(err, OK);
info->mOwnedByComponent = true;
}
@@ -1537,7 +1611,7 @@
InitOMXParams(&pcmParams);
pcmParams.nPortIndex = portIndex;
- status_t err = mOMX->get_parameter(
+ status_t err = mOMX->getParameter(
mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams));
CHECK_EQ(err, OK);
@@ -1558,7 +1632,7 @@
pcmParams.eChannelMapping[1] = OMX_AUDIO_ChannelRF;
}
- err = mOMX->set_parameter(
+ err = mOMX->setParameter(
mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams));
CHECK_EQ(err, OK);
@@ -1571,14 +1645,14 @@
def.nPortIndex = kPortIndexInput;
status_t err =
- mOMX->get_parameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
+ mOMX->getParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
CHECK_EQ(err, OK);
def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
def.eAMRBandMode = OMX_AUDIO_AMRBandModeNB0;
- err = mOMX->set_parameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
+ err = mOMX->setParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
CHECK_EQ(err, OK);
}
@@ -1602,14 +1676,14 @@
def.nPortIndex = kPortIndexInput;
status_t err =
- mOMX->get_parameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
+ mOMX->getParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
CHECK_EQ(err, OK);
def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
def.eAMRBandMode = OMX_AUDIO_AMRBandModeWB0;
- err = mOMX->set_parameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
+ err = mOMX->setParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
CHECK_EQ(err, OK);
}
@@ -1634,7 +1708,7 @@
InitOMXParams(&profile);
profile.nPortIndex = kPortIndexInput;
- status_t err = mOMX->get_parameter(
+ status_t err = mOMX->getParameter(
mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
CHECK_EQ(err, OK);
@@ -1642,7 +1716,7 @@
profile.nSampleRate = sampleRate;
profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS;
- err = mOMX->set_parameter(
+ err = mOMX->setParameter(
mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
CHECK_EQ(err, OK);
}
@@ -1666,7 +1740,7 @@
InitOMXParams(&def);
def.nPortIndex = kPortIndexOutput;
- status_t err = mOMX->get_parameter(
+ status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
@@ -1715,7 +1789,7 @@
def.nBufferCountActual = def.nBufferCountMin;
- err = mOMX->set_parameter(
+ err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
}
@@ -1726,7 +1800,7 @@
InitOMXParams(&def);
def.nPortIndex = kPortIndexInput;
- status_t err = mOMX->get_parameter(
+ status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
@@ -1740,7 +1814,7 @@
def.nBufferSize = compressedSize;
def.nBufferCountActual = def.nBufferCountMin;
- err = mOMX->set_parameter(
+ err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
}
@@ -1784,6 +1858,7 @@
mInitialBufferSubmit = true;
mSignalledEOS = false;
mNoMoreOutputData = false;
+ mOutputPortSettingsHaveChanged = false;
mSeekTimeUs = -1;
mFilledBuffers.clear();
@@ -1830,7 +1905,7 @@
mPortStatus[kPortIndexOutput] = SHUTTING_DOWN;
status_t err =
- mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
+ mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
CHECK_EQ(err, OK);
}
@@ -1854,6 +1929,8 @@
}
sp<MetaData> OMXCodec::getFormat() {
+ Mutex::Autolock autoLock(mLock);
+
return mOutputFormat;
}
@@ -1917,6 +1994,12 @@
return ERROR_END_OF_STREAM;
}
+ if (mOutputPortSettingsHaveChanged) {
+ mOutputPortSettingsHaveChanged = false;
+
+ return INFO_FORMAT_CHANGED;
+ }
+
size_t index = *mFilledBuffers.begin();
mFilledBuffers.erase(mFilledBuffers.begin());
@@ -2017,8 +2100,6 @@
size_t numNames = sizeof(kNames) / sizeof(kNames[0]);
- static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
-
if (type == OMX_QCOM_COLOR_FormatYVU420SemiPlanar) {
return "OMX_QCOM_COLOR_FormatYVU420SemiPlanar";
} else if (type < 0 || (size_t)type >= numNames) {
@@ -2162,7 +2243,7 @@
InitOMXParams(&def);
def.nPortIndex = portIndex;
- status_t err = mOMX->get_parameter(
+ status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
@@ -2228,7 +2309,7 @@
InitOMXParams(¶ms);
params.nPortIndex = portIndex;
- err = mOMX->get_parameter(
+ err = mOMX->getParameter(
mNode, OMX_IndexParamAudioPcm, ¶ms, sizeof(params));
CHECK_EQ(err, OK);
@@ -2247,7 +2328,7 @@
InitOMXParams(&amr);
amr.nPortIndex = portIndex;
- err = mOMX->get_parameter(
+ err = mOMX->getParameter(
mNode, OMX_IndexParamAudioAmr, &amr, sizeof(amr));
CHECK_EQ(err, OK);
@@ -2279,7 +2360,7 @@
InitOMXParams(&def);
def.nPortIndex = kPortIndexOutput;
- status_t err = mOMX->get_parameter(
+ status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
@@ -2305,7 +2386,7 @@
InitOMXParams(¶ms);
params.nPortIndex = kPortIndexOutput;
- err = mOMX->get_parameter(
+ err = mOMX->getParameter(
mNode, OMX_IndexParamAudioPcm, ¶ms, sizeof(params));
CHECK_EQ(err, OK);
@@ -2337,7 +2418,7 @@
InitOMXParams(&amr);
amr.nPortIndex = kPortIndexOutput;
- err = mOMX->get_parameter(
+ err = mOMX->getParameter(
mNode, OMX_IndexParamAudioAmr, &amr, sizeof(amr));
CHECK_EQ(err, OK);
@@ -2434,8 +2515,9 @@
return OK;
}
+ sp<OMXCodecObserver> observer = new OMXCodecObserver;
IOMX::node_id node;
- status_t err = omx->allocate_node(componentName, &node);
+ status_t err = omx->allocateNode(componentName, observer, &node);
if (err != OK) {
continue;
@@ -2453,7 +2535,7 @@
param.nPortIndex = queryDecoders ? 0 : 1;
for (param.nProfileIndex = 0;; ++param.nProfileIndex) {
- err = omx->get_parameter(
+ err = omx->getParameter(
node, OMX_IndexParamVideoProfileLevelQuerySupported,
¶m, sizeof(param));
@@ -2468,7 +2550,7 @@
caps->mProfileLevels.push(profileLevel);
}
- CHECK_EQ(omx->free_node(node), OK);
+ CHECK_EQ(omx->freeNode(node), OK);
}
}
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index 8efa7c7..5c5bb4d 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -17,11 +17,12 @@
#define LOG_TAG "SampleTable"
#include <utils/Log.h>
+#include "include/SampleTable.h"
+
#include <arpa/inet.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/SampleTable.h>
#include <media/stagefright/Utils.h>
namespace android {
@@ -54,7 +55,7 @@
}
status_t SampleTable::setChunkOffsetParams(
- uint32_t type, off_t data_offset, off_t data_size) {
+ uint32_t type, off_t data_offset, size_t data_size) {
if (mChunkOffsetOffset >= 0) {
return ERROR_MALFORMED;
}
@@ -95,7 +96,7 @@
}
status_t SampleTable::setSampleToChunkParams(
- off_t data_offset, off_t data_size) {
+ off_t data_offset, size_t data_size) {
if (mSampleToChunkOffset >= 0) {
return ERROR_MALFORMED;
}
@@ -127,7 +128,7 @@
}
status_t SampleTable::setSampleSizeParams(
- uint32_t type, off_t data_offset, off_t data_size) {
+ uint32_t type, off_t data_offset, size_t data_size) {
if (mSampleSizeOffset >= 0) {
return ERROR_MALFORMED;
}
@@ -187,7 +188,7 @@
}
status_t SampleTable::setTimeToSampleParams(
- off_t data_offset, off_t data_size) {
+ off_t data_offset, size_t data_size) {
if (mTimeToSample != NULL || data_size < 8) {
return ERROR_MALFORMED;
}
@@ -219,7 +220,7 @@
return OK;
}
-status_t SampleTable::setSyncSampleParams(off_t data_offset, off_t data_size) {
+status_t SampleTable::setSyncSampleParams(off_t data_offset, size_t data_size) {
if (mSyncSampleOffset >= 0 || data_size < 8) {
return ERROR_MALFORMED;
}
diff --git a/media/libstagefright/ShoutcastSource.cpp b/media/libstagefright/ShoutcastSource.cpp
index 8e8f4fa..ec25430 100644
--- a/media/libstagefright/ShoutcastSource.cpp
+++ b/media/libstagefright/ShoutcastSource.cpp
@@ -14,16 +14,17 @@
* limitations under the License.
*/
+#include "include/stagefright_string.h"
+#include "include/HTTPStream.h"
+
#include <stdlib.h>
-#include <media/stagefright/HTTPStream.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/ShoutcastSource.h>
-#include <media/stagefright/string.h>
namespace android {
diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
index 3d85f75..dd8005c 100644
--- a/media/libstagefright/TimedEventQueue.cpp
+++ b/media/libstagefright/TimedEventQueue.cpp
@@ -22,10 +22,11 @@
#define LOG_TAG "TimedEventQueue"
#include <utils/Log.h>
+#include "include/TimedEventQueue.h"
+
#include <sys/time.h>
#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/TimedEventQueue.h>
namespace android {
diff --git a/include/media/stagefright/ESDS.h b/media/libstagefright/include/ESDS.h
similarity index 100%
rename from include/media/stagefright/ESDS.h
rename to media/libstagefright/include/ESDS.h
diff --git a/include/media/stagefright/HTTPStream.h b/media/libstagefright/include/HTTPStream.h
similarity index 97%
rename from include/media/stagefright/HTTPStream.h
rename to media/libstagefright/include/HTTPStream.h
index 3d0d67a..43ef614 100644
--- a/include/media/stagefright/HTTPStream.h
+++ b/media/libstagefright/include/HTTPStream.h
@@ -18,10 +18,11 @@
#define HTTP_STREAM_H_
+#include "stagefright_string.h"
+
#include <sys/types.h>
#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/string.h>
#include <utils/KeyedVector.h>
namespace android {
diff --git a/media/libstagefright/omx/OMX.h b/media/libstagefright/include/OMX.h
similarity index 61%
rename from media/libstagefright/omx/OMX.h
rename to media/libstagefright/include/OMX.h
index 6325f79..a4b62b2 100644
--- a/media/libstagefright/omx/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -19,66 +19,67 @@
#include <media/IOMX.h>
#include <utils/threads.h>
+#include <utils/KeyedVector.h>
namespace android {
-class NodeMeta;
+class OMXNodeInstance;
-class OMX : public BnOMX {
+class OMX : public BnOMX,
+ public IBinder::DeathRecipient {
public:
OMX();
- virtual status_t list_nodes(List<String8> *list);
+ virtual status_t listNodes(List<String8> *list);
- virtual status_t allocate_node(const char *name, node_id *node);
- virtual status_t free_node(node_id node);
+ virtual status_t allocateNode(
+ const char *name, const sp<IOMXObserver> &observer, node_id *node);
- virtual status_t send_command(
+ virtual status_t freeNode(node_id node);
+
+ virtual status_t sendCommand(
node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param);
- virtual status_t get_parameter(
+ virtual status_t getParameter(
node_id node, OMX_INDEXTYPE index,
void *params, size_t size);
- virtual status_t set_parameter(
+ virtual status_t setParameter(
node_id node, OMX_INDEXTYPE index,
const void *params, size_t size);
- virtual status_t get_config(
+ virtual status_t getConfig(
node_id node, OMX_INDEXTYPE index,
void *params, size_t size);
- virtual status_t set_config(
+ virtual status_t setConfig(
node_id node, OMX_INDEXTYPE index,
const void *params, size_t size);
- virtual status_t use_buffer(
+ virtual status_t useBuffer(
node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
buffer_id *buffer);
- virtual status_t allocate_buffer(
+ virtual status_t allocateBuffer(
node_id node, OMX_U32 port_index, size_t size,
buffer_id *buffer);
- virtual status_t allocate_buffer_with_backup(
+ virtual status_t allocateBufferWithBackup(
node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
buffer_id *buffer);
- virtual status_t free_buffer(
+ virtual status_t freeBuffer(
node_id node, OMX_U32 port_index, buffer_id buffer);
- virtual status_t observe_node(
- node_id node, const sp<IOMXObserver> &observer);
+ virtual status_t fillBuffer(node_id node, buffer_id buffer);
- virtual void fill_buffer(node_id node, buffer_id buffer);
-
- virtual void empty_buffer(
+ virtual status_t emptyBuffer(
node_id node,
buffer_id buffer,
OMX_U32 range_offset, OMX_U32 range_length,
OMX_U32 flags, OMX_TICKS timestamp);
- virtual status_t get_extension_index(
+ virtual status_t getExtensionIndex(
node_id node,
const char *parameter_name,
OMX_INDEXTYPE *index);
@@ -90,44 +91,38 @@
size_t encodedWidth, size_t encodedHeight,
size_t displayWidth, size_t displayHeight);
-private:
- static OMX_CALLBACKTYPE kCallbacks;
-
- Mutex mLock;
-
- struct CallbackDispatcher;
- sp<CallbackDispatcher> mDispatcher;
-
- static OMX_ERRORTYPE OnEvent(
- OMX_IN OMX_HANDLETYPE hComponent,
- OMX_IN OMX_PTR pAppData,
- OMX_IN OMX_EVENTTYPE eEvent,
- OMX_IN OMX_U32 nData1,
- OMX_IN OMX_U32 nData2,
- OMX_IN OMX_PTR pEventData);
-
- static OMX_ERRORTYPE OnEmptyBufferDone(
- OMX_IN OMX_HANDLETYPE hComponent,
- OMX_IN OMX_PTR pAppData,
- OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
-
- static OMX_ERRORTYPE OnFillBufferDone(
- OMX_IN OMX_HANDLETYPE hComponent,
- OMX_IN OMX_PTR pAppData,
- OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
+ virtual void binderDied(const wp<IBinder> &the_late_who);
OMX_ERRORTYPE OnEvent(
- NodeMeta *meta,
+ node_id node,
OMX_IN OMX_EVENTTYPE eEvent,
OMX_IN OMX_U32 nData1,
OMX_IN OMX_U32 nData2,
OMX_IN OMX_PTR pEventData);
OMX_ERRORTYPE OnEmptyBufferDone(
- NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
+ node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
OMX_ERRORTYPE OnFillBufferDone(
- NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
+ node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
+
+ void invalidateNodeID(node_id node);
+
+private:
+ Mutex mLock;
+
+ struct CallbackDispatcher;
+ sp<CallbackDispatcher> mDispatcher;
+
+ int32_t mNodeCounter;
+
+ KeyedVector<wp<IBinder>, OMXNodeInstance *> mLiveNodes;
+ KeyedVector<node_id, OMXNodeInstance *> mNodeIDToInstance;
+
+ node_id makeNodeID(OMXNodeInstance *instance);
+ OMXNodeInstance *findInstance(node_id node);
+
+ void invalidateNodeID_l(node_id node);
OMX(const OMX &);
OMX &operator=(const OMX &);
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
new file mode 100644
index 0000000..181742e
--- /dev/null
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OMX_NODE_INSTANCE_H_
+
+#define OMX_NODE_INSTANCE_H_
+
+#include "OMX.h"
+
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class IOMXObserver;
+
+struct OMXNodeInstance {
+ OMXNodeInstance(
+ OMX *owner, const sp<IOMXObserver> &observer);
+
+ void setHandle(OMX::node_id node_id, OMX_HANDLETYPE handle);
+
+ OMX *owner();
+ sp<IOMXObserver> observer();
+ OMX::node_id nodeID();
+
+ status_t freeNode();
+
+ status_t sendCommand(OMX_COMMANDTYPE cmd, OMX_S32 param);
+ status_t getParameter(OMX_INDEXTYPE index, void *params, size_t size);
+
+ status_t setParameter(
+ OMX_INDEXTYPE index, const void *params, size_t size);
+
+ status_t getConfig(OMX_INDEXTYPE index, void *params, size_t size);
+ status_t setConfig(OMX_INDEXTYPE index, const void *params, size_t size);
+
+ status_t useBuffer(
+ OMX_U32 portIndex, const sp<IMemory> ¶ms,
+ OMX::buffer_id *buffer);
+
+ status_t allocateBuffer(
+ OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer);
+
+ status_t allocateBufferWithBackup(
+ OMX_U32 portIndex, const sp<IMemory> ¶ms,
+ OMX::buffer_id *buffer);
+
+ status_t freeBuffer(OMX_U32 portIndex, OMX::buffer_id buffer);
+
+ status_t fillBuffer(OMX::buffer_id buffer);
+
+ status_t emptyBuffer(
+ OMX::buffer_id buffer,
+ OMX_U32 rangeOffset, OMX_U32 rangeLength,
+ OMX_U32 flags, OMX_TICKS timestamp);
+
+ status_t getExtensionIndex(
+ const char *parameterName, OMX_INDEXTYPE *index);
+
+ void onMessage(const omx_message &msg);
+ void onObserverDied();
+ void onGetHandleFailed();
+
+ static OMX_CALLBACKTYPE kCallbacks;
+
+private:
+ Mutex mLock;
+
+ OMX *mOwner;
+ OMX::node_id mNodeID;
+ OMX_HANDLETYPE mHandle;
+ sp<IOMXObserver> mObserver;
+
+ ~OMXNodeInstance();
+
+ static OMX_ERRORTYPE OnEvent(
+ OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_EVENTTYPE eEvent,
+ OMX_IN OMX_U32 nData1,
+ OMX_IN OMX_U32 nData2,
+ OMX_IN OMX_PTR pEventData);
+
+ static OMX_ERRORTYPE OnEmptyBufferDone(
+ OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
+
+ static OMX_ERRORTYPE OnFillBufferDone(
+ OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
+
+ OMXNodeInstance(const OMXNodeInstance &);
+ OMXNodeInstance &operator=(const OMXNodeInstance &);
+};
+
+} // namespace android
+
+#endif // OMX_NODE_INSTANCE_H_
+
diff --git a/include/media/stagefright/QComHardwareRenderer.h b/media/libstagefright/include/QComHardwareRenderer.h
similarity index 100%
rename from include/media/stagefright/QComHardwareRenderer.h
rename to media/libstagefright/include/QComHardwareRenderer.h
diff --git a/include/media/stagefright/SampleTable.h b/media/libstagefright/include/SampleTable.h
similarity index 88%
rename from include/media/stagefright/SampleTable.h
rename to media/libstagefright/include/SampleTable.h
index 808d142..34a0649 100644
--- a/include/media/stagefright/SampleTable.h
+++ b/media/libstagefright/include/SampleTable.h
@@ -35,17 +35,17 @@
// type can be 'stco' or 'co64'.
status_t setChunkOffsetParams(
- uint32_t type, off_t data_offset, off_t data_size);
+ uint32_t type, off_t data_offset, size_t data_size);
- status_t setSampleToChunkParams(off_t data_offset, off_t data_size);
+ status_t setSampleToChunkParams(off_t data_offset, size_t data_size);
// type can be 'stsz' or 'stz2'.
status_t setSampleSizeParams(
- uint32_t type, off_t data_offset, off_t data_size);
+ uint32_t type, off_t data_offset, size_t data_size);
- status_t setTimeToSampleParams(off_t data_offset, off_t data_size);
+ status_t setTimeToSampleParams(off_t data_offset, size_t data_size);
- status_t setSyncSampleParams(off_t data_offset, off_t data_size);
+ status_t setSyncSampleParams(off_t data_offset, size_t data_size);
////////////////////////////////////////////////////////////////////////////
diff --git a/include/media/stagefright/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h
similarity index 86%
rename from include/media/stagefright/SoftwareRenderer.h
rename to media/libstagefright/include/SoftwareRenderer.h
index 1545493..9eed089 100644
--- a/include/media/stagefright/SoftwareRenderer.h
+++ b/media/libstagefright/include/SoftwareRenderer.h
@@ -18,7 +18,7 @@
#define SOFTWARE_RENDERER_H_
-#include <OMX_Video.h>
+#include <media/stagefright/ColorConverter.h>
#include <media/stagefright/VideoRenderer.h>
#include <utils/RefBase.h>
@@ -41,13 +41,8 @@
const void *data, size_t size, void *platformPrivate);
private:
- uint8_t *initClip();
-
- void renderCbYCrY(const void *data, size_t size);
- void renderYUV420Planar(const void *data, size_t size);
- void renderQCOMYUV420SemiPlanar(const void *data, size_t size);
-
OMX_COLOR_FORMATTYPE mColorFormat;
+ ColorConverter mConverter;
sp<ISurface> mISurface;
size_t mDisplayWidth, mDisplayHeight;
size_t mDecodedWidth, mDecodedHeight;
@@ -55,8 +50,6 @@
sp<MemoryHeapBase> mMemoryHeap;
int mIndex;
- uint8_t *mClip;
-
SoftwareRenderer(const SoftwareRenderer &);
SoftwareRenderer &operator=(const SoftwareRenderer &);
};
diff --git a/include/media/stagefright/TIHardwareRenderer.h b/media/libstagefright/include/TIHardwareRenderer.h
similarity index 100%
rename from include/media/stagefright/TIHardwareRenderer.h
rename to media/libstagefright/include/TIHardwareRenderer.h
diff --git a/include/media/stagefright/TimedEventQueue.h b/media/libstagefright/include/TimedEventQueue.h
similarity index 100%
rename from include/media/stagefright/TimedEventQueue.h
rename to media/libstagefright/include/TimedEventQueue.h
diff --git a/include/media/stagefright/string.h b/media/libstagefright/include/stagefright_string.h
similarity index 100%
rename from include/media/stagefright/string.h
rename to media/libstagefright/include/stagefright_string.h
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
index 4cadccd..edbc04e 100644
--- a/media/libstagefright/omx/Android.mk
+++ b/media/libstagefright/omx/Android.mk
@@ -10,7 +10,9 @@
LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)
LOCAL_SRC_FILES:= \
+ ColorConverter.cpp \
OMX.cpp \
+ OMXNodeInstance.cpp \
QComHardwareRenderer.cpp \
SoftwareRenderer.cpp \
TIHardwareRenderer.cpp
diff --git a/media/libstagefright/omx/ColorConverter.cpp b/media/libstagefright/omx/ColorConverter.cpp
new file mode 100644
index 0000000..e74782f
--- /dev/null
+++ b/media/libstagefright/omx/ColorConverter.cpp
@@ -0,0 +1,297 @@
+/*
+ * 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 <media/stagefright/ColorConverter.h>
+#include <media/stagefright/MediaDebug.h>
+
+namespace android {
+
+static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
+
+ColorConverter::ColorConverter(
+ OMX_COLOR_FORMATTYPE from, OMX_COLOR_FORMATTYPE to)
+ : mSrcFormat(from),
+ mDstFormat(to),
+ mClip(NULL) {
+}
+
+ColorConverter::~ColorConverter() {
+ delete[] mClip;
+ mClip = NULL;
+}
+
+bool ColorConverter::isValid() const {
+ if (mDstFormat != OMX_COLOR_Format16bitRGB565) {
+ return false;
+ }
+
+ switch (mSrcFormat) {
+ case OMX_COLOR_FormatYUV420Planar:
+ case OMX_COLOR_FormatCbYCrY:
+ case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+void ColorConverter::convert(
+ size_t width, size_t height,
+ const void *srcBits, size_t srcSkip,
+ void *dstBits, size_t dstSkip) {
+ CHECK_EQ(mDstFormat, OMX_COLOR_Format16bitRGB565);
+
+ switch (mSrcFormat) {
+ case OMX_COLOR_FormatYUV420Planar:
+ convertYUV420Planar(
+ width, height, srcBits, srcSkip, dstBits, dstSkip);
+ break;
+
+ case OMX_COLOR_FormatCbYCrY:
+ convertCbYCrY(
+ width, height, srcBits, srcSkip, dstBits, dstSkip);
+ break;
+
+ case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
+ convertQCOMYUV420SemiPlanar(
+ width, height, srcBits, srcSkip, dstBits, dstSkip);
+ break;
+
+ default:
+ {
+ CHECK(!"Should not be here. Unknown color conversion.");
+ break;
+ }
+ }
+}
+
+void ColorConverter::convertCbYCrY(
+ size_t width, size_t height,
+ const void *srcBits, size_t srcSkip,
+ void *dstBits, size_t dstSkip) {
+ CHECK_EQ(srcSkip, 0); // Doesn't really make sense for YUV formats.
+ CHECK(dstSkip >= width * 2);
+ CHECK((dstSkip & 3) == 0);
+
+ uint8_t *kAdjustedClip = initClip();
+
+ uint32_t *dst_ptr = (uint32_t *)dstBits;
+
+ const uint8_t *src = (const uint8_t *)srcBits;
+
+ for (size_t y = 0; y < height; ++y) {
+ for (size_t x = 0; x < width; x += 2) {
+ signed y1 = (signed)src[2 * x + 1] - 16;
+ signed y2 = (signed)src[2 * x + 3] - 16;
+ signed u = (signed)src[2 * x] - 128;
+ signed v = (signed)src[2 * x + 2] - 128;
+
+ signed u_b = u * 517;
+ signed u_g = -u * 100;
+ signed v_g = -v * 208;
+ signed v_r = v * 409;
+
+ signed tmp1 = y1 * 298;
+ signed b1 = (tmp1 + u_b) / 256;
+ signed g1 = (tmp1 + v_g + u_g) / 256;
+ signed r1 = (tmp1 + v_r) / 256;
+
+ signed tmp2 = y2 * 298;
+ signed b2 = (tmp2 + u_b) / 256;
+ signed g2 = (tmp2 + v_g + u_g) / 256;
+ signed r2 = (tmp2 + v_r) / 256;
+
+ uint32_t rgb1 =
+ ((kAdjustedClip[r1] >> 3) << 11)
+ | ((kAdjustedClip[g1] >> 2) << 5)
+ | (kAdjustedClip[b1] >> 3);
+
+ uint32_t rgb2 =
+ ((kAdjustedClip[r2] >> 3) << 11)
+ | ((kAdjustedClip[g2] >> 2) << 5)
+ | (kAdjustedClip[b2] >> 3);
+
+ dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+ }
+
+ src += width * 2;
+ dst_ptr += dstSkip / 4;
+ }
+}
+
+void ColorConverter::convertYUV420Planar(
+ size_t width, size_t height,
+ const void *srcBits, size_t srcSkip,
+ void *dstBits, size_t dstSkip) {
+ CHECK_EQ(srcSkip, 0); // Doesn't really make sense for YUV formats.
+ CHECK(dstSkip >= width * 2);
+ CHECK((dstSkip & 3) == 0);
+
+ uint8_t *kAdjustedClip = initClip();
+
+ uint32_t *dst_ptr = (uint32_t *)dstBits;
+ const uint8_t *src_y = (const uint8_t *)srcBits;
+
+ const uint8_t *src_u =
+ (const uint8_t *)src_y + width * height;
+
+ const uint8_t *src_v =
+ (const uint8_t *)src_u + (width / 2) * (height / 2);
+
+ for (size_t y = 0; y < height; ++y) {
+ for (size_t x = 0; x < width; x += 2) {
+ // B = 1.164 * (Y - 16) + 2.018 * (U - 128)
+ // G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128)
+ // R = 1.164 * (Y - 16) + 1.596 * (V - 128)
+
+ // B = 298/256 * (Y - 16) + 517/256 * (U - 128)
+ // G = .................. - 208/256 * (V - 128) - 100/256 * (U - 128)
+ // R = .................. + 409/256 * (V - 128)
+
+ // min_B = (298 * (- 16) + 517 * (- 128)) / 256 = -277
+ // min_G = (298 * (- 16) - 208 * (255 - 128) - 100 * (255 - 128)) / 256 = -172
+ // min_R = (298 * (- 16) + 409 * (- 128)) / 256 = -223
+
+ // max_B = (298 * (255 - 16) + 517 * (255 - 128)) / 256 = 534
+ // max_G = (298 * (255 - 16) - 208 * (- 128) - 100 * (- 128)) / 256 = 432
+ // max_R = (298 * (255 - 16) + 409 * (255 - 128)) / 256 = 481
+
+ // clip range -278 .. 535
+
+ signed y1 = (signed)src_y[x] - 16;
+ signed y2 = (signed)src_y[x + 1] - 16;
+
+ signed u = (signed)src_u[x / 2] - 128;
+ signed v = (signed)src_v[x / 2] - 128;
+
+ signed u_b = u * 517;
+ signed u_g = -u * 100;
+ signed v_g = -v * 208;
+ signed v_r = v * 409;
+
+ signed tmp1 = y1 * 298;
+ signed b1 = (tmp1 + u_b) / 256;
+ signed g1 = (tmp1 + v_g + u_g) / 256;
+ signed r1 = (tmp1 + v_r) / 256;
+
+ signed tmp2 = y2 * 298;
+ signed b2 = (tmp2 + u_b) / 256;
+ signed g2 = (tmp2 + v_g + u_g) / 256;
+ signed r2 = (tmp2 + v_r) / 256;
+
+ uint32_t rgb1 =
+ ((kAdjustedClip[r1] >> 3) << 11)
+ | ((kAdjustedClip[g1] >> 2) << 5)
+ | (kAdjustedClip[b1] >> 3);
+
+ uint32_t rgb2 =
+ ((kAdjustedClip[r2] >> 3) << 11)
+ | ((kAdjustedClip[g2] >> 2) << 5)
+ | (kAdjustedClip[b2] >> 3);
+
+ dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+ }
+
+ src_y += width;
+
+ if (y & 1) {
+ src_u += width / 2;
+ src_v += width / 2;
+ }
+
+ dst_ptr += dstSkip / 4;
+ }
+}
+
+void ColorConverter::convertQCOMYUV420SemiPlanar(
+ size_t width, size_t height,
+ const void *srcBits, size_t srcSkip,
+ void *dstBits, size_t dstSkip) {
+ CHECK_EQ(srcSkip, 0); // Doesn't really make sense for YUV formats.
+ CHECK(dstSkip >= width * 2);
+ CHECK((dstSkip & 3) == 0);
+
+ uint8_t *kAdjustedClip = initClip();
+
+ uint32_t *dst_ptr = (uint32_t *)dstBits;
+ const uint8_t *src_y = (const uint8_t *)srcBits;
+
+ const uint8_t *src_u =
+ (const uint8_t *)src_y + width * height;
+
+ for (size_t y = 0; y < height; ++y) {
+ for (size_t x = 0; x < width; x += 2) {
+ signed y1 = (signed)src_y[x] - 16;
+ signed y2 = (signed)src_y[x + 1] - 16;
+
+ signed u = (signed)src_u[x & ~1] - 128;
+ signed v = (signed)src_u[(x & ~1) + 1] - 128;
+
+ signed u_b = u * 517;
+ signed u_g = -u * 100;
+ signed v_g = -v * 208;
+ signed v_r = v * 409;
+
+ signed tmp1 = y1 * 298;
+ signed b1 = (tmp1 + u_b) / 256;
+ signed g1 = (tmp1 + v_g + u_g) / 256;
+ signed r1 = (tmp1 + v_r) / 256;
+
+ signed tmp2 = y2 * 298;
+ signed b2 = (tmp2 + u_b) / 256;
+ signed g2 = (tmp2 + v_g + u_g) / 256;
+ signed r2 = (tmp2 + v_r) / 256;
+
+ uint32_t rgb1 =
+ ((kAdjustedClip[b1] >> 3) << 11)
+ | ((kAdjustedClip[g1] >> 2) << 5)
+ | (kAdjustedClip[r1] >> 3);
+
+ uint32_t rgb2 =
+ ((kAdjustedClip[b2] >> 3) << 11)
+ | ((kAdjustedClip[g2] >> 2) << 5)
+ | (kAdjustedClip[r2] >> 3);
+
+ dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+ }
+
+ src_y += width;
+
+ if (y & 1) {
+ src_u += width;
+ }
+
+ dst_ptr += dstSkip / 4;
+ }
+}
+
+uint8_t *ColorConverter::initClip() {
+ static const signed kClipMin = -278;
+ static const signed kClipMax = 535;
+
+ if (mClip == NULL) {
+ mClip = new uint8_t[kClipMax - kClipMin + 1];
+
+ for (signed i = kClipMin; i <= kClipMax; ++i) {
+ mClip[i - kClipMin] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i;
+ }
+ }
+
+ return &mClip[-kClipMin];
+}
+
+} // namespace android
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 8b83dd6..3800a26 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -18,65 +18,28 @@
#define LOG_TAG "OMX"
#include <utils/Log.h>
-#include <sys/socket.h>
-
-#include "OMX.h"
+#include "../include/OMX.h"
#include "OMXRenderer.h"
#include "pv_omxcore.h"
+#include "../include/OMXNodeInstance.h"
+#include "../include/QComHardwareRenderer.h"
+#include "../include/SoftwareRenderer.h"
+#include "../include/TIHardwareRenderer.h"
+
#include <binder/IMemory.h>
#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/QComHardwareRenderer.h>
-#include <media/stagefright/SoftwareRenderer.h>
-#include <media/stagefright/TIHardwareRenderer.h>
#include <media/stagefright/VideoRenderer.h>
#include <OMX_Component.h>
namespace android {
-class NodeMeta {
-public:
- NodeMeta(OMX *owner)
- : mOwner(owner),
- mHandle(NULL) {
- }
-
- OMX *owner() const {
- return mOwner;
- }
-
- void setHandle(OMX_HANDLETYPE handle) {
- CHECK_EQ(mHandle, NULL);
- mHandle = handle;
- }
-
- OMX_HANDLETYPE handle() const {
- return mHandle;
- }
-
- void setObserver(const sp<IOMXObserver> &observer) {
- mObserver = observer;
- }
-
- sp<IOMXObserver> observer() {
- return mObserver;
- }
-
-private:
- OMX *mOwner;
- OMX_HANDLETYPE mHandle;
- sp<IOMXObserver> mObserver;
-
- NodeMeta(const NodeMeta &);
- NodeMeta &operator=(const NodeMeta &);
-};
-
////////////////////////////////////////////////////////////////////////////////
struct OMX::CallbackDispatcher : public RefBase {
- CallbackDispatcher();
+ CallbackDispatcher(OMX *owner);
void post(const omx_message &msg);
@@ -85,6 +48,8 @@
private:
Mutex mLock;
+
+ OMX *mOwner;
bool mDone;
Condition mQueueChanged;
List<omx_message> mQueue;
@@ -100,8 +65,9 @@
CallbackDispatcher &operator=(const CallbackDispatcher &);
};
-OMX::CallbackDispatcher::CallbackDispatcher()
- : mDone(false) {
+OMX::CallbackDispatcher::CallbackDispatcher(OMX *owner)
+ : mOwner(owner),
+ mDone(false) {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
@@ -130,12 +96,12 @@
}
void OMX::CallbackDispatcher::dispatch(const omx_message &msg) {
- NodeMeta *meta = static_cast<NodeMeta *>(msg.node);
-
- sp<IOMXObserver> observer = meta->observer();
- if (observer.get() != NULL) {
- observer->on_message(msg);
+ OMXNodeInstance *instance = mOwner->findInstance(msg.node);
+ if (instance == NULL) {
+ LOGW("Would have dispatched a message to a node that's already gone.");
+ return;
}
+ instance->onMessage(msg);
}
// static
@@ -213,46 +179,30 @@
BufferMeta &operator=(const BufferMeta &);
};
-// static
-OMX_CALLBACKTYPE OMX::kCallbacks = {
- &OnEvent, &OnEmptyBufferDone, &OnFillBufferDone
-};
-
-// static
-OMX_ERRORTYPE OMX::OnEvent(
- OMX_IN OMX_HANDLETYPE hComponent,
- OMX_IN OMX_PTR pAppData,
- OMX_IN OMX_EVENTTYPE eEvent,
- OMX_IN OMX_U32 nData1,
- OMX_IN OMX_U32 nData2,
- OMX_IN OMX_PTR pEventData) {
- NodeMeta *meta = static_cast<NodeMeta *>(pAppData);
- return meta->owner()->OnEvent(meta, eEvent, nData1, nData2, pEventData);
-}
-
-// static
-OMX_ERRORTYPE OMX::OnEmptyBufferDone(
- OMX_IN OMX_HANDLETYPE hComponent,
- OMX_IN OMX_PTR pAppData,
- OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
- NodeMeta *meta = static_cast<NodeMeta *>(pAppData);
- return meta->owner()->OnEmptyBufferDone(meta, pBuffer);
-}
-
-// static
-OMX_ERRORTYPE OMX::OnFillBufferDone(
- OMX_IN OMX_HANDLETYPE hComponent,
- OMX_IN OMX_PTR pAppData,
- OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
- NodeMeta *meta = static_cast<NodeMeta *>(pAppData);
- return meta->owner()->OnFillBufferDone(meta, pBuffer);
-}
-
OMX::OMX()
- : mDispatcher(new CallbackDispatcher) {
+ : mDispatcher(new CallbackDispatcher(this)),
+ mNodeCounter(0) {
}
-status_t OMX::list_nodes(List<String8> *list) {
+void OMX::binderDied(const wp<IBinder> &the_late_who) {
+ OMXNodeInstance *instance;
+
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ ssize_t index = mLiveNodes.indexOfKey(the_late_who);
+ CHECK(index >= 0);
+
+ instance = mLiveNodes.editValueAt(index);
+ mLiveNodes.removeItemsAt(index);
+
+ invalidateNodeID_l(instance->nodeID());
+ }
+
+ instance->onObserverDied();
+}
+
+status_t OMX::listNodes(List<String8> *list) {
OMX_MasterInit(); // XXX Put this somewhere else.
list->clear();
@@ -269,204 +219,132 @@
return OK;
}
-status_t OMX::allocate_node(const char *name, node_id *node) {
+status_t OMX::allocateNode(
+ const char *name, const sp<IOMXObserver> &observer, node_id *node) {
Mutex::Autolock autoLock(mLock);
*node = 0;
OMX_MasterInit(); // XXX Put this somewhere else.
- NodeMeta *meta = new NodeMeta(this);
+ OMXNodeInstance *instance = new OMXNodeInstance(this, observer);
OMX_HANDLETYPE handle;
OMX_ERRORTYPE err = OMX_MasterGetHandle(
- &handle, const_cast<char *>(name), meta, &kCallbacks);
+ &handle, const_cast<char *>(name), instance,
+ &OMXNodeInstance::kCallbacks);
if (err != OMX_ErrorNone) {
- LOGE("FAILED to allocate omx component '%s'", name);
+ LOGV("FAILED to allocate omx component '%s'", name);
- delete meta;
- meta = NULL;
+ instance->onGetHandleFailed();
return UNKNOWN_ERROR;
}
- meta->setHandle(handle);
+ *node = makeNodeID(instance);
- *node = meta;
+ instance->setHandle(*node, handle);
+
+ mLiveNodes.add(observer->asBinder(), instance);
+ observer->asBinder()->linkToDeath(this);
return OK;
}
-status_t OMX::free_node(node_id node) {
- Mutex::Autolock autoLock(mLock);
+status_t OMX::freeNode(node_id node) {
+ OMXNodeInstance *instance = findInstance(node);
- NodeMeta *meta = static_cast<NodeMeta *>(node);
+ ssize_t index = mLiveNodes.indexOfKey(instance->observer()->asBinder());
+ CHECK(index >= 0);
+ mLiveNodes.removeItemsAt(index);
+ instance->observer()->asBinder()->unlinkToDeath(this);
- OMX_ERRORTYPE err = OMX_MasterFreeHandle(meta->handle());
-
- delete meta;
- meta = NULL;
-
- return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+ return instance->freeNode();
}
-status_t OMX::send_command(
+status_t OMX::sendCommand(
node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
- Mutex::Autolock autoLock(mLock);
-
- NodeMeta *meta = static_cast<NodeMeta *>(node);
- OMX_ERRORTYPE err = OMX_SendCommand(meta->handle(), cmd, param, NULL);
-
- return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+ return findInstance(node)->sendCommand(cmd, param);
}
-status_t OMX::get_parameter(
+status_t OMX::getParameter(
node_id node, OMX_INDEXTYPE index,
void *params, size_t size) {
- Mutex::Autolock autoLock(mLock);
-
- NodeMeta *meta = static_cast<NodeMeta *>(node);
- OMX_ERRORTYPE err = OMX_GetParameter(meta->handle(), index, params);
-
- return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+ return findInstance(node)->getParameter(
+ index, params, size);
}
-status_t OMX::set_parameter(
+status_t OMX::setParameter(
node_id node, OMX_INDEXTYPE index,
const void *params, size_t size) {
- Mutex::Autolock autoLock(mLock);
-
- NodeMeta *meta = static_cast<NodeMeta *>(node);
- OMX_ERRORTYPE err =
- OMX_SetParameter(meta->handle(), index, const_cast<void *>(params));
-
- return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+ return findInstance(node)->setParameter(
+ index, params, size);
}
-status_t OMX::get_config(
+status_t OMX::getConfig(
node_id node, OMX_INDEXTYPE index,
void *params, size_t size) {
- Mutex::Autolock autoLock(mLock);
-
- NodeMeta *meta = static_cast<NodeMeta *>(node);
- OMX_ERRORTYPE err = OMX_GetConfig(meta->handle(), index, params);
-
- return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+ return findInstance(node)->getConfig(
+ index, params, size);
}
-status_t OMX::set_config(
+status_t OMX::setConfig(
node_id node, OMX_INDEXTYPE index,
const void *params, size_t size) {
- Mutex::Autolock autoLock(mLock);
-
- NodeMeta *meta = static_cast<NodeMeta *>(node);
- OMX_ERRORTYPE err =
- OMX_SetConfig(meta->handle(), index, const_cast<void *>(params));
-
- return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+ return findInstance(node)->setConfig(
+ index, params, size);
}
-status_t OMX::use_buffer(
+status_t OMX::useBuffer(
node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
buffer_id *buffer) {
- Mutex::Autolock autoLock(mLock);
-
- BufferMeta *buffer_meta = new BufferMeta(this, params);
-
- OMX_BUFFERHEADERTYPE *header;
-
- NodeMeta *node_meta = static_cast<NodeMeta *>(node);
- OMX_ERRORTYPE err =
- OMX_UseBuffer(node_meta->handle(), &header, port_index, buffer_meta,
- params->size(), static_cast<OMX_U8 *>(params->pointer()));
-
- if (err != OMX_ErrorNone) {
- LOGE("OMX_UseBuffer failed with error %d (0x%08x)", err, err);
-
- delete buffer_meta;
- buffer_meta = NULL;
-
- *buffer = 0;
- return UNKNOWN_ERROR;
- }
-
- *buffer = header;
-
- return OK;
+ return findInstance(node)->useBuffer(
+ port_index, params, buffer);
}
-status_t OMX::allocate_buffer(
+status_t OMX::allocateBuffer(
node_id node, OMX_U32 port_index, size_t size,
buffer_id *buffer) {
- Mutex::Autolock autoLock(mLock);
-
- BufferMeta *buffer_meta = new BufferMeta(this, size);
-
- OMX_BUFFERHEADERTYPE *header;
-
- NodeMeta *node_meta = static_cast<NodeMeta *>(node);
- OMX_ERRORTYPE err =
- OMX_AllocateBuffer(node_meta->handle(), &header, port_index,
- buffer_meta, size);
-
- if (err != OMX_ErrorNone) {
- delete buffer_meta;
- buffer_meta = NULL;
-
- *buffer = 0;
- return UNKNOWN_ERROR;
- }
-
- *buffer = header;
-
- return OK;
+ return findInstance(node)->allocateBuffer(
+ port_index, size, buffer);
}
-status_t OMX::allocate_buffer_with_backup(
+status_t OMX::allocateBufferWithBackup(
node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
buffer_id *buffer) {
- Mutex::Autolock autoLock(mLock);
-
- BufferMeta *buffer_meta = new BufferMeta(this, params, true);
-
- OMX_BUFFERHEADERTYPE *header;
-
- NodeMeta *node_meta = static_cast<NodeMeta *>(node);
- OMX_ERRORTYPE err =
- OMX_AllocateBuffer(
- node_meta->handle(), &header, port_index, buffer_meta,
- params->size());
-
- if (err != OMX_ErrorNone) {
- delete buffer_meta;
- buffer_meta = NULL;
-
- *buffer = 0;
- return UNKNOWN_ERROR;
- }
-
- *buffer = header;
-
- return OK;
+ return findInstance(node)->allocateBufferWithBackup(
+ port_index, params, buffer);
}
-status_t OMX::free_buffer(node_id node, OMX_U32 port_index, buffer_id buffer) {
- OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
- BufferMeta *buffer_meta = static_cast<BufferMeta *>(header->pAppPrivate);
+status_t OMX::freeBuffer(node_id node, OMX_U32 port_index, buffer_id buffer) {
+ return findInstance(node)->freeBuffer(
+ port_index, buffer);
+}
- NodeMeta *node_meta = static_cast<NodeMeta *>(node);
- OMX_ERRORTYPE err =
- OMX_FreeBuffer(node_meta->handle(), port_index, header);
+status_t OMX::fillBuffer(node_id node, buffer_id buffer) {
+ return findInstance(node)->fillBuffer(buffer);
+}
- delete buffer_meta;
- buffer_meta = NULL;
+status_t OMX::emptyBuffer(
+ node_id node,
+ buffer_id buffer,
+ OMX_U32 range_offset, OMX_U32 range_length,
+ OMX_U32 flags, OMX_TICKS timestamp) {
+ return findInstance(node)->emptyBuffer(
+ buffer, range_offset, range_length, flags, timestamp);
+}
- return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+status_t OMX::getExtensionIndex(
+ node_id node,
+ const char *parameter_name,
+ OMX_INDEXTYPE *index) {
+ return findInstance(node)->getExtensionIndex(
+ parameter_name, index);
}
OMX_ERRORTYPE OMX::OnEvent(
- NodeMeta *meta,
+ node_id node,
OMX_IN OMX_EVENTTYPE eEvent,
OMX_IN OMX_U32 nData1,
OMX_IN OMX_U32 nData2,
@@ -475,7 +353,7 @@
omx_message msg;
msg.type = omx_message::EVENT;
- msg.node = meta;
+ msg.node = node;
msg.u.event_data.event = eEvent;
msg.u.event_data.data1 = nData1;
msg.u.event_data.data2 = nData2;
@@ -484,14 +362,14 @@
return OMX_ErrorNone;
}
-
+
OMX_ERRORTYPE OMX::OnEmptyBufferDone(
- NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
+ node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
LOGV("OnEmptyBufferDone buffer=%p", pBuffer);
omx_message msg;
msg.type = omx_message::EMPTY_BUFFER_DONE;
- msg.node = meta;
+ msg.node = node;
msg.u.buffer_data.buffer = pBuffer;
mDispatcher->post(msg);
@@ -500,14 +378,12 @@
}
OMX_ERRORTYPE OMX::OnFillBufferDone(
- NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
+ node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
LOGV("OnFillBufferDone buffer=%p", pBuffer);
- BufferMeta *buffer_meta = static_cast<BufferMeta *>(pBuffer->pAppPrivate);
- buffer_meta->CopyFromOMX(pBuffer);
omx_message msg;
msg.type = omx_message::FILL_BUFFER_DONE;
- msg.node = meta;
+ msg.node = node;
msg.u.extended_buffer_data.buffer = pBuffer;
msg.u.extended_buffer_data.range_offset = pBuffer->nOffset;
msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen;
@@ -520,62 +396,31 @@
return OMX_ErrorNone;
}
-status_t OMX::observe_node(
- node_id node, const sp<IOMXObserver> &observer) {
- NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+OMX::node_id OMX::makeNodeID(OMXNodeInstance *instance) {
+ // mLock is already held.
- node_meta->setObserver(observer);
+ node_id node = (node_id)++mNodeCounter;
+ mNodeIDToInstance.add(node, instance);
- return OK;
+ return node;
}
-void OMX::fill_buffer(node_id node, buffer_id buffer) {
- OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
- header->nFilledLen = 0;
- header->nOffset = 0;
- header->nFlags = 0;
+OMXNodeInstance *OMX::findInstance(node_id node) {
+ Mutex::Autolock autoLock(mLock);
- NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+ ssize_t index = mNodeIDToInstance.indexOfKey(node);
- OMX_ERRORTYPE err =
- OMX_FillThisBuffer(node_meta->handle(), header);
- CHECK_EQ(err, OMX_ErrorNone);
+ return index < 0 ? NULL : mNodeIDToInstance.valueAt(index);
}
-void OMX::empty_buffer(
- node_id node,
- buffer_id buffer,
- OMX_U32 range_offset, OMX_U32 range_length,
- OMX_U32 flags, OMX_TICKS timestamp) {
- OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
- header->nFilledLen = range_length;
- header->nOffset = range_offset;
- header->nFlags = flags;
- header->nTimeStamp = timestamp;
-
- BufferMeta *buffer_meta =
- static_cast<BufferMeta *>(header->pAppPrivate);
- buffer_meta->CopyToOMX(header);
-
- NodeMeta *node_meta = static_cast<NodeMeta *>(node);
-
- OMX_ERRORTYPE err =
- OMX_EmptyThisBuffer(node_meta->handle(), header);
- CHECK_EQ(err, OMX_ErrorNone);
+void OMX::invalidateNodeID(node_id node) {
+ Mutex::Autolock autoLock(mLock);
+ invalidateNodeID_l(node);
}
-status_t OMX::get_extension_index(
- node_id node,
- const char *parameter_name,
- OMX_INDEXTYPE *index) {
- NodeMeta *node_meta = static_cast<NodeMeta *>(node);
-
- OMX_ERRORTYPE err =
- OMX_GetExtensionIndex(
- node_meta->handle(),
- const_cast<char *>(parameter_name), index);
-
- return err == OMX_ErrorNone ? OK : UNKNOWN_ERROR;
+void OMX::invalidateNodeID_l(node_id node) {
+ // mLock is held.
+ mNodeIDToInstance.removeItem(node);
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
new file mode 100644
index 0000000..d277587
--- /dev/null
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -0,0 +1,373 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OMXNodeInstance"
+#include <utils/Log.h>
+
+#include "../include/OMXNodeInstance.h"
+
+#include "pv_omxcore.h"
+
+#include <binder/IMemory.h>
+#include <media/stagefright/MediaDebug.h>
+
+namespace android {
+
+struct BufferMeta {
+ BufferMeta(const sp<IMemory> &mem, bool is_backup = false)
+ : mMem(mem),
+ mIsBackup(is_backup) {
+ }
+
+ BufferMeta(size_t size)
+ : mSize(size),
+ mIsBackup(false) {
+ }
+
+ void CopyFromOMX(const OMX_BUFFERHEADERTYPE *header) {
+ if (!mIsBackup) {
+ return;
+ }
+
+ memcpy((OMX_U8 *)mMem->pointer() + header->nOffset,
+ header->pBuffer + header->nOffset,
+ header->nFilledLen);
+ }
+
+ void CopyToOMX(const OMX_BUFFERHEADERTYPE *header) {
+ if (!mIsBackup) {
+ return;
+ }
+
+ memcpy(header->pBuffer + header->nOffset,
+ (const OMX_U8 *)mMem->pointer() + header->nOffset,
+ header->nFilledLen);
+ }
+
+private:
+ sp<IMemory> mMem;
+ size_t mSize;
+ bool mIsBackup;
+
+ BufferMeta(const BufferMeta &);
+ BufferMeta &operator=(const BufferMeta &);
+};
+
+// static
+OMX_CALLBACKTYPE OMXNodeInstance::kCallbacks = {
+ &OnEvent, &OnEmptyBufferDone, &OnFillBufferDone
+};
+
+OMXNodeInstance::OMXNodeInstance(
+ OMX *owner, const sp<IOMXObserver> &observer)
+ : mOwner(owner),
+ mNodeID(NULL),
+ mHandle(NULL),
+ mObserver(observer) {
+}
+
+OMXNodeInstance::~OMXNodeInstance() {
+ CHECK_EQ(mHandle, NULL);
+}
+
+void OMXNodeInstance::setHandle(OMX::node_id node_id, OMX_HANDLETYPE handle) {
+ CHECK_EQ(mHandle, NULL);
+ mNodeID = node_id;
+ mHandle = handle;
+}
+
+OMX *OMXNodeInstance::owner() {
+ return mOwner;
+}
+
+sp<IOMXObserver> OMXNodeInstance::observer() {
+ return mObserver;
+}
+
+OMX::node_id OMXNodeInstance::nodeID() {
+ return mNodeID;
+}
+
+static status_t StatusFromOMXError(OMX_ERRORTYPE err) {
+ return (err == OMX_ErrorNone) ? OK : UNKNOWN_ERROR;
+}
+
+status_t OMXNodeInstance::freeNode() {
+ Mutex::Autolock autoLock(mLock);
+
+ OMX_ERRORTYPE err = OMX_MasterFreeHandle(mHandle);
+ mHandle = NULL;
+
+ if (err != OMX_ErrorNone) {
+ LOGE("FreeHandle FAILED with error 0x%08x.", err);
+ }
+
+ mOwner->invalidateNodeID(mNodeID);
+ mNodeID = NULL;
+
+ LOGI("OMXNodeInstance going away.");
+ delete this;
+
+ return StatusFromOMXError(err);
+}
+
+status_t OMXNodeInstance::sendCommand(
+ OMX_COMMANDTYPE cmd, OMX_S32 param) {
+ Mutex::Autolock autoLock(mLock);
+
+ OMX_ERRORTYPE err = OMX_SendCommand(mHandle, cmd, param, NULL);
+ return StatusFromOMXError(err);
+}
+
+status_t OMXNodeInstance::getParameter(
+ OMX_INDEXTYPE index, void *params, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ OMX_ERRORTYPE err = OMX_GetParameter(mHandle, index, params);
+ return StatusFromOMXError(err);
+}
+
+status_t OMXNodeInstance::setParameter(
+ OMX_INDEXTYPE index, const void *params, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ OMX_ERRORTYPE err = OMX_SetParameter(
+ mHandle, index, const_cast<void *>(params));
+
+ return StatusFromOMXError(err);
+}
+
+status_t OMXNodeInstance::getConfig(
+ OMX_INDEXTYPE index, void *params, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ OMX_ERRORTYPE err = OMX_GetConfig(mHandle, index, params);
+ return StatusFromOMXError(err);
+}
+
+status_t OMXNodeInstance::setConfig(
+ OMX_INDEXTYPE index, const void *params, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ OMX_ERRORTYPE err = OMX_SetConfig(
+ mHandle, index, const_cast<void *>(params));
+
+ return StatusFromOMXError(err);
+}
+
+status_t OMXNodeInstance::useBuffer(
+ OMX_U32 portIndex, const sp<IMemory> ¶ms,
+ OMX::buffer_id *buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ BufferMeta *buffer_meta = new BufferMeta(params);
+
+ OMX_BUFFERHEADERTYPE *header;
+
+ OMX_ERRORTYPE err = OMX_UseBuffer(
+ mHandle, &header, portIndex, buffer_meta,
+ params->size(), static_cast<OMX_U8 *>(params->pointer()));
+
+ if (err != OMX_ErrorNone) {
+ LOGE("OMX_UseBuffer failed with error %d (0x%08x)", err, err);
+
+ delete buffer_meta;
+ buffer_meta = NULL;
+
+ *buffer = 0;
+
+ return UNKNOWN_ERROR;
+ }
+
+ *buffer = header;
+
+ return OK;
+}
+
+status_t OMXNodeInstance::allocateBuffer(
+ OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ BufferMeta *buffer_meta = new BufferMeta(size);
+
+ OMX_BUFFERHEADERTYPE *header;
+
+ OMX_ERRORTYPE err = OMX_AllocateBuffer(
+ mHandle, &header, portIndex, buffer_meta, size);
+
+ if (err != OMX_ErrorNone) {
+ LOGE("OMX_AllocateBuffer failed with error %d (0x%08x)", err, err);
+
+ delete buffer_meta;
+ buffer_meta = NULL;
+
+ *buffer = 0;
+
+ return UNKNOWN_ERROR;
+ }
+
+ *buffer = header;
+
+ return OK;
+}
+
+status_t OMXNodeInstance::allocateBufferWithBackup(
+ OMX_U32 portIndex, const sp<IMemory> ¶ms,
+ OMX::buffer_id *buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ BufferMeta *buffer_meta = new BufferMeta(params, true);
+
+ OMX_BUFFERHEADERTYPE *header;
+
+ OMX_ERRORTYPE err = OMX_AllocateBuffer(
+ mHandle, &header, portIndex, buffer_meta, params->size());
+
+ if (err != OMX_ErrorNone) {
+ LOGE("OMX_AllocateBuffer failed with error %d (0x%08x)", err, err);
+
+ delete buffer_meta;
+ buffer_meta = NULL;
+
+ *buffer = 0;
+
+ return UNKNOWN_ERROR;
+ }
+
+ *buffer = header;
+
+ return OK;
+}
+
+status_t OMXNodeInstance::freeBuffer(
+ OMX_U32 portIndex, OMX::buffer_id buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
+ BufferMeta *buffer_meta = static_cast<BufferMeta *>(header->pAppPrivate);
+
+ OMX_ERRORTYPE err = OMX_FreeBuffer(mHandle, portIndex, header);
+
+ delete buffer_meta;
+ buffer_meta = NULL;
+
+ return StatusFromOMXError(err);
+}
+
+status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
+ header->nFilledLen = 0;
+ header->nOffset = 0;
+ header->nFlags = 0;
+
+ OMX_ERRORTYPE err = OMX_FillThisBuffer(mHandle, header);
+
+ return StatusFromOMXError(err);
+}
+
+status_t OMXNodeInstance::emptyBuffer(
+ OMX::buffer_id buffer,
+ OMX_U32 rangeOffset, OMX_U32 rangeLength,
+ OMX_U32 flags, OMX_TICKS timestamp) {
+ Mutex::Autolock autoLock(mLock);
+
+ OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
+ header->nFilledLen = rangeLength;
+ header->nOffset = rangeOffset;
+ header->nFlags = flags;
+ header->nTimeStamp = timestamp;
+
+ BufferMeta *buffer_meta =
+ static_cast<BufferMeta *>(header->pAppPrivate);
+ buffer_meta->CopyToOMX(header);
+
+ OMX_ERRORTYPE err = OMX_EmptyThisBuffer(mHandle, header);
+
+ return StatusFromOMXError(err);
+}
+
+status_t OMXNodeInstance::getExtensionIndex(
+ const char *parameterName, OMX_INDEXTYPE *index) {
+ Mutex::Autolock autoLock(mLock);
+
+ OMX_ERRORTYPE err = OMX_GetExtensionIndex(
+ mHandle, const_cast<char *>(parameterName), index);
+
+ return StatusFromOMXError(err);
+}
+
+void OMXNodeInstance::onMessage(const omx_message &msg) {
+ if (msg.type == omx_message::FILL_BUFFER_DONE) {
+ OMX_BUFFERHEADERTYPE *buffer =
+ static_cast<OMX_BUFFERHEADERTYPE *>(
+ msg.u.extended_buffer_data.buffer);
+
+ BufferMeta *buffer_meta =
+ static_cast<BufferMeta *>(buffer->pAppPrivate);
+
+ buffer_meta->CopyFromOMX(buffer);
+ }
+
+ mObserver->onMessage(msg);
+}
+
+void OMXNodeInstance::onObserverDied() {
+ LOGE("!!! Observer died. Quickly, do something, ... anything...");
+
+ // Try to force shutdown of the node and hope for the best.
+ freeNode();
+}
+
+void OMXNodeInstance::onGetHandleFailed() {
+ delete this;
+}
+
+// static
+OMX_ERRORTYPE OMXNodeInstance::OnEvent(
+ OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_EVENTTYPE eEvent,
+ OMX_IN OMX_U32 nData1,
+ OMX_IN OMX_U32 nData2,
+ OMX_IN OMX_PTR pEventData) {
+ OMXNodeInstance *instance = static_cast<OMXNodeInstance *>(pAppData);
+ return instance->owner()->OnEvent(
+ instance->nodeID(), eEvent, nData1, nData2, pEventData);
+}
+
+// static
+OMX_ERRORTYPE OMXNodeInstance::OnEmptyBufferDone(
+ OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
+ OMXNodeInstance *instance = static_cast<OMXNodeInstance *>(pAppData);
+ return instance->owner()->OnEmptyBufferDone(instance->nodeID(), pBuffer);
+}
+
+// static
+OMX_ERRORTYPE OMXNodeInstance::OnFillBufferDone(
+ OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
+ OMXNodeInstance *instance = static_cast<OMXNodeInstance *>(pAppData);
+ return instance->owner()->OnFillBufferDone(instance->nodeID(), pBuffer);
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/omx/QComHardwareRenderer.cpp b/media/libstagefright/omx/QComHardwareRenderer.cpp
index e9930be..8e78c77 100644
--- a/media/libstagefright/omx/QComHardwareRenderer.cpp
+++ b/media/libstagefright/omx/QComHardwareRenderer.cpp
@@ -14,10 +14,11 @@
* limitations under the License.
*/
+#include "../include/QComHardwareRenderer.h"
+
#include <binder/MemoryHeapBase.h>
#include <binder/MemoryHeapPmem.h>
#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/QComHardwareRenderer.h>
#include <ui/ISurface.h>
namespace android {
@@ -83,6 +84,11 @@
}
mISurface->postBuffer(offset);
+
+ // Since we cannot tell how long it'll take until surface flinger
+ // has displayed the data onscreen, we'll just have to guess...
+ // We must not return the buffer to the decoder before it's been displayed.
+ usleep(25000);
}
bool QComHardwareRenderer::getOffset(void *platformPrivate, size_t *offset) {
diff --git a/media/libstagefright/omx/SoftwareRenderer.cpp b/media/libstagefright/omx/SoftwareRenderer.cpp
index 4ed6869..ef6ede0 100644
--- a/media/libstagefright/omx/SoftwareRenderer.cpp
+++ b/media/libstagefright/omx/SoftwareRenderer.cpp
@@ -17,21 +17,21 @@
#define LOG_TAG "SoftwareRenderer"
#include <utils/Log.h>
+#include "../include/SoftwareRenderer.h"
+
#include <binder/MemoryHeapBase.h>
#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/SoftwareRenderer.h>
#include <ui/ISurface.h>
namespace android {
-#define QCOM_YUV 0
-
SoftwareRenderer::SoftwareRenderer(
OMX_COLOR_FORMATTYPE colorFormat,
const sp<ISurface> &surface,
size_t displayWidth, size_t displayHeight,
size_t decodedWidth, size_t decodedHeight)
: mColorFormat(colorFormat),
+ mConverter(colorFormat, OMX_COLOR_Format16bitRGB565),
mISurface(surface),
mDisplayWidth(displayWidth),
mDisplayHeight(displayHeight),
@@ -39,12 +39,12 @@
mDecodedHeight(decodedHeight),
mFrameSize(mDecodedWidth * mDecodedHeight * 2), // RGB565
mMemoryHeap(new MemoryHeapBase(2 * mFrameSize)),
- mIndex(0),
- mClip(NULL) {
+ mIndex(0) {
CHECK(mISurface.get() != NULL);
CHECK(mDecodedWidth > 0);
CHECK(mDecodedHeight > 0);
CHECK(mMemoryHeap->heapID() >= 0);
+ CHECK(mConverter.isValid());
ISurface::BufferHeap bufferHeap(
mDisplayWidth, mDisplayHeight,
@@ -58,278 +58,19 @@
SoftwareRenderer::~SoftwareRenderer() {
mISurface->unregisterBuffers();
-
- delete[] mClip;
- mClip = NULL;
}
void SoftwareRenderer::render(
const void *data, size_t size, void *platformPrivate) {
- static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
-
- switch (mColorFormat) {
- case OMX_COLOR_FormatYUV420Planar:
- return renderYUV420Planar(data, size);
-
- case OMX_COLOR_FormatCbYCrY:
- return renderCbYCrY(data, size);
-
- case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
- return renderQCOMYUV420SemiPlanar(data, size);
-
- default:
- {
- LOGW("Cannot render color format %ld", mColorFormat);
- break;
- }
- }
-}
-
-void SoftwareRenderer::renderYUV420Planar(
- const void *data, size_t size) {
- if (size != (mDecodedHeight * mDecodedWidth * 3) / 2) {
- LOGE("size is %d, expected %d",
- size, (mDecodedHeight * mDecodedWidth * 3) / 2);
- }
- CHECK(size >= (mDecodedWidth * mDecodedHeight * 3) / 2);
-
- uint8_t *kAdjustedClip = initClip();
-
size_t offset = mIndex * mFrameSize;
-
void *dst = (uint8_t *)mMemoryHeap->getBase() + offset;
- uint32_t *dst_ptr = (uint32_t *)dst;
-
- const uint8_t *src_y = (const uint8_t *)data;
-
- const uint8_t *src_u =
- (const uint8_t *)src_y + mDecodedWidth * mDecodedHeight;
-
-#if !QCOM_YUV
- const uint8_t *src_v =
- (const uint8_t *)src_u + (mDecodedWidth / 2) * (mDecodedHeight / 2);
-#endif
-
- for (size_t y = 0; y < mDecodedHeight; ++y) {
- for (size_t x = 0; x < mDecodedWidth; x += 2) {
- // B = 1.164 * (Y - 16) + 2.018 * (U - 128)
- // G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128)
- // R = 1.164 * (Y - 16) + 1.596 * (V - 128)
-
- // B = 298/256 * (Y - 16) + 517/256 * (U - 128)
- // G = .................. - 208/256 * (V - 128) - 100/256 * (U - 128)
- // R = .................. + 409/256 * (V - 128)
-
- // min_B = (298 * (- 16) + 517 * (- 128)) / 256 = -277
- // min_G = (298 * (- 16) - 208 * (255 - 128) - 100 * (255 - 128)) / 256 = -172
- // min_R = (298 * (- 16) + 409 * (- 128)) / 256 = -223
-
- // max_B = (298 * (255 - 16) + 517 * (255 - 128)) / 256 = 534
- // max_G = (298 * (255 - 16) - 208 * (- 128) - 100 * (- 128)) / 256 = 432
- // max_R = (298 * (255 - 16) + 409 * (255 - 128)) / 256 = 481
-
- // clip range -278 .. 535
-
- signed y1 = (signed)src_y[x] - 16;
- signed y2 = (signed)src_y[x + 1] - 16;
-
-#if QCOM_YUV
- signed u = (signed)src_u[x & ~1] - 128;
- signed v = (signed)src_u[(x & ~1) + 1] - 128;
-#else
- signed u = (signed)src_u[x / 2] - 128;
- signed v = (signed)src_v[x / 2] - 128;
-#endif
-
- signed u_b = u * 517;
- signed u_g = -u * 100;
- signed v_g = -v * 208;
- signed v_r = v * 409;
-
- signed tmp1 = y1 * 298;
- signed b1 = (tmp1 + u_b) / 256;
- signed g1 = (tmp1 + v_g + u_g) / 256;
- signed r1 = (tmp1 + v_r) / 256;
-
- signed tmp2 = y2 * 298;
- signed b2 = (tmp2 + u_b) / 256;
- signed g2 = (tmp2 + v_g + u_g) / 256;
- signed r2 = (tmp2 + v_r) / 256;
-
- uint32_t rgb1 =
- ((kAdjustedClip[r1] >> 3) << 11)
- | ((kAdjustedClip[g1] >> 2) << 5)
- | (kAdjustedClip[b1] >> 3);
-
- uint32_t rgb2 =
- ((kAdjustedClip[r2] >> 3) << 11)
- | ((kAdjustedClip[g2] >> 2) << 5)
- | (kAdjustedClip[b2] >> 3);
-
- dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
- }
-
- src_y += mDecodedWidth;
-
- if (y & 1) {
-#if QCOM_YUV
- src_u += mDecodedWidth;
-#else
- src_u += mDecodedWidth / 2;
- src_v += mDecodedWidth / 2;
-#endif
- }
-
- dst_ptr += mDecodedWidth / 2;
- }
+ mConverter.convert(
+ mDecodedWidth, mDecodedHeight,
+ data, 0, dst, 2 * mDecodedWidth);
mISurface->postBuffer(offset);
mIndex = 1 - mIndex;
}
-void SoftwareRenderer::renderCbYCrY(
- const void *data, size_t size) {
- if (size != (mDecodedHeight * mDecodedWidth * 2)) {
- LOGE("size is %d, expected %d",
- size, (mDecodedHeight * mDecodedWidth * 2));
- }
- CHECK(size >= (mDecodedWidth * mDecodedHeight * 2));
-
- uint8_t *kAdjustedClip = initClip();
-
- size_t offset = mIndex * mFrameSize;
- void *dst = (uint8_t *)mMemoryHeap->getBase() + offset;
- uint32_t *dst_ptr = (uint32_t *)dst;
-
- const uint8_t *src = (const uint8_t *)data;
-
- for (size_t y = 0; y < mDecodedHeight; ++y) {
- for (size_t x = 0; x < mDecodedWidth; x += 2) {
- signed y1 = (signed)src[2 * x + 1] - 16;
- signed y2 = (signed)src[2 * x + 3] - 16;
- signed u = (signed)src[2 * x] - 128;
- signed v = (signed)src[2 * x + 2] - 128;
-
- signed u_b = u * 517;
- signed u_g = -u * 100;
- signed v_g = -v * 208;
- signed v_r = v * 409;
-
- signed tmp1 = y1 * 298;
- signed b1 = (tmp1 + u_b) / 256;
- signed g1 = (tmp1 + v_g + u_g) / 256;
- signed r1 = (tmp1 + v_r) / 256;
-
- signed tmp2 = y2 * 298;
- signed b2 = (tmp2 + u_b) / 256;
- signed g2 = (tmp2 + v_g + u_g) / 256;
- signed r2 = (tmp2 + v_r) / 256;
-
- uint32_t rgb1 =
- ((kAdjustedClip[r1] >> 3) << 11)
- | ((kAdjustedClip[g1] >> 2) << 5)
- | (kAdjustedClip[b1] >> 3);
-
- uint32_t rgb2 =
- ((kAdjustedClip[r2] >> 3) << 11)
- | ((kAdjustedClip[g2] >> 2) << 5)
- | (kAdjustedClip[b2] >> 3);
-
- dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
- }
-
- src += mDecodedWidth * 2;
- dst_ptr += mDecodedWidth / 2;
- }
-
- mISurface->postBuffer(offset);
- mIndex = 1 - mIndex;
-}
-
-void SoftwareRenderer::renderQCOMYUV420SemiPlanar(
- const void *data, size_t size) {
- if (size != (mDecodedHeight * mDecodedWidth * 3) / 2) {
- LOGE("size is %d, expected %d",
- size, (mDecodedHeight * mDecodedWidth * 3) / 2);
- }
- CHECK(size >= (mDecodedWidth * mDecodedHeight * 3) / 2);
-
- uint8_t *kAdjustedClip = initClip();
-
- size_t offset = mIndex * mFrameSize;
-
- void *dst = (uint8_t *)mMemoryHeap->getBase() + offset;
-
- uint32_t *dst_ptr = (uint32_t *)dst;
-
- const uint8_t *src_y = (const uint8_t *)data;
-
- const uint8_t *src_u =
- (const uint8_t *)src_y + mDecodedWidth * mDecodedHeight;
-
- for (size_t y = 0; y < mDecodedHeight; ++y) {
- for (size_t x = 0; x < mDecodedWidth; x += 2) {
- signed y1 = (signed)src_y[x] - 16;
- signed y2 = (signed)src_y[x + 1] - 16;
-
- signed u = (signed)src_u[x & ~1] - 128;
- signed v = (signed)src_u[(x & ~1) + 1] - 128;
-
- signed u_b = u * 517;
- signed u_g = -u * 100;
- signed v_g = -v * 208;
- signed v_r = v * 409;
-
- signed tmp1 = y1 * 298;
- signed b1 = (tmp1 + u_b) / 256;
- signed g1 = (tmp1 + v_g + u_g) / 256;
- signed r1 = (tmp1 + v_r) / 256;
-
- signed tmp2 = y2 * 298;
- signed b2 = (tmp2 + u_b) / 256;
- signed g2 = (tmp2 + v_g + u_g) / 256;
- signed r2 = (tmp2 + v_r) / 256;
-
- uint32_t rgb1 =
- ((kAdjustedClip[b1] >> 3) << 11)
- | ((kAdjustedClip[g1] >> 2) << 5)
- | (kAdjustedClip[r1] >> 3);
-
- uint32_t rgb2 =
- ((kAdjustedClip[b2] >> 3) << 11)
- | ((kAdjustedClip[g2] >> 2) << 5)
- | (kAdjustedClip[r2] >> 3);
-
- dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
- }
-
- src_y += mDecodedWidth;
-
- if (y & 1) {
- src_u += mDecodedWidth;
- }
-
- dst_ptr += mDecodedWidth / 2;
- }
-
- mISurface->postBuffer(offset);
- mIndex = 1 - mIndex;
-}
-
-uint8_t *SoftwareRenderer::initClip() {
- static const signed kClipMin = -278;
- static const signed kClipMax = 535;
-
- if (mClip == NULL) {
- mClip = new uint8_t[kClipMax - kClipMin + 1];
-
- for (signed i = kClipMin; i <= kClipMax; ++i) {
- mClip[i - kClipMin] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i;
- }
- }
-
- return &mClip[-kClipMin];
-}
-
} // namespace android
diff --git a/media/libstagefright/omx/TIHardwareRenderer.cpp b/media/libstagefright/omx/TIHardwareRenderer.cpp
index ebade4a..6dde86a 100644
--- a/media/libstagefright/omx/TIHardwareRenderer.cpp
+++ b/media/libstagefright/omx/TIHardwareRenderer.cpp
@@ -17,7 +17,8 @@
#define LOG_TAG "TIHardwareRenderer"
#include <utils/Log.h>
-#include <media/stagefright/TIHardwareRenderer.h>
+#include "../include/TIHardwareRenderer.h"
+
#include <media/stagefright/MediaDebug.h>
#include <ui/ISurface.h>
#include <ui/Overlay.h>
diff --git a/media/libstagefright/string.cpp b/media/libstagefright/string.cpp
index 5b16784..bd6204b 100644
--- a/media/libstagefright/string.cpp
+++ b/media/libstagefright/string.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <media/stagefright/string.h>
+#include "include/stagefright_string.h"
namespace android {
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 695d061..f485d26 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -18,7 +18,6 @@
import java.io.Writer;
import java.util.ArrayList;
-import java.util.concurrent.Semaphore;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
@@ -30,6 +29,8 @@
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
+import android.content.pm.ConfigurationInfo;
+import android.os.SystemProperties;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
@@ -681,7 +682,10 @@
}
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int[] num_config = new int[1];
- egl.eglChooseConfig(display, mConfigSpec, null, 0, num_config);
+ if (!egl.eglChooseConfig(display, mConfigSpec, null, 0,
+ num_config)) {
+ throw new IllegalArgumentException("eglChooseConfig failed");
+ }
int numConfigs = num_config[0];
@@ -691,8 +695,10 @@
}
EGLConfig[] configs = new EGLConfig[numConfigs];
- egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
- num_config);
+ if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
+ num_config)) {
+ throw new IllegalArgumentException("eglChooseConfig#2 failed");
+ }
EGLConfig config = chooseConfig(egl, display, configs);
if (config == null) {
throw new IllegalArgumentException("No config chosen");
@@ -817,11 +823,17 @@
*/
mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+ if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
+ throw new RuntimeException("eglGetDisplay failed");
+ }
+
/*
* We can now initialize EGL for that display
*/
int[] version = new int[2];
- mEgl.eglInitialize(mEglDisplay, version);
+ if(!mEgl.eglInitialize(mEglDisplay, version)) {
+ throw new RuntimeException("eglInitialize failed");
+ }
mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
/*
@@ -966,16 +978,12 @@
* accesses EGL.
*/
try {
- try {
- sEglSemaphore.acquire();
- } catch (InterruptedException e) {
- return;
- }
+ sGLAccessLock.acquire();
guardedRun();
} catch (InterruptedException e) {
// fall thru and exit normally
} finally {
- sEglSemaphore.release();
+ sGLAccessLock.release();
}
}
@@ -1040,6 +1048,7 @@
}
if (changed) {
gl = (GL10) mEglHelper.createSurface(getHolder());
+ sGLAccessLock.checkGLDriver(gl);
tellRendererSurfaceChanged = true;
}
if (tellRendererSurfaceCreated) {
@@ -1241,7 +1250,56 @@
}
}
- private static final Semaphore sEglSemaphore = new Semaphore(1);
+ private static class GLAccessLock {
+ public synchronized void acquire() throws InterruptedException {
+ if (! mGLESVersionCheckComplete) {
+ mGLESVersion = SystemProperties.getInt(
+ "ro.opengles.version",
+ ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
+ if (mGLESVersion >= kGLES_20) {
+ mMultipleGLESContextsAllowed = true;
+ }
+ mGLESVersionCheckComplete = true;
+ }
+
+ while ((! mMultipleGLESContextsAllowed)
+ && mGLContextCount > 0) {
+ wait();
+ }
+
+ mGLContextCount++;
+
+ }
+
+ public synchronized void release() {
+ mGLContextCount--;
+ notifyAll();
+ }
+
+ public synchronized void checkGLDriver(GL10 gl) {
+ if (! mGLESDriverCheckComplete) {
+ if (mGLESVersion < kGLES_20) {
+ String renderer = gl.glGetString(GL10.GL_RENDERER);
+ mMultipleGLESContextsAllowed =
+ ! renderer.startsWith(kMSM7K_RENDERER_PREFIX);
+ notifyAll();
+ }
+ mGLESDriverCheckComplete = true;
+ }
+ }
+
+ private boolean mGLESVersionCheckComplete;
+ private int mGLESVersion;
+ private boolean mGLESDriverCheckComplete;
+ private boolean mMultipleGLESContextsAllowed;
+ private int mGLContextCount;
+ private static final int kGLES_20 = 0x20000;
+ private static final String kMSM7K_RENDERER_PREFIX =
+ "Q3Dimension MSM7500 ";
+ };
+
+ private static GLAccessLock sGLAccessLock = new GLAccessLock();
+
private boolean mSizeChanged = true;
private GLThread mGLThread;
diff --git a/opengl/tests/gl2_jni/src/com/android/gl2jni/GL2JNIView.java b/opengl/tests/gl2_jni/src/com/android/gl2jni/GL2JNIView.java
index 2dae090..72b1dfb 100644
--- a/opengl/tests/gl2_jni/src/com/android/gl2jni/GL2JNIView.java
+++ b/opengl/tests/gl2_jni/src/com/android/gl2jni/GL2JNIView.java
@@ -56,19 +56,22 @@
*/
class GL2JNIView extends GLSurfaceView {
private static String TAG = "GL2JNIView";
- GL2JNIView(Context context) {
+
+ public GL2JNIView(Context context) {
super(context);
- init();
+ init(false, 0, 0);
}
- public GL2JNIView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init();
+ public GL2JNIView(Context context, boolean translucent, int depth, int stencil) {
+ super(context);
+ init(translucent, depth, stencil);
}
- private void init() {
+ private void init(boolean translucent, int depth, int stencil) {
setEGLContextFactory(new ContextFactory());
- setEGLConfigChooser(new ConfigChooser());
+ setEGLConfigChooser( translucent ?
+ new ConfigChooser(8,8,8,8, depth, stencil) :
+ new ConfigChooser(5,6,5,0, depth, stencil));
setRenderer(new Renderer());
}
@@ -105,6 +108,16 @@
EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL10.EGL_NONE
};
+
+ public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) {
+ mRedSize = r;
+ mGreenSize = g;
+ mBlueSize = b;
+ mAlphaSize = a;
+ mDepthSize = depth;
+ mStencilSize = stencil;
+ }
+
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int[] num_config = new int[1];
@@ -112,14 +125,158 @@
int numConfigs = num_config[0];
- Log.w(TAG, String.format("Found %d configurations", numConfigs));
if (numConfigs <= 0) {
throw new IllegalArgumentException("No configs match configSpec");
}
EGLConfig[] configs = new EGLConfig[numConfigs];
egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config);
- return configs[0];
+ // printConfigs(egl, display, configs);
+ return chooseConfig(egl, display, configs);
}
+
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs) {
+ EGLConfig closestConfig = null;
+ int closestDistance = 1000;
+ for(EGLConfig config : configs) {
+ int d = findConfigAttrib(egl, display, config,
+ EGL10.EGL_DEPTH_SIZE, 0);
+ int s = findConfigAttrib(egl, display, config,
+ EGL10.EGL_STENCIL_SIZE, 0);
+ if (d >= mDepthSize && s>= mStencilSize) {
+ int r = findConfigAttrib(egl, display, config,
+ EGL10.EGL_RED_SIZE, 0);
+ int g = findConfigAttrib(egl, display, config,
+ EGL10.EGL_GREEN_SIZE, 0);
+ int b = findConfigAttrib(egl, display, config,
+ EGL10.EGL_BLUE_SIZE, 0);
+ int a = findConfigAttrib(egl, display, config,
+ EGL10.EGL_ALPHA_SIZE, 0);
+ int distance = Math.abs(r - mRedSize)
+ + Math.abs(g - mGreenSize)
+ + Math.abs(b - mBlueSize)
+ + Math.abs(a - mAlphaSize);
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestConfig = config;
+ }
+ }
+ }
+ return closestConfig;
+ }
+
+ private int findConfigAttrib(EGL10 egl, EGLDisplay display,
+ EGLConfig config, int attribute, int defaultValue) {
+
+ if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
+ return mValue[0];
+ }
+ return defaultValue;
+ }
+
+ private void printConfigs(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs) {
+ int numConfigs = configs.length;
+ Log.w(TAG, String.format("%d configurations", numConfigs));
+ for (int i = 0; i < numConfigs; i++) {
+ Log.w(TAG, String.format("Configuration %d:\n", i));
+ printConfig(egl, display, configs[i]);
+ }
+ }
+
+ private void printConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig config) {
+ int[] attributes = {
+ EGL10.EGL_BUFFER_SIZE,
+ EGL10.EGL_ALPHA_SIZE,
+ EGL10.EGL_BLUE_SIZE,
+ EGL10.EGL_GREEN_SIZE,
+ EGL10.EGL_RED_SIZE,
+ EGL10.EGL_DEPTH_SIZE,
+ EGL10.EGL_STENCIL_SIZE,
+ EGL10.EGL_CONFIG_CAVEAT,
+ EGL10.EGL_CONFIG_ID,
+ EGL10.EGL_LEVEL,
+ EGL10.EGL_MAX_PBUFFER_HEIGHT,
+ EGL10.EGL_MAX_PBUFFER_PIXELS,
+ EGL10.EGL_MAX_PBUFFER_WIDTH,
+ EGL10.EGL_NATIVE_RENDERABLE,
+ EGL10.EGL_NATIVE_VISUAL_ID,
+ EGL10.EGL_NATIVE_VISUAL_TYPE,
+ 0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
+ EGL10.EGL_SAMPLES,
+ EGL10.EGL_SAMPLE_BUFFERS,
+ EGL10.EGL_SURFACE_TYPE,
+ EGL10.EGL_TRANSPARENT_TYPE,
+ EGL10.EGL_TRANSPARENT_RED_VALUE,
+ EGL10.EGL_TRANSPARENT_GREEN_VALUE,
+ EGL10.EGL_TRANSPARENT_BLUE_VALUE,
+ 0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,
+ 0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,
+ 0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,
+ 0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,
+ EGL10.EGL_LUMINANCE_SIZE,
+ EGL10.EGL_ALPHA_MASK_SIZE,
+ EGL10.EGL_COLOR_BUFFER_TYPE,
+ EGL10.EGL_RENDERABLE_TYPE,
+ 0x3042 // EGL10.EGL_CONFORMANT
+ };
+ String[] names = {
+ "EGL_BUFFER_SIZE",
+ "EGL_ALPHA_SIZE",
+ "EGL_BLUE_SIZE",
+ "EGL_GREEN_SIZE",
+ "EGL_RED_SIZE",
+ "EGL_DEPTH_SIZE",
+ "EGL_STENCIL_SIZE",
+ "EGL_CONFIG_CAVEAT",
+ "EGL_CONFIG_ID",
+ "EGL_LEVEL",
+ "EGL_MAX_PBUFFER_HEIGHT",
+ "EGL_MAX_PBUFFER_PIXELS",
+ "EGL_MAX_PBUFFER_WIDTH",
+ "EGL_NATIVE_RENDERABLE",
+ "EGL_NATIVE_VISUAL_ID",
+ "EGL_NATIVE_VISUAL_TYPE",
+ "EGL_PRESERVED_RESOURCES",
+ "EGL_SAMPLES",
+ "EGL_SAMPLE_BUFFERS",
+ "EGL_SURFACE_TYPE",
+ "EGL_TRANSPARENT_TYPE",
+ "EGL_TRANSPARENT_RED_VALUE",
+ "EGL_TRANSPARENT_GREEN_VALUE",
+ "EGL_TRANSPARENT_BLUE_VALUE",
+ "EGL_BIND_TO_TEXTURE_RGB",
+ "EGL_BIND_TO_TEXTURE_RGBA",
+ "EGL_MIN_SWAP_INTERVAL",
+ "EGL_MAX_SWAP_INTERVAL",
+ "EGL_LUMINANCE_SIZE",
+ "EGL_ALPHA_MASK_SIZE",
+ "EGL_COLOR_BUFFER_TYPE",
+ "EGL_RENDERABLE_TYPE",
+ "EGL_CONFORMANT"
+ };
+ int[] value = new int[1];
+ for (int i = 0; i < attributes.length; i++) {
+ int attribute = attributes[i];
+ String name = names[i];
+ if ( egl.eglGetConfigAttrib(display, config, attribute, value)) {
+ Log.w(TAG, String.format(" %s: %d\n", name, value[0]));
+ } else {
+ // Log.w(TAG, String.format(" %s: failed\n", name));
+ while (egl.eglGetError() != EGL10.EGL_SUCCESS);
+ }
+ }
+ }
+
+ // Subclasses can adjust these values:
+ protected int mRedSize;
+ protected int mGreenSize;
+ protected int mBlueSize;
+ protected int mAlphaSize;
+ protected int mDepthSize;
+ protected int mStencilSize;
+ private int[] mValue = new int[1];
}
private static class Renderer implements GLSurfaceView.Renderer {
diff --git a/opengl/tests/gldual/Android.mk b/opengl/tests/gldual/Android.mk
new file mode 100644
index 0000000..e73c249
--- /dev/null
+++ b/opengl/tests/gldual/Android.mk
@@ -0,0 +1,51 @@
+#########################################################################
+# OpenGL ES JNI sample
+# This makefile builds both an activity and a shared library.
+#########################################################################
+ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build activity
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := user
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := GLDual
+
+LOCAL_JNI_SHARED_LIBRARIES := libgldualjni
+
+include $(BUILD_PACKAGE)
+
+#########################################################################
+# Build JNI Shared Library
+#########################################################################
+
+LOCAL_PATH:= $(LOCAL_PATH)/jni
+
+include $(CLEAR_VARS)
+
+# Optional tag would mean it doesn't get installed by default
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_CFLAGS := -Werror
+
+LOCAL_SRC_FILES:= \
+ gl_code.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libutils \
+ libEGL \
+ libGLESv2
+
+LOCAL_MODULE := libgldualjni
+
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif # TARGET_SIMULATOR
diff --git a/opengl/tests/gldual/AndroidManifest.xml b/opengl/tests/gldual/AndroidManifest.xml
new file mode 100644
index 0000000..06f4c4d
--- /dev/null
+++ b/opengl/tests/gldual/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.gldual">
+ <application
+ android:label="@string/gldual_activity">
+ <activity android:name="GLDualActivity"
+ android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+ android:launchMode="singleTask"
+ android:configChanges="orientation|keyboardHidden">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/opengl/tests/gldual/jni/gl_code.cpp b/opengl/tests/gldual/jni/gl_code.cpp
new file mode 100644
index 0000000..f1f0a1f
--- /dev/null
+++ b/opengl/tests/gldual/jni/gl_code.cpp
@@ -0,0 +1,165 @@
+// OpenGL ES 2.0 code
+
+#include <nativehelper/jni.h>
+#define LOG_TAG "GL2JNI gl_code.cpp"
+#include <utils/Log.h>
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+static void printGLString(const char *name, GLenum s) {
+ const char *v = (const char *) glGetString(s);
+ LOGI("GL %s = %s\n", name, v);
+}
+
+static void checkGlError(const char* op) {
+ for (GLint error = glGetError(); error; error
+ = glGetError()) {
+ LOGI("after %s() glError (0x%x)\n", op, error);
+ }
+}
+
+static const char gVertexShader[] = "attribute vec4 vPosition;\n"
+ "void main() {\n"
+ " gl_Position = vPosition;\n"
+ "}\n";
+
+static const char gFragmentShader[] = "precision mediump float;\n"
+ "void main() {\n"
+ " gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
+ "}\n";
+
+GLuint loadShader(GLenum shaderType, const char* pSource) {
+ GLuint shader = glCreateShader(shaderType);
+ if (shader) {
+ glShaderSource(shader, 1, &pSource, NULL);
+ glCompileShader(shader);
+ GLint compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ GLint infoLen = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+ if (infoLen) {
+ char* buf = (char*) malloc(infoLen);
+ if (buf) {
+ glGetShaderInfoLog(shader, infoLen, NULL, buf);
+ LOGE("Could not compile shader %d:\n%s\n",
+ shaderType, buf);
+ free(buf);
+ }
+ glDeleteShader(shader);
+ shader = 0;
+ }
+ }
+ }
+ return shader;
+}
+
+GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) {
+ GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
+ if (!vertexShader) {
+ return 0;
+ }
+
+ GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
+ if (!pixelShader) {
+ return 0;
+ }
+
+ GLuint program = glCreateProgram();
+ if (program) {
+ glAttachShader(program, vertexShader);
+ checkGlError("glAttachShader");
+ glAttachShader(program, pixelShader);
+ checkGlError("glAttachShader");
+ glLinkProgram(program);
+ GLint linkStatus = GL_FALSE;
+ glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+ if (linkStatus != GL_TRUE) {
+ GLint bufLength = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
+ if (bufLength) {
+ char* buf = (char*) malloc(bufLength);
+ if (buf) {
+ glGetProgramInfoLog(program, bufLength, NULL, buf);
+ LOGE("Could not link program:\n%s\n", buf);
+ free(buf);
+ }
+ }
+ glDeleteProgram(program);
+ program = 0;
+ }
+ }
+ return program;
+}
+
+GLuint gProgram;
+GLuint gvPositionHandle;
+
+bool setupGraphics(int w, int h) {
+ printGLString("Version", GL_VERSION);
+ printGLString("Vendor", GL_VENDOR);
+ printGLString("Renderer", GL_RENDERER);
+ printGLString("Extensions", GL_EXTENSIONS);
+
+ LOGI("setupGraphics(%d, %d)", w, h);
+ gProgram = createProgram(gVertexShader, gFragmentShader);
+ if (!gProgram) {
+ LOGE("Could not create program.");
+ return false;
+ }
+ gvPositionHandle = glGetAttribLocation(gProgram, "vPosition");
+ checkGlError("glGetAttribLocation");
+ LOGI("glGetAttribLocation(\"vPosition\") = %d\n",
+ gvPositionHandle);
+
+ glViewport(0, 0, w, h);
+ checkGlError("glViewport");
+ return true;
+}
+
+const GLfloat gTriangleVertices[] = { 0.0f, 0.5f, -0.5f, -0.5f,
+ 0.5f, -0.5f };
+
+void renderFrame() {
+ static float grey;
+ grey += 0.01f;
+ if (grey > 1.0f) {
+ grey = 0.0f;
+ }
+ glClearColor(grey, grey, grey, 1.0f);
+ checkGlError("glClearColor");
+ glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ checkGlError("glClear");
+
+ glUseProgram(gProgram);
+ checkGlError("glUseProgram");
+
+ glVertexAttribPointer(gvPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, gTriangleVertices);
+ checkGlError("glVertexAttribPointer");
+ glEnableVertexAttribArray(gvPositionHandle);
+ checkGlError("glEnableVertexAttribArray");
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+ checkGlError("glDrawArrays");
+}
+
+extern "C" {
+ JNIEXPORT void JNICALL Java_com_android_gldual_GLDualLib_init(JNIEnv * env, jobject obj, jint width, jint height);
+ JNIEXPORT void JNICALL Java_com_android_gldual_GLDualLib_step(JNIEnv * env, jobject obj);
+};
+
+JNIEXPORT void JNICALL Java_com_android_gldual_GLDualLib_init(JNIEnv * env, jobject obj, jint width, jint height)
+{
+ setupGraphics(width, height);
+}
+
+JNIEXPORT void JNICALL Java_com_android_gldual_GLDualLib_step(JNIEnv * env, jobject obj)
+{
+ renderFrame();
+}
+
diff --git a/opengl/tests/gldual/res/layout/gldual_activity.xml b/opengl/tests/gldual/res/layout/gldual_activity.xml
new file mode 100644
index 0000000..f2d59c7
--- /dev/null
+++ b/opengl/tests/gldual/res/layout/gldual_activity.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/text"
+
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <android.opengl.GLSurfaceView android:id="@+id/gl1"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1" />
+ <com.android.gldual.GLDualGL2View android:id="@+id/gl2"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1" />
+</LinearLayout>
diff --git a/opengl/tests/gldual/res/values/strings.xml b/opengl/tests/gldual/res/values/strings.xml
new file mode 100644
index 0000000..4267dff
--- /dev/null
+++ b/opengl/tests/gldual/res/values/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This file contains resource definitions for displayed strings, allowing
+ them to be changed based on the locale and options. -->
+
+<resources>
+ <!-- Simple strings. -->
+ <string name="gldual_activity">GLDual</string>
+
+</resources>
+
diff --git a/opengl/tests/gldual/src/com/android/gldual/GLDualActivity.java b/opengl/tests/gldual/src/com/android/gldual/GLDualActivity.java
new file mode 100644
index 0000000..9d88f64
--- /dev/null
+++ b/opengl/tests/gldual/src/com/android/gldual/GLDualActivity.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gldual;
+
+import android.app.Activity;
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.LinearLayout;
+
+
+public class GLDualActivity extends Activity {
+
+ GLSurfaceView mGLView;
+ GLDualGL2View mGL2View;
+
+ @Override protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ View root = getLayoutInflater().inflate(R.layout.gldual_activity, null);
+ mGLView = (GLSurfaceView) root.findViewById(R.id.gl1);
+ mGLView.setEGLConfigChooser(5,6,5,0,0,0);
+ mGLView.setRenderer(new TriangleRenderer());
+ mGL2View = (GLDualGL2View) root.findViewById(R.id.gl2);
+ setContentView(root);
+ }
+
+ @Override protected void onPause() {
+ super.onPause();
+ mGLView.onPause();
+ mGL2View.onPause();
+ }
+
+ @Override protected void onResume() {
+ super.onResume();
+ mGLView.onResume();
+ mGL2View.onResume();
+ }
+}
diff --git a/opengl/tests/gldual/src/com/android/gldual/GLDualGL2View.java b/opengl/tests/gldual/src/com/android/gldual/GLDualGL2View.java
new file mode 100644
index 0000000..8f5e347
--- /dev/null
+++ b/opengl/tests/gldual/src/com/android/gldual/GLDualGL2View.java
@@ -0,0 +1,299 @@
+/*
+ * 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.gldual;
+/*
+ * 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.
+ */
+
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+import android.util.Log;
+
+/**
+ * An implementation of SurfaceView that uses the dedicated surface for
+ * displaying an OpenGL animation. This allows the animation to run in a
+ * separate thread, without requiring that it be driven by the update mechanism
+ * of the view hierarchy.
+ *
+ * The application-specific rendering code is delegated to a GLView.Renderer
+ * instance.
+ */
+class GLDualGL2View extends GLSurfaceView {
+ private static String TAG = "GLDualGL2View";
+
+ public GLDualGL2View(Context context) {
+ super(context);
+ init(false, 0, 0);
+ }
+
+ public GLDualGL2View(Context context, AttributeSet set) {
+ super(context, set);
+ init(false, 0, 0);
+ }
+
+ public GLDualGL2View(Context context, boolean translucent, int depth, int stencil) {
+ super(context);
+ init(translucent, depth, stencil);
+ }
+
+ private void init(boolean translucent, int depth, int stencil) {
+ setEGLContextFactory(new ContextFactory());
+ setEGLConfigChooser( translucent ?
+ new ConfigChooser(8,8,8,8, depth, stencil) :
+ new ConfigChooser(5,6,5,0, depth, stencil));
+ setRenderer(new Renderer());
+ }
+
+ private static class ContextFactory implements GLSurfaceView.EGLContextFactory {
+ private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+ public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
+ Log.w(TAG, "creating OpenGL ES 2.0 context");
+ checkEglError("Before eglCreateContext", egl);
+ int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
+ EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
+ checkEglError("After eglCreateContext", egl);
+ return context;
+ }
+
+ public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
+ egl.eglDestroyContext(display, context);
+ }
+ }
+
+ private static void checkEglError(String prompt, EGL10 egl) {
+ int error;
+ while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) {
+ Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error));
+ }
+ }
+
+ private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser {
+ private static int EGL_OPENGL_ES2_BIT = 4;
+ private static int[] s_configAttribs2 =
+ {
+ EGL10.EGL_RED_SIZE, 4,
+ EGL10.EGL_GREEN_SIZE, 4,
+ EGL10.EGL_BLUE_SIZE, 4,
+ EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL10.EGL_NONE
+ };
+
+ public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) {
+ mRedSize = r;
+ mGreenSize = g;
+ mBlueSize = b;
+ mAlphaSize = a;
+ mDepthSize = depth;
+ mStencilSize = stencil;
+ }
+
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+
+ int[] num_config = new int[1];
+ egl.eglChooseConfig(display, s_configAttribs2, null, 0, num_config);
+
+ int numConfigs = num_config[0];
+
+ if (numConfigs <= 0) {
+ throw new IllegalArgumentException("No configs match configSpec");
+ }
+ EGLConfig[] configs = new EGLConfig[numConfigs];
+ egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config);
+ // printConfigs(egl, display, configs);
+ return chooseConfig(egl, display, configs);
+ }
+
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs) {
+ EGLConfig closestConfig = null;
+ int closestDistance = 1000;
+ for(EGLConfig config : configs) {
+ int d = findConfigAttrib(egl, display, config,
+ EGL10.EGL_DEPTH_SIZE, 0);
+ int s = findConfigAttrib(egl, display, config,
+ EGL10.EGL_STENCIL_SIZE, 0);
+ if (d >= mDepthSize && s>= mStencilSize) {
+ int r = findConfigAttrib(egl, display, config,
+ EGL10.EGL_RED_SIZE, 0);
+ int g = findConfigAttrib(egl, display, config,
+ EGL10.EGL_GREEN_SIZE, 0);
+ int b = findConfigAttrib(egl, display, config,
+ EGL10.EGL_BLUE_SIZE, 0);
+ int a = findConfigAttrib(egl, display, config,
+ EGL10.EGL_ALPHA_SIZE, 0);
+ int distance = Math.abs(r - mRedSize)
+ + Math.abs(g - mGreenSize)
+ + Math.abs(b - mBlueSize)
+ + Math.abs(a - mAlphaSize);
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestConfig = config;
+ }
+ }
+ }
+ return closestConfig;
+ }
+
+ private int findConfigAttrib(EGL10 egl, EGLDisplay display,
+ EGLConfig config, int attribute, int defaultValue) {
+
+ if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
+ return mValue[0];
+ }
+ return defaultValue;
+ }
+
+ private void printConfigs(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs) {
+ int numConfigs = configs.length;
+ Log.w(TAG, String.format("%d configurations", numConfigs));
+ for (int i = 0; i < numConfigs; i++) {
+ Log.w(TAG, String.format("Configuration %d:\n", i));
+ printConfig(egl, display, configs[i]);
+ }
+ }
+
+ private void printConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig config) {
+ int[] attributes = {
+ EGL10.EGL_BUFFER_SIZE,
+ EGL10.EGL_ALPHA_SIZE,
+ EGL10.EGL_BLUE_SIZE,
+ EGL10.EGL_GREEN_SIZE,
+ EGL10.EGL_RED_SIZE,
+ EGL10.EGL_DEPTH_SIZE,
+ EGL10.EGL_STENCIL_SIZE,
+ EGL10.EGL_CONFIG_CAVEAT,
+ EGL10.EGL_CONFIG_ID,
+ EGL10.EGL_LEVEL,
+ EGL10.EGL_MAX_PBUFFER_HEIGHT,
+ EGL10.EGL_MAX_PBUFFER_PIXELS,
+ EGL10.EGL_MAX_PBUFFER_WIDTH,
+ EGL10.EGL_NATIVE_RENDERABLE,
+ EGL10.EGL_NATIVE_VISUAL_ID,
+ EGL10.EGL_NATIVE_VISUAL_TYPE,
+ 0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
+ EGL10.EGL_SAMPLES,
+ EGL10.EGL_SAMPLE_BUFFERS,
+ EGL10.EGL_SURFACE_TYPE,
+ EGL10.EGL_TRANSPARENT_TYPE,
+ EGL10.EGL_TRANSPARENT_RED_VALUE,
+ EGL10.EGL_TRANSPARENT_GREEN_VALUE,
+ EGL10.EGL_TRANSPARENT_BLUE_VALUE,
+ 0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,
+ 0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,
+ 0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,
+ 0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,
+ EGL10.EGL_LUMINANCE_SIZE,
+ EGL10.EGL_ALPHA_MASK_SIZE,
+ EGL10.EGL_COLOR_BUFFER_TYPE,
+ EGL10.EGL_RENDERABLE_TYPE,
+ 0x3042 // EGL10.EGL_CONFORMANT
+ };
+ String[] names = {
+ "EGL_BUFFER_SIZE",
+ "EGL_ALPHA_SIZE",
+ "EGL_BLUE_SIZE",
+ "EGL_GREEN_SIZE",
+ "EGL_RED_SIZE",
+ "EGL_DEPTH_SIZE",
+ "EGL_STENCIL_SIZE",
+ "EGL_CONFIG_CAVEAT",
+ "EGL_CONFIG_ID",
+ "EGL_LEVEL",
+ "EGL_MAX_PBUFFER_HEIGHT",
+ "EGL_MAX_PBUFFER_PIXELS",
+ "EGL_MAX_PBUFFER_WIDTH",
+ "EGL_NATIVE_RENDERABLE",
+ "EGL_NATIVE_VISUAL_ID",
+ "EGL_NATIVE_VISUAL_TYPE",
+ "EGL_PRESERVED_RESOURCES",
+ "EGL_SAMPLES",
+ "EGL_SAMPLE_BUFFERS",
+ "EGL_SURFACE_TYPE",
+ "EGL_TRANSPARENT_TYPE",
+ "EGL_TRANSPARENT_RED_VALUE",
+ "EGL_TRANSPARENT_GREEN_VALUE",
+ "EGL_TRANSPARENT_BLUE_VALUE",
+ "EGL_BIND_TO_TEXTURE_RGB",
+ "EGL_BIND_TO_TEXTURE_RGBA",
+ "EGL_MIN_SWAP_INTERVAL",
+ "EGL_MAX_SWAP_INTERVAL",
+ "EGL_LUMINANCE_SIZE",
+ "EGL_ALPHA_MASK_SIZE",
+ "EGL_COLOR_BUFFER_TYPE",
+ "EGL_RENDERABLE_TYPE",
+ "EGL_CONFORMANT"
+ };
+ int[] value = new int[1];
+ for (int i = 0; i < attributes.length; i++) {
+ int attribute = attributes[i];
+ String name = names[i];
+ if ( egl.eglGetConfigAttrib(display, config, attribute, value)) {
+ Log.w(TAG, String.format(" %s: %d\n", name, value[0]));
+ } else {
+ // Log.w(TAG, String.format(" %s: failed\n", name));
+ while (egl.eglGetError() != EGL10.EGL_SUCCESS);
+ }
+ }
+ }
+
+ // Subclasses can adjust these values:
+ protected int mRedSize;
+ protected int mGreenSize;
+ protected int mBlueSize;
+ protected int mAlphaSize;
+ protected int mDepthSize;
+ protected int mStencilSize;
+ private int[] mValue = new int[1];
+ }
+
+ private static class Renderer implements GLSurfaceView.Renderer {
+ public void onDrawFrame(GL10 gl) {
+ GLDualLib.step();
+ }
+
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ GLDualLib.init(width, height);
+ }
+
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ // Do nothing.
+ }
+ }
+}
+
diff --git a/opengl/tests/gldual/src/com/android/gldual/GLDualLib.java b/opengl/tests/gldual/src/com/android/gldual/GLDualLib.java
new file mode 100644
index 0000000..d8f765e
--- /dev/null
+++ b/opengl/tests/gldual/src/com/android/gldual/GLDualLib.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gldual;
+
+// Wrapper for native library
+
+public class GLDualLib {
+
+ static {
+ System.loadLibrary("gldualjni");
+ }
+
+ /**
+ * @param width the current view width
+ * @param height the current view height
+ */
+ public static native void init(int width, int height);
+ public static native void step();
+}
diff --git a/opengl/tests/gldual/src/com/android/gldual/TriangleRenderer.java b/opengl/tests/gldual/src/com/android/gldual/TriangleRenderer.java
new file mode 100644
index 0000000..098c4d2
--- /dev/null
+++ b/opengl/tests/gldual/src/com/android/gldual/TriangleRenderer.java
@@ -0,0 +1,149 @@
+package com.android.gldual;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.opengl.GLSurfaceView;
+import android.opengl.GLU;
+import android.os.SystemClock;
+
+public class TriangleRenderer implements GLSurfaceView.Renderer{
+
+ public TriangleRenderer() {
+ mTriangle = new Triangle();
+ }
+
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ /*
+ * By default, OpenGL enables features that improve quality
+ * but reduce performance. One might want to tweak that
+ * especially on software renderer.
+ */
+ gl.glDisable(GL10.GL_DITHER);
+
+ /*
+ * Some one-time OpenGL initialization can be made here
+ * probably based on features of this particular context
+ */
+ gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,
+ GL10.GL_FASTEST);
+
+ gl.glClearColor(.5f, .5f, .5f, 1);
+ gl.glShadeModel(GL10.GL_SMOOTH);
+ }
+
+ public void onDrawFrame(GL10 gl) {
+ /*
+ * By default, OpenGL enables features that improve quality
+ * but reduce performance. One might want to tweak that
+ * especially on software renderer.
+ */
+ gl.glDisable(GL10.GL_DITHER);
+
+ /*
+ * Usually, the first thing one might want to do is to clear
+ * the screen. The most efficient way of doing this is to use
+ * glClear().
+ */
+
+ gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+
+ /*
+ * Now we're ready to draw some 3D objects
+ */
+
+ gl.glMatrixMode(GL10.GL_MODELVIEW);
+ gl.glLoadIdentity();
+
+ GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
+
+ gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+
+ long time = SystemClock.uptimeMillis() % 4000L;
+ float angle = 0.090f * ((int) time);
+
+ gl.glRotatef(angle, 0, 0, 1.0f);
+
+ mTriangle.draw(gl);
+ }
+
+ public void onSurfaceChanged(GL10 gl, int w, int h) {
+ gl.glViewport(0, 0, w, h);
+
+ /*
+ * Set our projection matrix. This doesn't have to be done
+ * each time we draw, but usually a new projection needs to
+ * be set when the viewport is resized.
+ */
+
+ float ratio = (float) w / h;
+ gl.glMatrixMode(GL10.GL_PROJECTION);
+ gl.glLoadIdentity();
+ gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7);
+
+ }
+
+ private Triangle mTriangle;
+}
+
+class Triangle {
+ public Triangle() {
+
+ // Buffers to be passed to gl*Pointer() functions
+ // must be direct, i.e., they must be placed on the
+ // native heap where the garbage collector cannot
+ // move them.
+ //
+ // Buffers with multi-byte datatypes (e.g., short, int, float)
+ // must have their byte order set to native order
+
+ ByteBuffer vbb = ByteBuffer.allocateDirect(VERTS * 3 * 4);
+ vbb.order(ByteOrder.nativeOrder());
+ mFVertexBuffer = vbb.asFloatBuffer();
+
+ ByteBuffer tbb = ByteBuffer.allocateDirect(VERTS * 2 * 4);
+ tbb.order(ByteOrder.nativeOrder());
+
+ ByteBuffer ibb = ByteBuffer.allocateDirect(VERTS * 2);
+ ibb.order(ByteOrder.nativeOrder());
+ mIndexBuffer = ibb.asShortBuffer();
+
+ // A unit-sided equalateral triangle centered on the origin.
+ float[] coords = {
+ // X, Y, Z
+ -0.5f, -0.25f, 0,
+ 0.5f, -0.25f, 0,
+ 0.0f, 0.559016994f, 0
+ };
+
+ for (int i = 0; i < VERTS; i++) {
+ for(int j = 0; j < 3; j++) {
+ mFVertexBuffer.put(coords[i*3+j] * 2.0f);
+ }
+ }
+
+ for(int i = 0; i < VERTS; i++) {
+ mIndexBuffer.put((short) i);
+ }
+
+ mFVertexBuffer.position(0);
+ mIndexBuffer.position(0);
+ }
+
+ public void draw(GL10 gl) {
+ gl.glFrontFace(GL10.GL_CCW);
+ gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mFVertexBuffer);
+ gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, VERTS,
+ GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
+ }
+
+ private final static int VERTS = 3;
+
+ private FloatBuffer mFVertexBuffer;
+ private ShortBuffer mIndexBuffer;
+}
diff --git a/packages/VpnServices/res/values-zh-rCN/strings.xml b/packages/VpnServices/res/values-zh-rCN/strings.xml
index 4de0f38..940e210 100644
--- a/packages/VpnServices/res/values-zh-rCN/strings.xml
+++ b/packages/VpnServices/res/values-zh-rCN/strings.xml
@@ -15,8 +15,8 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="4589592829302498102">"VPN 服务"</string>
+ <string name="app_label" msgid="4589592829302498102">"虚拟专用网服务"</string>
<string name="vpn_notification_title_connected" msgid="8598654486956133580">"<xliff:g id="PROFILENAME">%s</xliff:g> VPN 已连接"</string>
<string name="vpn_notification_title_disconnected" msgid="6216572264382192027">"<xliff:g id="PROFILENAME">%s</xliff:g> VPN 连接已断开"</string>
- <string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"触摸可重新连接 VPN。"</string>
+ <string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"轻触可重新连接到虚拟专用网。"</string>
</resources>
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 78215b0..a91635e 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -572,6 +572,8 @@
// javadoc from interface
public int stopUsingNetworkFeature(int networkType, String feature) {
+ enforceChangePermission();
+
int pid = getCallingPid();
int uid = getCallingUid();
@@ -611,7 +613,7 @@
Log.d(TAG, "stopUsingNetworkFeature for net " + networkType +
": " + feature);
}
- enforceChangePermission();
+
if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
return -1;
}
diff --git a/services/java/com/android/server/DropBoxService.java b/services/java/com/android/server/DropBoxService.java
new file mode 100644
index 0000000..3a4c3ac
--- /dev/null
+++ b/services/java/com/android/server/DropBoxService.java
@@ -0,0 +1,725 @@
+/*
+ * 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.server;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.DropBoxEntry;
+import android.os.IDropBox;
+import android.os.ParcelFileDescriptor;
+import android.os.StatFs;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.text.format.DateFormat;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Formatter;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * Implementation of {@link IDropBox} using the filesystem.
+ *
+ * {@hide}
+ */
+public final class DropBoxService extends IDropBox.Stub {
+ private static final String TAG = "DropBoxService";
+ private static final int DEFAULT_RESERVE_PERCENT = 10;
+ private static final int DEFAULT_QUOTA_PERCENT = 10;
+ private static final int DEFAULT_QUOTA_KB = 5 * 1024;
+ private static final int DEFAULT_AGE_SECONDS = 3 * 86400;
+ private static final int QUOTA_RESCAN_MILLIS = 5000;
+
+ // TODO: This implementation currently uses one file per entry, which is
+ // inefficient for smallish entries -- consider using a single queue file
+ // per tag (or even globally) instead.
+
+ // The cached context and derived objects
+
+ private final Context mContext;
+ private final ContentResolver mContentResolver;
+ private final File mDropBoxDir;
+
+ // Accounting of all currently written log files (set in init()).
+
+ private FileList mAllFiles = null;
+ private HashMap<String, FileList> mFilesByTag = null;
+
+ // Various bits of disk information
+
+ private StatFs mStatFs = null;
+ private int mBlockSize = 0;
+ private int mCachedQuotaBlocks = 0; // Space we can use: computed from free space, etc.
+ private long mCachedQuotaUptimeMillis = 0;
+
+ // Ensure that all log entries have a unique timestamp
+ private long mLastTimestamp = 0;
+
+ /** Receives events that might indicate a need to clean up files. */
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mCachedQuotaUptimeMillis = 0; // Force a re-check of quota size
+ try {
+ init();
+ trimToFit();
+ } catch (IOException e) {
+ Log.e(TAG, "Can't init", e);
+ }
+ }
+ };
+
+ /**
+ * Creates an instance of managed drop box storage. Normally there is one of these
+ * run by the system, but others can be created for testing and other purposes.
+ *
+ * @param context to use for receiving free space & gservices intents
+ * @param path to store drop box entries in
+ */
+ public DropBoxService(Context context, File path) {
+ mDropBoxDir = path;
+
+ // Set up intent receivers
+ mContext = context;
+ mContentResolver = context.getContentResolver();
+ context.registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW));
+ context.registerReceiver(mReceiver, new IntentFilter(Settings.Gservices.CHANGED_ACTION));
+
+ // The real work gets done lazily in init() -- that way service creation always
+ // succeeds, and things like disk problems cause individual method failures.
+ }
+
+ /** Unregisters broadcast receivers and any other hooks -- for test instances */
+ public void stop() {
+ mContext.unregisterReceiver(mReceiver);
+ }
+
+ public void addText(String tag, String data) {
+ addData(tag, data.getBytes(), DropBoxEntry.IS_TEXT);
+ }
+
+ public void addData(String tag, byte[] data, int flags) {
+ File temp = null;
+ OutputStream out = null;
+ try {
+ if ((flags & DropBoxEntry.IS_EMPTY) != 0) throw new IllegalArgumentException();
+
+ init();
+ if (!isTagEnabled(tag)) return;
+
+ long max = trimToFit();
+ if (data.length > max) {
+ Log.w(TAG, "Dropping: " + tag + " (" + data.length + " > " + max + " bytes)");
+ // Pass temp = null to createEntry() to leave a tombstone
+ } else {
+ temp = new File(mDropBoxDir, "drop" + Thread.currentThread().getId() + ".tmp");
+ out = new FileOutputStream(temp);
+ if (data.length > mBlockSize && ((flags & DropBoxEntry.IS_GZIPPED) == 0)) {
+ flags = flags | DropBoxEntry.IS_GZIPPED;
+ out = new GZIPOutputStream(out);
+ }
+ out.write(data);
+ out.close();
+ out = null;
+ }
+
+ createEntry(temp, tag, flags);
+ temp = null;
+ } catch (IOException e) {
+ Log.e(TAG, "Can't write: " + tag, e);
+ } finally {
+ try { if (out != null) out.close(); } catch (IOException e) {}
+ if (temp != null) temp.delete();
+ }
+ }
+
+ public void addFile(String tag, ParcelFileDescriptor data, int flags) {
+ File temp = null;
+ OutputStream output = null;
+ try {
+ if ((flags & DropBoxEntry.IS_EMPTY) != 0) throw new IllegalArgumentException();
+
+ init();
+ if (!isTagEnabled(tag)) return;
+ long max = trimToFit();
+ long lastTrim = System.currentTimeMillis();
+
+ byte[] buffer = new byte[mBlockSize];
+ FileInputStream input = new FileInputStream(data.getFileDescriptor());
+
+ // First, accumulate up to one block worth of data in memory before
+ // deciding whether to compress the data or not.
+
+ int read = 0;
+ while (read < buffer.length) {
+ int n = input.read(buffer, read, buffer.length - read);
+ if (n <= 0) break;
+ read += n;
+ }
+
+ // If we have at least one block, compress it -- otherwise, just write
+ // the data in uncompressed form.
+
+ temp = new File(mDropBoxDir, "drop" + Thread.currentThread().getId() + ".tmp");
+ output = new FileOutputStream(temp);
+ if (read == buffer.length && ((flags & DropBoxEntry.IS_GZIPPED) == 0)) {
+ output = new GZIPOutputStream(output);
+ flags = flags | DropBoxEntry.IS_GZIPPED;
+ }
+
+ do {
+ output.write(buffer, 0, read);
+
+ long now = System.currentTimeMillis();
+ if (now - lastTrim > 30 * 1000) {
+ max = trimToFit(); // In case data dribbles in slowly
+ lastTrim = now;
+ }
+
+ read = input.read(buffer);
+ if (read <= 0) {
+ output.close(); // Get a final size measurement
+ output = null;
+ } else {
+ output.flush(); // So the size measurement is pseudo-reasonable
+ }
+
+ long len = temp.length();
+ if (len > max) {
+ Log.w(TAG, "Dropping: " + tag + " (" + temp.length() + " > " + max + " bytes)");
+ temp.delete();
+ temp = null; // Pass temp = null to createEntry() to leave a tombstone
+ break;
+ }
+ } while (read > 0);
+
+ createEntry(temp, tag, flags);
+ temp = null;
+ } catch (IOException e) {
+ Log.e(TAG, "Can't write: " + tag, e);
+ } finally {
+ try { if (output != null) output.close(); } catch (IOException e) {}
+ try { data.close(); } catch (IOException e) {}
+ if (temp != null) temp.delete();
+ }
+ }
+
+ public boolean isTagEnabled(String tag) {
+ return !"disabled".equals(Settings.Gservices.getString(
+ mContentResolver, Settings.Gservices.DROPBOX_TAG_PREFIX + tag));
+ }
+
+ public synchronized DropBoxEntry getNextEntry(long millis) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.READ_LOGS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("READ_LOGS permission required");
+ }
+
+ try {
+ init();
+ } catch (IOException e) {
+ Log.e(TAG, "Can't init", e);
+ return null;
+ }
+
+ for (EntryFile entry : mAllFiles.contents.tailSet(new EntryFile(millis + 1))) {
+ if (entry.tag == null) continue;
+ try {
+ File file = (entry.flags & DropBoxEntry.IS_EMPTY) != 0 ? null : entry.file;
+ return new DropBoxEntry(entry.tag, entry.timestampMillis, file, entry.flags);
+ } catch (IOException e) {
+ Log.e(TAG, "Can't read: " + entry.file, e);
+ // Continue to next file
+ }
+ }
+
+ return null;
+ }
+
+ public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: Can't dump DropBoxService");
+ return;
+ }
+
+ try {
+ init();
+ } catch (IOException e) {
+ pw.println("Can't initialize: " + e);
+ Log.e(TAG, "Can't init", e);
+ return;
+ }
+
+ boolean doPrint = false, doFile = false;
+ ArrayList<String> searchArgs = new ArrayList<String>();
+ for (int i = 0; args != null && i < args.length; i++) {
+ if (args[i].equals("-p") || args[i].equals("--print")) {
+ doPrint = true;
+ } else if (args[i].equals("-f") || args[i].equals("--file")) {
+ doFile = true;
+ } else if (args[i].startsWith("-")) {
+ pw.print("Unknown argument: ");
+ pw.println(args[i]);
+ } else {
+ searchArgs.add(args[i]);
+ }
+ }
+
+ pw.format("Drop box contents: %d entries", mAllFiles.contents.size());
+ pw.println();
+
+ if (!searchArgs.isEmpty()) {
+ pw.print("Searching for:");
+ for (String a : searchArgs) pw.format(" %s", a);
+ pw.println();
+ }
+
+ int numFound = 0;
+ pw.println();
+ for (EntryFile entry : mAllFiles.contents) {
+ String date = new Formatter().format("%s.%03d",
+ DateFormat.format("yyyy-MM-dd kk:mm:ss", entry.timestampMillis),
+ entry.timestampMillis % 1000).toString();
+
+ boolean match = true;
+ for (String a: searchArgs) match = match && (date.contains(a) || a.equals(entry.tag));
+ if (!match) continue;
+
+ numFound++;
+ pw.print(date);
+ pw.print(" ");
+ pw.print(entry.tag == null ? "(no tag)" : entry.tag);
+ if (entry.file == null) {
+ pw.println(" (no file)");
+ continue;
+ } else if ((entry.flags & DropBoxEntry.IS_EMPTY) != 0) {
+ pw.println(" (contents lost)");
+ continue;
+ } else {
+ pw.print((entry.flags & DropBoxEntry.IS_GZIPPED) != 0 ? " (comopressed " : " (");
+ pw.print((entry.flags & DropBoxEntry.IS_TEXT) != 0 ? "text" : "data");
+ pw.format(", %d bytes)", entry.file.length());
+ pw.println();
+ }
+
+ if (doFile || (doPrint && (entry.flags & DropBoxEntry.IS_TEXT) == 0)) {
+ if (!doPrint) pw.print(" ");
+ pw.println(entry.file.getPath());
+ }
+
+ if ((entry.flags & DropBoxEntry.IS_TEXT) != 0 && (doPrint || !doFile)) {
+ DropBoxEntry dbe = null;
+ try {
+ dbe = new DropBoxEntry(
+ entry.tag, entry.timestampMillis, entry.file, entry.flags);
+
+ if (doPrint) {
+ InputStreamReader r = new InputStreamReader(dbe.getInputStream());
+ char[] buf = new char[4096];
+ boolean newline = false;
+ for (;;) {
+ int n = r.read(buf);
+ if (n <= 0) break;
+ pw.write(buf, 0, n);
+ newline = (buf[n - 1] == '\n');
+ }
+ if (!newline) pw.println();
+ } else {
+ String text = dbe.getText(70);
+ boolean truncated = (text.length() == 70);
+ pw.print(" ");
+ pw.print(text.trim().replace('\n', '/'));
+ if (truncated) pw.print(" ...");
+ pw.println();
+ }
+ } catch (IOException e) {
+ pw.print("*** ");
+ pw.println(e.toString());
+ Log.e(TAG, "Can't read: " + entry.file, e);
+ } finally {
+ if (dbe != null) dbe.close();
+ }
+ }
+
+ if (doPrint) pw.println();
+ }
+
+ if (numFound == 0) pw.println("(No entries found.)");
+
+ if (args == null || args.length == 0) {
+ if (!doPrint) pw.println();
+ pw.println("Usage: dumpsys dropbox [--print|--file] [YYYY-mm-dd] [HH:MM:SS.SSS] [tag]");
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ /** Chronologically sorted list of {@link #EntryFile} */
+ private static final class FileList implements Comparable<FileList> {
+ public int blocks = 0;
+ public final TreeSet<EntryFile> contents = new TreeSet<EntryFile>();
+
+ /** Sorts bigger FileList instances before smaller ones. */
+ public final int compareTo(FileList o) {
+ if (blocks != o.blocks) return o.blocks - blocks;
+ if (this == o) return 0;
+ if (hashCode() < o.hashCode()) return -1;
+ if (hashCode() > o.hashCode()) return 1;
+ return 0;
+ }
+ }
+
+ /** Metadata describing an on-disk log file. */
+ private static final class EntryFile implements Comparable<EntryFile> {
+ public final String tag;
+ public final long timestampMillis;
+ public final int flags;
+ public final File file;
+ public final int blocks;
+
+ /** Sorts earlier EntryFile instances before later ones. */
+ public final int compareTo(EntryFile o) {
+ if (timestampMillis < o.timestampMillis) return -1;
+ if (timestampMillis > o.timestampMillis) return 1;
+ if (file != null && o.file != null) return file.compareTo(o.file);
+ if (o.file != null) return -1;
+ if (file != null) return 1;
+ if (this == o) return 0;
+ if (hashCode() < o.hashCode()) return -1;
+ if (hashCode() > o.hashCode()) return 1;
+ return 0;
+ }
+
+ /**
+ * Moves an existing temporary file to a new log filename.
+ * @param temp file to rename
+ * @param dir to store file in
+ * @param tag to use for new log file name
+ * @param timestampMillis of log entry
+ * @param flags for the entry data (from {@link DropBoxEntry})
+ * @param blockSize to use for space accounting
+ * @throws IOException if the file can't be moved
+ */
+ public EntryFile(File temp, File dir, String tag,long timestampMillis,
+ int flags, int blockSize) throws IOException {
+ if ((flags & DropBoxEntry.IS_EMPTY) != 0) throw new IllegalArgumentException();
+
+ this.tag = tag;
+ this.timestampMillis = timestampMillis;
+ this.flags = flags;
+ this.file = new File(dir, Uri.encode(tag) + "@" + timestampMillis +
+ ((flags & DropBoxEntry.IS_TEXT) != 0 ? ".txt" : ".dat") +
+ ((flags & DropBoxEntry.IS_GZIPPED) != 0 ? ".gz" : ""));
+
+ if (!temp.renameTo(this.file)) {
+ throw new IOException("Can't rename " + temp + " to " + this.file);
+ }
+ this.blocks = (int) ((this.file.length() + blockSize - 1) / blockSize);
+ }
+
+ /**
+ * Creates a zero-length tombstone for a file whose contents were lost.
+ * @param dir to store file in
+ * @param tag to use for new log file name
+ * @param timestampMillis of log entry
+ * @throws IOException if the file can't be created.
+ */
+ public EntryFile(File dir, String tag, long timestampMillis) throws IOException {
+ this.tag = tag;
+ this.timestampMillis = timestampMillis;
+ this.flags = DropBoxEntry.IS_EMPTY;
+ this.file = new File(dir, Uri.encode(tag) + "@" + timestampMillis + ".lost");
+ this.blocks = 0;
+ new FileOutputStream(this.file).close();
+ }
+
+ /**
+ * Extracts metadata from an existing on-disk log filename.
+ * @param file name of existing log file
+ * @param blockSize to use for space accounting
+ */
+ public EntryFile(File file, int blockSize) {
+ this.file = file;
+ this.blocks = (int) ((this.file.length() + blockSize - 1) / blockSize);
+
+ String name = file.getName();
+ int at = name.lastIndexOf('@');
+ if (at < 0) {
+ this.tag = null;
+ this.timestampMillis = 0;
+ this.flags = DropBoxEntry.IS_EMPTY;
+ return;
+ }
+
+ int flags = 0;
+ this.tag = Uri.decode(name.substring(0, at));
+ if (name.endsWith(".gz")) {
+ flags |= DropBoxEntry.IS_GZIPPED;
+ name = name.substring(0, name.length() - 3);
+ }
+ if (name.endsWith(".lost")) {
+ flags |= DropBoxEntry.IS_EMPTY;
+ name = name.substring(at + 1, name.length() - 5);
+ } else if (name.endsWith(".txt")) {
+ flags |= DropBoxEntry.IS_TEXT;
+ name = name.substring(at + 1, name.length() - 4);
+ } else if (name.endsWith(".dat")) {
+ name = name.substring(at + 1, name.length() - 4);
+ } else {
+ this.flags = DropBoxEntry.IS_EMPTY;
+ this.timestampMillis = 0;
+ return;
+ }
+ this.flags = flags;
+
+ long millis;
+ try { millis = Long.valueOf(name); } catch (NumberFormatException e) { millis = 0; }
+ this.timestampMillis = millis;
+ }
+
+ /**
+ * Creates a EntryFile object with only a timestamp for comparison purposes.
+ * @param timestampMillis to compare with.
+ */
+ public EntryFile(long millis) {
+ this.tag = null;
+ this.timestampMillis = millis;
+ this.flags = DropBoxEntry.IS_EMPTY;
+ this.file = null;
+ this.blocks = 0;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ /** If never run before, scans disk contents to build in-memory tracking data. */
+ private synchronized void init() throws IOException {
+ if (mStatFs == null) {
+ if (!mDropBoxDir.isDirectory() && !mDropBoxDir.mkdirs()) {
+ throw new IOException("Can't mkdir: " + mDropBoxDir);
+ }
+ try {
+ mStatFs = new StatFs(mDropBoxDir.getPath());
+ mBlockSize = mStatFs.getBlockSize();
+ } catch (IllegalArgumentException e) { // StatFs throws this on error
+ throw new IOException("Can't statfs: " + mDropBoxDir);
+ }
+ }
+
+ if (mAllFiles == null) {
+ File[] files = mDropBoxDir.listFiles();
+ if (files == null) throw new IOException("Can't list files: " + mDropBoxDir);
+
+ mAllFiles = new FileList();
+ mFilesByTag = new HashMap<String, FileList>();
+
+ // Scan pre-existing files.
+ for (File file : files) {
+ if (file.getName().endsWith(".tmp")) {
+ Log.i(TAG, "Cleaning temp file: " + file);
+ file.delete();
+ continue;
+ }
+
+ EntryFile entry = new EntryFile(file, mBlockSize);
+ if (entry.tag == null) {
+ Log.w(TAG, "Unrecognized file: " + file);
+ continue;
+ } else if (entry.timestampMillis == 0) {
+ Log.w(TAG, "Invalid filename: " + file);
+ file.delete();
+ continue;
+ }
+
+ enrollEntry(entry);
+ }
+ }
+ }
+
+ /** Adds a disk log file to in-memory tracking for accounting and enumeration. */
+ private synchronized void enrollEntry(EntryFile entry) {
+ mAllFiles.contents.add(entry);
+ mAllFiles.blocks += entry.blocks;
+
+ // mFilesByTag is used for trimming, so don't list empty files.
+ // (Zero-length/lost files are trimmed by date from mAllFiles.)
+
+ if (entry.tag != null && entry.file != null && entry.blocks > 0) {
+ FileList tagFiles = mFilesByTag.get(entry.tag);
+ if (tagFiles == null) {
+ tagFiles = new FileList();
+ mFilesByTag.put(entry.tag, tagFiles);
+ }
+ tagFiles.contents.add(entry);
+ tagFiles.blocks += entry.blocks;
+ }
+ }
+
+ /** Moves a temporary file to a final log filename and enrolls it. */
+ private synchronized void createEntry(File temp, String tag, int flags) throws IOException {
+ long t = System.currentTimeMillis();
+
+ // Require each entry to have a unique timestamp; if there are entries
+ // >10sec in the future (due to clock skew), drag them back to avoid
+ // keeping them around forever.
+
+ SortedSet<EntryFile> tail = mAllFiles.contents.tailSet(new EntryFile(t + 10000));
+ EntryFile[] future = null;
+ if (!tail.isEmpty()) {
+ future = tail.toArray(new EntryFile[tail.size()]);
+ tail.clear(); // Remove from mAllFiles
+ }
+
+ if (!mAllFiles.contents.isEmpty()) {
+ t = Math.max(t, mAllFiles.contents.last().timestampMillis + 1);
+ }
+
+ if (future != null) {
+ for (EntryFile late : future) {
+ mAllFiles.blocks -= late.blocks;
+ FileList tagFiles = mFilesByTag.get(late.tag);
+ if (tagFiles.contents.remove(late)) tagFiles.blocks -= late.blocks;
+ if ((late.flags & DropBoxEntry.IS_EMPTY) == 0) {
+ enrollEntry(new EntryFile(
+ late.file, mDropBoxDir, late.tag, t++, late.flags, mBlockSize));
+ } else {
+ enrollEntry(new EntryFile(mDropBoxDir, late.tag, t++));
+ }
+ }
+ }
+
+ if (temp == null) {
+ enrollEntry(new EntryFile(mDropBoxDir, tag, t));
+ } else {
+ enrollEntry(new EntryFile(temp, mDropBoxDir, tag, t, flags, mBlockSize));
+ }
+ }
+
+ /**
+ * Trims the files on disk to make sure they aren't using too much space.
+ * @return the overall quota for storage (in bytes)
+ */
+ private synchronized long trimToFit() {
+ // Expunge aged items (including tombstones marking deleted data).
+
+ int ageSeconds = Settings.Gservices.getInt(mContentResolver,
+ Settings.Gservices.DROPBOX_AGE_SECONDS, DEFAULT_AGE_SECONDS);
+ long cutoffMillis = System.currentTimeMillis() - ageSeconds * 1000;
+ while (!mAllFiles.contents.isEmpty()) {
+ EntryFile entry = mAllFiles.contents.first();
+ if (entry.timestampMillis > cutoffMillis) break;
+
+ FileList tag = mFilesByTag.get(entry.tag);
+ if (tag != null && tag.contents.remove(entry)) tag.blocks -= entry.blocks;
+ if (mAllFiles.contents.remove(entry)) mAllFiles.blocks -= entry.blocks;
+ if (entry.file != null) entry.file.delete();
+ }
+
+ // Compute overall quota (a fraction of available free space) in blocks.
+ // The quota changes dynamically based on the amount of free space;
+ // that way when lots of data is available we can use it, but we'll get
+ // out of the way if storage starts getting tight.
+
+ long uptimeMillis = SystemClock.uptimeMillis();
+ if (uptimeMillis > mCachedQuotaUptimeMillis + QUOTA_RESCAN_MILLIS) {
+ int quotaPercent = Settings.Gservices.getInt(mContentResolver,
+ Settings.Gservices.DROPBOX_QUOTA_PERCENT, DEFAULT_QUOTA_PERCENT);
+ int reservePercent = Settings.Gservices.getInt(mContentResolver,
+ Settings.Gservices.DROPBOX_RESERVE_PERCENT, DEFAULT_RESERVE_PERCENT);
+ int quotaKb = Settings.Gservices.getInt(mContentResolver,
+ Settings.Gservices.DROPBOX_QUOTA_KB, DEFAULT_QUOTA_KB);
+
+ mStatFs.restat(mDropBoxDir.getPath());
+ int available = mStatFs.getAvailableBlocks();
+ int nonreserved = available - mStatFs.getBlockCount() * reservePercent / 100;
+ int maximum = quotaKb * 1024 / mBlockSize;
+ mCachedQuotaBlocks = Math.min(maximum, Math.max(0, nonreserved * quotaPercent / 100));
+ mCachedQuotaUptimeMillis = uptimeMillis;
+ }
+
+ // If we're using too much space, delete old items to make room.
+ //
+ // We trim each tag independently (this is why we keep per-tag lists).
+ // Space is "fairly" shared between tags -- they are all squeezed
+ // equally until enough space is reclaimed.
+ //
+ // A single circular buffer (a la logcat) would be simpler, but this
+ // way we can handle fat/bursty data (like 1MB+ bugreports, 300KB+
+ // kernel crash dumps, and 100KB+ ANR reports) without swamping small,
+ // well-behaved data // streams (event statistics, profile data, etc).
+ //
+ // Deleted files are replaced with zero-length tombstones to mark what
+ // was lost. Tombstones are expunged by age (see above).
+
+ if (mAllFiles.blocks > mCachedQuotaBlocks) {
+ Log.i(TAG, "Usage (" + mAllFiles.blocks + ") > Quota (" + mCachedQuotaBlocks + ")");
+
+ // Find a fair share amount of space to limit each tag
+ int unsqueezed = mAllFiles.blocks, squeezed = 0;
+ TreeSet<FileList> tags = new TreeSet<FileList>(mFilesByTag.values());
+ for (FileList tag : tags) {
+ if (squeezed > 0 && tag.blocks <= (mCachedQuotaBlocks - unsqueezed) / squeezed) {
+ break;
+ }
+ unsqueezed -= tag.blocks;
+ squeezed++;
+ }
+ int tagQuota = (mCachedQuotaBlocks - unsqueezed) / squeezed;
+
+ // Remove old items from each tag until it meets the per-tag quota.
+ for (FileList tag : tags) {
+ if (mAllFiles.blocks < mCachedQuotaBlocks) break;
+ while (tag.blocks > tagQuota && !tag.contents.isEmpty()) {
+ EntryFile entry = tag.contents.first();
+ if (tag.contents.remove(entry)) tag.blocks -= entry.blocks;
+ if (mAllFiles.contents.remove(entry)) mAllFiles.blocks -= entry.blocks;
+
+ try {
+ if (entry.file != null) entry.file.delete();
+ enrollEntry(new EntryFile(mDropBoxDir, entry.tag, entry.timestampMillis));
+ } catch (IOException e) {
+ Log.e(TAG, "Can't write tombstone file", e);
+ }
+ }
+ }
+ }
+
+ return mCachedQuotaBlocks * mBlockSize;
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b8cf844..1a7416a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -43,6 +43,7 @@
import android.util.Log;
import android.accounts.AccountManagerService;
+import java.io.File;
import java.util.Timer;
import java.util.TimerTask;
@@ -294,6 +295,14 @@
}
try {
+ Log.i(TAG, "DropBox Service");
+ ServiceManager.addService("dropbox",
+ new DropBoxService(context, new File("/data/system/dropbox")));
+ } catch (Throwable e) {
+ Log.e(TAG, "Failure starting DropBox Service", e);
+ }
+
+ try {
Log.i(TAG, "Checkin Service");
Intent intent = new Intent().setComponent(new ComponentName(
"com.google.android.server.checkin",
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 32ad6c6..89d969d 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -542,7 +542,7 @@
value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName);
if (!TextUtils.isEmpty(value)) {
- config.SSID = value;
+ config.SSID = removeDoubleQuotes(value);
} else {
config.SSID = null;
}
@@ -675,11 +675,21 @@
value = WifiNative.getNetworkVariableCommand(netId,
field.varName());
if (!TextUtils.isEmpty(value)) {
+ if (field != config.eap) value = removeDoubleQuotes(value);
field.setValue(value);
}
}
}
+ private static String removeDoubleQuotes(String string) {
+ if (string.length() <= 2) return "";
+ return string.substring(1, string.length() - 1);
+ }
+
+ private static String convertToQuotedString(String string) {
+ return "\"" + string + "\"";
+ }
+
/**
* see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)}
* @return the supplicant-assigned identifier for the new or updated
@@ -731,7 +741,7 @@
!WifiNative.setNetworkVariableCommand(
netId,
WifiConfiguration.ssidVarName,
- config.SSID)) {
+ convertToQuotedString(config.SSID))) {
if (DBG) {
Log.d(TAG, "failed to set SSID: "+config.SSID);
}
@@ -894,18 +904,22 @@
: config.enterpriseFields) {
String varName = field.varName();
String value = field.value();
- if ((value != null) && !WifiNative.setNetworkVariableCommand(
- netId,
- varName,
- value)) {
- if (DBG) {
- Log.d(TAG, config.SSID + ": failed to set " + varName +
- ": " + value);
+ if (value != null) {
+ if (field != config.eap) {
+ value = convertToQuotedString(value);
}
- break setVariables;
+ if (!WifiNative.setNetworkVariableCommand(
+ netId,
+ varName,
+ value)) {
+ if (DBG) {
+ Log.d(TAG, config.SSID + ": failed to set " + varName +
+ ": " + value);
+ }
+ break setVariables;
+ }
}
}
-
return netId;
}
diff --git a/tests/AndroidTests/Android.mk b/tests/AndroidTests/Android.mk
index ced796a..757044f 100644
--- a/tests/AndroidTests/Android.mk
+++ b/tests/AndroidTests/Android.mk
@@ -3,7 +3,7 @@
LOCAL_MODULE_TAGS := tests
-LOCAL_JAVA_LIBRARIES := framework-tests android.test.runner
+LOCAL_JAVA_LIBRARIES := framework-tests android.test.runner services
LOCAL_STATIC_JAVA_LIBRARIES := googlelogin-client
diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml
index d94327a..786178c 100644
--- a/tests/AndroidTests/AndroidManifest.xml
+++ b/tests/AndroidTests/AndroidManifest.xml
@@ -35,27 +35,27 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
- <uses-permission android:name="android.permission.READ_CONTACTS" />
- <uses-permission android:name="android.permission.WRITE_CONTACTS" />
- <uses-permission android:name="android.permission.WRITE_SETTINGS" />
- <uses-permission android:name="android.permission.READ_SMS"/>
- <uses-permission android:name="android.permission.WRITE_SMS"/>
- <uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
<uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
<uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
+ <uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
- <uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.WRITE_GSERVICES" />
- <uses-permission android:name="android.permission.WAKE_LOCK" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-
- <uses-permission android:name="com.android.unit_tests.permission.TEST_GRANTED" />
-
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+ <uses-permission android:name="android.permission.READ_LOGS"/>
+ <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_GSERVICES" />
+ <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+ <uses-permission android:name="android.permission.WRITE_SMS"/>
+ <uses-permission android:name="com.android.unit_tests.permission.TEST_GRANTED" />
+ <uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" />
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.ALL_SERVICES" />
- <uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" />
+
<!-- InstrumentationTestRunner for AndroidTests -->
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.android.unit_tests"
diff --git a/tests/AndroidTests/res/raw/v21_org_before_title.vcf b/tests/AndroidTests/res/raw/v21_org_before_title.vcf
new file mode 100644
index 0000000..8ff1190
--- /dev/null
+++ b/tests/AndroidTests/res/raw/v21_org_before_title.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+VERSION:2.1
+FN:Normal Guy
+ORG:Company;Organization;Devision;Room;Sheet No.
+TITLE:Excellent Janitor
+END:VCARD
diff --git a/tests/AndroidTests/res/raw/v21_pref_handling.vcf b/tests/AndroidTests/res/raw/v21_pref_handling.vcf
new file mode 100644
index 0000000..5105310
--- /dev/null
+++ b/tests/AndroidTests/res/raw/v21_pref_handling.vcf
@@ -0,0 +1,15 @@
+BEGIN:VCARD
+VERSION:2.1
+FN:Smith
+TEL;HOME:1
+TEL;WORK;PREF:2
+TEL;ISDN:3
+EMAIL;PREF;HOME:test@example.com
+EMAIL;CELL;PREF:test2@examination.com
+ORG:Company
+TITLE:Engineer
+ORG:Mystery
+TITLE:Blogger
+ORG:Poetry
+TITLE:Poet
+END:VCARD
diff --git a/tests/AndroidTests/res/raw/v21_title_before_org.vcf b/tests/AndroidTests/res/raw/v21_title_before_org.vcf
new file mode 100644
index 0000000..9fdc738
--- /dev/null
+++ b/tests/AndroidTests/res/raw/v21_title_before_org.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+VERSION:2.1
+FN:Nice Guy
+TITLE:Cool Title
+ORG:Marverous;Perfect;Great;Good;Bad;Poor
+END:VCARD
diff --git a/tests/AndroidTests/src/com/android/unit_tests/DropBoxTest.java b/tests/AndroidTests/src/com/android/unit_tests/DropBoxTest.java
new file mode 100644
index 0000000..77ebf8d
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/DropBoxTest.java
@@ -0,0 +1,479 @@
+/*
+ * 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;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.DropBoxEntry;
+import android.os.IDropBox;
+import android.os.ParcelFileDescriptor;
+import android.os.ServiceManager;
+import android.os.StatFs;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+
+import com.android.server.DropBoxService;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.InputStream;
+import java.util.Random;
+import java.util.zip.GZIPOutputStream;
+
+/** Test {@link IDropBox} functionality. */
+public class DropBoxTest extends AndroidTestCase {
+ public void tearDown() throws Exception {
+ Intent override = new Intent(Settings.Gservices.OVERRIDE_ACTION);
+ override.putExtra(Settings.Gservices.DROPBOX_AGE_SECONDS, "");
+ override.putExtra(Settings.Gservices.DROPBOX_QUOTA_KB, "");
+ override.putExtra(Settings.Gservices.DROPBOX_TAG_PREFIX + "DropBoxTest", "");
+ waitForBroadcast(override);
+ }
+
+ public void testAddText() throws Exception {
+ IDropBox dropbox = IDropBox.Stub.asInterface(ServiceManager.getService("dropbox"));
+ long before = System.currentTimeMillis();
+ Thread.sleep(5);
+ dropbox.addText("DropBoxTest", "TEST0");
+ Thread.sleep(5);
+ long between = System.currentTimeMillis();
+ Thread.sleep(5);
+ dropbox.addText("DropBoxTest", "TEST1");
+ dropbox.addText("DropBoxTest", "TEST2");
+ Thread.sleep(5);
+ long after = System.currentTimeMillis();
+
+ DropBoxEntry e0 = getNextTestEntry(dropbox, before);
+ DropBoxEntry e1 = getNextTestEntry(dropbox, e0.getTimeMillis());
+ DropBoxEntry e2 = getNextTestEntry(dropbox, e1.getTimeMillis());
+ assertTrue(null == getNextTestEntry(dropbox, e2.getTimeMillis()));
+
+ assertTrue(e0.getTimeMillis() > before);
+ assertTrue(e0.getTimeMillis() < between);
+ assertTrue(e1.getTimeMillis() > between);
+ assertTrue(e1.getTimeMillis() < e2.getTimeMillis());
+ assertTrue(e2.getTimeMillis() < after);
+
+ assertEquals("TEST0", e0.getText(80));
+ assertEquals("TEST1", e1.getText(80));
+ assertEquals("TES", e2.getText(3));
+
+ e0.close();
+ e1.close();
+ e2.close();
+ }
+
+ public void testAddData() throws Exception {
+ IDropBox dropbox = IDropBox.Stub.asInterface(ServiceManager.getService("dropbox"));
+ long before = System.currentTimeMillis();
+ dropbox.addData("DropBoxTest", "TEST".getBytes(), 0);
+ long after = System.currentTimeMillis();
+
+ DropBoxEntry e = getNextTestEntry(dropbox, before);
+ assertTrue(null == getNextTestEntry(dropbox, e.getTimeMillis()));
+
+ assertEquals("DropBoxTest", e.getTag());
+ assertTrue(e.getTimeMillis() >= before);
+ assertEquals(0, e.getFlags());
+ assertTrue(null == e.getText(80));
+
+ byte[] buf = new byte[80];
+ assertEquals("TEST", new String(buf, 0, e.getInputStream().read(buf)));
+
+ e.close();
+ }
+
+ public void testAddFile() throws Exception {
+ File dir = getEmptyDir("testAddFile");
+ long before = System.currentTimeMillis();
+
+ File f0 = new File(dir, "f0.txt");
+ File f1 = new File(dir, "f1.txt.gz");
+ File f2 = new File(dir, "f2.dat");
+ File f3 = new File(dir, "f2.dat.gz");
+
+ FileWriter w0 = new FileWriter(f0);
+ GZIPOutputStream gz1 = new GZIPOutputStream(new FileOutputStream(f1));
+ FileOutputStream os2 = new FileOutputStream(f2);
+ GZIPOutputStream gz3 = new GZIPOutputStream(new FileOutputStream(f3));
+
+ w0.write("FILE0");
+ gz1.write("FILE1".getBytes());
+ os2.write("DATA2".getBytes());
+ gz3.write("DATA3".getBytes());
+
+ w0.close();
+ gz1.close();
+ os2.close();
+ gz3.close();
+
+ IDropBox dropbox = IDropBox.Stub.asInterface(ServiceManager.getService("dropbox"));
+ int mode = ParcelFileDescriptor.MODE_READ_ONLY;
+
+ ParcelFileDescriptor pfd0 = ParcelFileDescriptor.open(f0, mode);
+ ParcelFileDescriptor pfd1 = ParcelFileDescriptor.open(f1, mode);
+ ParcelFileDescriptor pfd2 = ParcelFileDescriptor.open(f2, mode);
+ ParcelFileDescriptor pfd3 = ParcelFileDescriptor.open(f3, mode);
+
+ dropbox.addFile("DropBoxTest", pfd0, DropBoxEntry.IS_TEXT);
+ dropbox.addFile("DropBoxTest", pfd1, DropBoxEntry.IS_TEXT | DropBoxEntry.IS_GZIPPED);
+ dropbox.addFile("DropBoxTest", pfd2, 0);
+ dropbox.addFile("DropBoxTest", pfd3, DropBoxEntry.IS_GZIPPED);
+
+ pfd0.close();
+ pfd1.close();
+ pfd2.close();
+ pfd3.close();
+
+ DropBoxEntry e0 = getNextTestEntry(dropbox, before);
+ DropBoxEntry e1 = getNextTestEntry(dropbox, e0.getTimeMillis());
+ DropBoxEntry e2 = getNextTestEntry(dropbox, e1.getTimeMillis());
+ DropBoxEntry e3 = getNextTestEntry(dropbox, e2.getTimeMillis());
+ assertTrue(null == getNextTestEntry(dropbox, e3.getTimeMillis()));
+
+ assertTrue(e0.getTimeMillis() > before);
+ assertTrue(e1.getTimeMillis() > e0.getTimeMillis());
+ assertTrue(e2.getTimeMillis() > e1.getTimeMillis());
+ assertTrue(e3.getTimeMillis() > e2.getTimeMillis());
+
+ assertEquals(DropBoxEntry.IS_TEXT, e0.getFlags());
+ assertEquals(DropBoxEntry.IS_TEXT, e1.getFlags());
+ assertEquals(0, e2.getFlags());
+ assertEquals(0, e3.getFlags());
+
+ assertEquals("FILE0", e0.getText(80));
+
+ byte[] buf1 = new byte[80];
+ assertEquals("FILE1", new String(buf1, 0, e1.getInputStream().read(buf1)));
+
+ assertTrue(null == e2.getText(80));
+ byte[] buf2 = new byte[80];
+ assertEquals("DATA2", new String(buf2, 0, e2.getInputStream().read(buf2)));
+
+ assertTrue(null == e3.getText(80));
+ byte[] buf3 = new byte[80];
+ assertEquals("DATA3", new String(buf3, 0, e3.getInputStream().read(buf3)));
+
+ e0.close();
+ e1.close();
+ e2.close();
+ e3.close();
+ }
+
+ public void testAddEntriesInTheFuture() throws Exception {
+ File dir = getEmptyDir("testAddEntriesInTheFuture");
+ long before = System.currentTimeMillis();
+
+ // Near future: should be allowed to persist
+ FileWriter w0 = new FileWriter(new File(dir, "DropBoxTest@" + (before + 5000) + ".txt"));
+ w0.write("FUTURE0");
+ w0.close();
+
+ // Far future: should be collapsed
+ FileWriter w1 = new FileWriter(new File(dir, "DropBoxTest@" + (before + 100000) + ".txt"));
+ w1.write("FUTURE1");
+ w1.close();
+
+ // Another far future item, this one gzipped
+ File f2 = new File(dir, "DropBoxTest@" + (before + 100001) + ".txt.gz");
+ GZIPOutputStream gz2 = new GZIPOutputStream(new FileOutputStream(f2));
+ gz2.write("FUTURE2".getBytes());
+ gz2.close();
+
+ // Tombstone in the far future
+ new FileOutputStream(new File(dir, "DropBoxTest@" + (before + 100002) + ".lost")).close();
+
+ DropBoxService dropbox = new DropBoxService(getContext(), dir);
+
+ // Until a write, the timestamps are taken at face value
+ DropBoxEntry e0 = getNextTestEntry(dropbox, before);
+ DropBoxEntry e1 = getNextTestEntry(dropbox, e0.getTimeMillis());
+ DropBoxEntry e2 = getNextTestEntry(dropbox, e1.getTimeMillis());
+ DropBoxEntry e3 = getNextTestEntry(dropbox, e2.getTimeMillis());
+ assertTrue(null == getNextTestEntry(dropbox, e3.getTimeMillis()));
+
+ assertEquals("FUTURE0", e0.getText(80));
+ assertEquals("FUTURE1", e1.getText(80));
+ assertEquals("FUTURE2", e2.getText(80));
+ assertEquals(null, e3.getText(80));
+
+ assertEquals(before + 5000, e0.getTimeMillis());
+ assertEquals(before + 100000, e1.getTimeMillis());
+ assertEquals(before + 100001, e2.getTimeMillis());
+ assertEquals(before + 100002, e3.getTimeMillis());
+
+ e0.close();
+ e1.close();
+ e2.close();
+ e3.close();
+
+ // Write something to force a collapse
+ dropbox.addText("NotDropBoxTest", "FUTURE");
+ e0 = getNextTestEntry(dropbox, before);
+ e1 = getNextTestEntry(dropbox, e0.getTimeMillis());
+ e2 = getNextTestEntry(dropbox, e1.getTimeMillis());
+ e3 = getNextTestEntry(dropbox, e2.getTimeMillis());
+ assertTrue(null == getNextTestEntry(dropbox, e3.getTimeMillis()));
+
+ assertEquals("FUTURE0", e0.getText(80));
+ assertEquals("FUTURE1", e1.getText(80));
+ assertEquals("FUTURE2", e2.getText(80));
+ assertEquals(null, e3.getText(80));
+
+ assertEquals(before + 5000, e0.getTimeMillis());
+ assertEquals(before + 5001, e1.getTimeMillis());
+ assertEquals(before + 5002, e2.getTimeMillis());
+ assertEquals(before + 5003, e3.getTimeMillis());
+
+ e0.close();
+ e1.close();
+ e2.close();
+ e3.close();
+ dropbox.stop();
+ }
+
+ public void testIsTagEnabled() throws Exception {
+ IDropBox dropbox = IDropBox.Stub.asInterface(ServiceManager.getService("dropbox"));
+ long before = System.currentTimeMillis();
+ dropbox.addText("DropBoxTest", "TEST-ENABLED");
+ assertTrue(dropbox.isTagEnabled("DropBoxTest"));
+
+ Intent override = new Intent(Settings.Gservices.OVERRIDE_ACTION);
+ override.putExtra(Settings.Gservices.DROPBOX_TAG_PREFIX + "DropBoxTest", "disabled");
+ waitForBroadcast(override);
+
+ dropbox.addText("DropBoxTest", "TEST-DISABLED");
+ assertFalse(dropbox.isTagEnabled("DropBoxTest"));
+
+ override = new Intent(Settings.Gservices.OVERRIDE_ACTION);
+ override.putExtra(Settings.Gservices.DROPBOX_TAG_PREFIX + "DropBoxTest", "");
+ waitForBroadcast(override);
+
+ dropbox.addText("DropBoxTest", "TEST-ENABLED-AGAIN");
+ assertTrue(dropbox.isTagEnabled("DropBoxTest"));
+
+ DropBoxEntry e0 = getNextTestEntry(dropbox, before);
+ DropBoxEntry e1 = getNextTestEntry(dropbox, e0.getTimeMillis());
+ assertTrue(null == getNextTestEntry(dropbox, e1.getTimeMillis()));
+
+ assertEquals("TEST-ENABLED", e0.getText(80));
+ assertEquals("TEST-ENABLED-AGAIN", e1.getText(80));
+
+ e0.close();
+ e1.close();
+ }
+
+ public void testSizeLimits() throws Exception {
+ File dir = getEmptyDir("testSizeLimits");
+ int blockSize = new StatFs(dir.getPath()).getBlockSize();
+
+ // Limit storage to 10 blocks
+ int kb = blockSize * 10 / 1024;
+ Intent override = new Intent(Settings.Gservices.OVERRIDE_ACTION);
+ override.putExtra(Settings.Gservices.DROPBOX_QUOTA_KB, Integer.toString(kb));
+ waitForBroadcast(override);
+
+ // Three tags using a total of 12 blocks:
+ // DropBoxTest0 [ ][ ]
+ // DropBoxTest1 [x][ ][ ][ ][xxx(20 blocks)xxx]
+ // DropBoxTest2 [xxxxxxxxxx][ ][ ]
+ //
+ // The blocks marked "x" will be removed due to storage restrictions.
+ // Use random fill (so it doesn't compress), subtract a little for gzip overhead
+
+ final int overhead = 64;
+ long before = System.currentTimeMillis();
+ DropBoxService dropbox = new DropBoxService(getContext(), dir);
+ addRandomEntry(dropbox, "DropBoxTest0", blockSize - overhead);
+ addRandomEntry(dropbox, "DropBoxTest0", blockSize - overhead);
+
+ addRandomEntry(dropbox, "DropBoxTest1", blockSize - overhead);
+ addRandomEntry(dropbox, "DropBoxTest1", blockSize - overhead);
+ addRandomEntry(dropbox, "DropBoxTest1", blockSize * 2 - overhead);
+ addRandomEntry(dropbox, "DropBoxTest1", blockSize - overhead);
+ addRandomEntry(dropbox, "DropBoxTest1", blockSize * 20 - overhead);
+
+ addRandomEntry(dropbox, "DropBoxTest2", blockSize * 4 - overhead);
+ addRandomEntry(dropbox, "DropBoxTest2", blockSize - overhead);
+ addRandomEntry(dropbox, "DropBoxTest2", blockSize - overhead);
+
+ DropBoxEntry e0 = getNextTestEntry(dropbox, before);
+ DropBoxEntry e1 = getNextTestEntry(dropbox, e0.getTimeMillis());
+ DropBoxEntry e2 = getNextTestEntry(dropbox, e1.getTimeMillis());
+ DropBoxEntry e3 = getNextTestEntry(dropbox, e2.getTimeMillis());
+ DropBoxEntry e4 = getNextTestEntry(dropbox, e3.getTimeMillis());
+ DropBoxEntry e5 = getNextTestEntry(dropbox, e4.getTimeMillis());
+ DropBoxEntry e6 = getNextTestEntry(dropbox, e5.getTimeMillis());
+ DropBoxEntry e7 = getNextTestEntry(dropbox, e6.getTimeMillis());
+ DropBoxEntry e8 = getNextTestEntry(dropbox, e7.getTimeMillis());
+ DropBoxEntry e9 = getNextTestEntry(dropbox, e8.getTimeMillis());
+ assertTrue(null == getNextTestEntry(dropbox, e9.getTimeMillis()));
+
+ assertEquals("DropBoxTest0", e0.getTag());
+ assertEquals("DropBoxTest0", e1.getTag());
+ assertEquals(blockSize - overhead, getEntrySize(e0));
+ assertEquals(blockSize - overhead, getEntrySize(e1));
+
+ assertEquals("DropBoxTest1", e2.getTag());
+ assertEquals("DropBoxTest1", e3.getTag());
+ assertEquals("DropBoxTest1", e4.getTag());
+ assertEquals("DropBoxTest1", e5.getTag());
+ assertEquals("DropBoxTest1", e6.getTag());
+ assertEquals(-1, getEntrySize(e2)); // Tombstone
+ assertEquals(blockSize - overhead, getEntrySize(e3));
+ assertEquals(blockSize * 2 - overhead, getEntrySize(e4));
+ assertEquals(blockSize - overhead, getEntrySize(e5));
+ assertEquals(-1, getEntrySize(e6));
+
+ assertEquals("DropBoxTest2", e7.getTag());
+ assertEquals("DropBoxTest2", e8.getTag());
+ assertEquals("DropBoxTest2", e9.getTag());
+ assertEquals(-1, getEntrySize(e7)); // Tombstone
+ assertEquals(blockSize - overhead, getEntrySize(e8));
+ assertEquals(blockSize - overhead, getEntrySize(e9));
+
+ e0.close();
+ e1.close();
+ e2.close();
+ e3.close();
+ e4.close();
+ e5.close();
+ e6.close();
+ e7.close();
+ e8.close();
+ e9.close();
+ dropbox.stop();
+ }
+
+ public void testAgeLimits() throws Exception {
+ File dir = getEmptyDir("testAgeLimits");
+ int blockSize = new StatFs(dir.getPath()).getBlockSize();
+
+ // Limit storage to 10 blocks with an expiration of 1 second
+ int kb = blockSize * 10 / 1024;
+ Intent override = new Intent(Settings.Gservices.OVERRIDE_ACTION);
+ override.putExtra(Settings.Gservices.DROPBOX_AGE_SECONDS, "1");
+ override.putExtra(Settings.Gservices.DROPBOX_QUOTA_KB, Integer.toString(kb));
+ waitForBroadcast(override);
+
+ // Write one normal entry and another so big that it is instantly tombstoned
+ long before = System.currentTimeMillis();
+ DropBoxService dropbox = new DropBoxService(getContext(), dir);
+ dropbox.addText("DropBoxTest", "TEST");
+ addRandomEntry(dropbox, "DropBoxTest", blockSize * 20);
+
+ // Verify that things are as expected
+ DropBoxEntry e0 = getNextTestEntry(dropbox, before);
+ DropBoxEntry e1 = getNextTestEntry(dropbox, e0.getTimeMillis());
+ assertTrue(null == getNextTestEntry(dropbox, e1.getTimeMillis()));
+
+ assertEquals("TEST", e0.getText(80));
+ assertEquals(null, e1.getText(80));
+ assertEquals(-1, getEntrySize(e1));
+
+ e0.close();
+ e1.close();
+
+ // Wait a second and write another entry -- old ones should be expunged
+ Thread.sleep(2000);
+ dropbox.addText("DropBoxTest", "TEST1");
+
+ e0 = getNextTestEntry(dropbox, before);
+ assertTrue(null == getNextTestEntry(dropbox, e0.getTimeMillis()));
+ assertEquals("TEST1", e0.getText(80));
+ e0.close();
+ }
+
+ public void testCreateDropBoxWithInvalidDirectory() throws Exception {
+ // If created with an invalid directory, the DropBox should suffer quietly
+ // and fail all operations (this is how it survives a full disk).
+ // Once the directory becomes possible to create, it will start working.
+
+ File dir = new File(getEmptyDir("testCreateDropBoxWith"), "InvalidDirectory");
+ new FileOutputStream(dir).close(); // Create an empty file
+ DropBoxService dropbox = new DropBoxService(getContext(), dir);
+
+ dropbox.addText("DropBoxTest", "should be ignored");
+ dropbox.addData("DropBoxTest", "should be ignored".getBytes(), 0);
+ assertTrue(null == getNextTestEntry(dropbox, 0));
+
+ dir.delete(); // Remove the file so a directory can be created
+ dropbox.addText("DropBoxTest", "TEST");
+ DropBoxEntry e = getNextTestEntry(dropbox, 0);
+ assertTrue(null == getNextTestEntry(dropbox, e.getTimeMillis()));
+ assertEquals("DropBoxTest", e.getTag());
+ assertEquals("TEST", e.getText(80));
+ e.close();
+ dropbox.stop();
+ }
+
+ private void addRandomEntry(IDropBox dropbox, String tag, int size) throws Exception {
+ byte[] bytes = new byte[size];
+ new Random(System.currentTimeMillis()).nextBytes(bytes);
+
+ File f = new File(getEmptyDir("addRandomEntry"), "random.dat");
+ FileOutputStream os = new FileOutputStream(f);
+ os.write(bytes);
+ os.close();
+
+ ParcelFileDescriptor fd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
+ dropbox.addFile(tag, fd, 0);
+ fd.close();
+ }
+
+ private int getEntrySize(DropBoxEntry e) throws Exception {
+ InputStream is = e.getInputStream();
+ if (is == null) return -1;
+ int length = 0;
+ while (is.read() != -1) length++;
+ return length;
+ }
+
+ private void waitForBroadcast(Intent intent) throws InterruptedException {
+ BroadcastReceiver receiver = new BroadcastReceiver() {
+ public synchronized void onReceive(Context context, Intent intent) { notify(); }
+ };
+
+ getContext().sendOrderedBroadcast(intent, null, receiver, null, 0, null, null);
+ synchronized (receiver) { receiver.wait(); }
+ }
+
+ private void recursiveDelete(File file) {
+ if (!file.delete() && file.isDirectory()) {
+ for (File f : file.listFiles()) recursiveDelete(f);
+ file.delete();
+ }
+ }
+
+ private File getEmptyDir(String name) {
+ File dir = getContext().getDir("DropBoxTest." + name, 0);
+ for (File f : dir.listFiles()) recursiveDelete(f);
+ assertTrue(dir.listFiles().length == 0);
+ return dir;
+ }
+
+ private DropBoxEntry getNextTestEntry(IDropBox dropbox, long millis) throws Exception {
+ for (;;) {
+ DropBoxEntry entry = dropbox.getNextEntry(millis);
+ if (entry == null || entry.getTag().startsWith("DropBoxTest")) return entry;
+ entry.close();
+ millis = entry.getTimeMillis();
+ }
+ }
+}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/MockCursor.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/MockCursor.java
new file mode 100644
index 0000000..2523b0d
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/MockCursor.java
@@ -0,0 +1,254 @@
+/*
+ * 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.ContentResolver;
+import android.database.CharArrayBuffer;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.DataSetObserver;
+import android.net.Uri;
+import android.os.Bundle;
+
+import java.util.Map;
+
+public class MockCursor implements Cursor {
+ public int getColumnCount() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public int getColumnIndex(String columnName) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public int getColumnIndexOrThrow(String columnName) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public String getColumnName(int columnIndex) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public String[] getColumnNames() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public int getCount() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public boolean isNull(int columnIndex) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public int getInt(int columnIndex) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public long getLong(int columnIndex) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public short getShort(int columnIndex) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public float getFloat(int columnIndex) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public double getDouble(int columnIndex) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public byte[] getBlob(int columnIndex) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public String getString(int columnIndex) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public Bundle getExtras() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public int getPosition() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public boolean isAfterLast() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public boolean isBeforeFirst() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public boolean isFirst() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public boolean isLast() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public boolean move(int offset) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public boolean moveToFirst() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public boolean moveToLast() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public boolean moveToNext() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public boolean moveToPrevious() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public boolean moveToPosition(int position) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public void deactivate() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public void close() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public boolean isClosed() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public boolean requery() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public void registerContentObserver(ContentObserver observer) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public void registerDataSetObserver(DataSetObserver observer) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public Bundle respond(Bundle extras) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public boolean getWantsAllOnMoveCalls() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @SuppressWarnings("deprecation")
+ public boolean commitUpdates() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @SuppressWarnings("deprecation")
+ public boolean commitUpdates(Map<? extends Long, ? extends Map<String, Object>> values) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @SuppressWarnings("deprecation")
+ public boolean hasUpdates() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @SuppressWarnings("deprecation")
+ public void setNotificationUri(ContentResolver cr, Uri uri) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @SuppressWarnings("deprecation")
+ public boolean supportsUpdates() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @SuppressWarnings("deprecation")
+ public boolean deleteRow() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @SuppressWarnings("deprecation")
+ public void unregisterContentObserver(ContentObserver observer) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @SuppressWarnings("deprecation")
+ public void unregisterDataSetObserver(DataSetObserver observer) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @SuppressWarnings("deprecation")
+ public boolean updateBlob(int columnIndex, byte[] value) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @SuppressWarnings("deprecation")
+ public boolean updateDouble(int columnIndex, double value) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @SuppressWarnings("deprecation")
+ public boolean updateFloat(int columnIndex, float value) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @SuppressWarnings("deprecation")
+ public boolean updateInt(int columnIndex, int value) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @SuppressWarnings("deprecation")
+ public boolean updateLong(int columnIndex, long value) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @SuppressWarnings("deprecation")
+ public boolean updateShort(int columnIndex, short value) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @SuppressWarnings("deprecation")
+ public boolean updateString(int columnIndex, String value) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @SuppressWarnings("deprecation")
+ public boolean updateToNull(int columnIndex) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @SuppressWarnings("deprecation")
+ public void abortUpdates() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+}
\ No newline at end of file
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java
index 0ee74df..d93a41b 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java
@@ -16,19 +16,21 @@
package com.android.unit_tests.vcard;
import android.content.ContentValues;
-
-import org.apache.commons.codec.binary.Base64;
+import android.pim.vcard.ContactStruct;
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
+ * Previously used in main vCard handling code but now exists only for testing.
+ *
+ * Especially useful for testing parser code (VCardParser), since all properties can be
+ * checked via this class unlike {@link ContactStruct}, which only emits the result of
+ * interpretation of the content of each vCard. We cannot know whether vCard parser or
+ * ContactStruct is wrong withouth this class.
*/
public class PropertyNode {
public String propName;
@@ -101,6 +103,15 @@
}
@Override
+ public int hashCode() {
+ // vCard may contain more than one same line in one entry, while HashSet or any other
+ // library which utilize hashCode() does not honor that, so intentionally throw an
+ // Exception.
+ throw new UnsupportedOperationException(
+ "PropertyNode does not provide hashCode() implementation intentionally.");
+ }
+
+ @Override
public boolean equals(Object obj) {
if (!(obj instanceof PropertyNode)) {
return false;
@@ -159,164 +170,4 @@
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/vcard/PropertyNodesVerifier.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNodesVerifier.java
new file mode 100644
index 0000000..b009e2d
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNodesVerifier.java
@@ -0,0 +1,235 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ * Utility class which verifies input VNode.
+ *
+ * This class first checks whether each propertyNode in the VNode is in the
+ * "ordered expected property list".
+ * If the node does not exist in the "ordered list", the class refers to
+ * "unorderd expected property set" and checks the node is expected somewhere.
+ */
+public class PropertyNodesVerifier {
+ public static class TypeSet extends HashSet<String> {
+ public TypeSet(String ... array) {
+ super(Arrays.asList(array));
+ }
+ }
+
+ public static class GroupSet extends HashSet<String> {
+ public GroupSet(String ... array) {
+ super(Arrays.asList(array));
+ }
+ }
+
+ private final HashMap<String, List<PropertyNode>> mOrderedNodeMap;
+ // Intentionally use ArrayList instead of Set, assuming there may be more than one
+ // exactly same objects.
+ private final ArrayList<PropertyNode> mUnorderedNodeList;
+ private final TestCase mTestCase;
+
+ public PropertyNodesVerifier(TestCase testCase) {
+ mOrderedNodeMap = new HashMap<String, List<PropertyNode>>();
+ mUnorderedNodeList = new ArrayList<PropertyNode>();
+ mTestCase = testCase;
+ }
+
+ // WithOrder
+
+ public PropertyNodesVerifier addNodeWithOrder(String propName, String propValue) {
+ return addNodeWithOrder(propName, propValue, null, null, null, null, null);
+ }
+
+ public PropertyNodesVerifier addNodeWithOrder(String propName, String propValue,
+ List<String> propValueList) {
+ return addNodeWithOrder(propName, propValue, propValueList, null, null, null, null);
+ }
+
+ public PropertyNodesVerifier addNodeWithOrder(String propName, String propValue,
+ TypeSet paramMap_TYPE) {
+ return addNodeWithOrder(propName, propValue, null, null, null, paramMap_TYPE, null);
+ }
+
+ public PropertyNodesVerifier addNodeWithOrder(String propName, String propValue,
+ List<String> propValueList, TypeSet paramMap_TYPE) {
+ return addNodeWithOrder(propName, propValue, propValueList, null, null,
+ paramMap_TYPE, null);
+ }
+
+ public PropertyNodesVerifier addNodeWithOrder(String propName, String propValue,
+ List<String> propValueList, byte[] propValue_bytes,
+ ContentValues paramMap, TypeSet paramMap_TYPE, GroupSet propGroupSet) {
+ PropertyNode propertyNode = new PropertyNode(propName,
+ propValue, propValueList, propValue_bytes,
+ paramMap, paramMap_TYPE, propGroupSet);
+ List<PropertyNode> expectedNodeList = mOrderedNodeMap.get(propName);
+ if (expectedNodeList == null) {
+ expectedNodeList = new ArrayList<PropertyNode>();
+ mOrderedNodeMap.put(propName, expectedNodeList);
+ }
+ expectedNodeList.add(propertyNode);
+ return this;
+ }
+
+ // WithoutOrder
+
+ public PropertyNodesVerifier addNodeWithoutOrder(String propName, String propValue) {
+ return addNodeWithoutOrder(propName, propValue, null, null, null, null, null);
+ }
+
+ public PropertyNodesVerifier addNodeWithoutOrder(String propName, String propValue,
+ List<String> propValueList) {
+ return addNodeWithoutOrder(propName, propValue, propValueList, null, null, null, null);
+ }
+
+ public PropertyNodesVerifier addNodeWithoutOrder(String propName, String propValue,
+ TypeSet paramMap_TYPE) {
+ return addNodeWithoutOrder(propName, propValue, null, null, null, paramMap_TYPE, null);
+ }
+
+ public PropertyNodesVerifier addNodeWithoutOrder(String propName, String propValue,
+ List<String> propValueList, TypeSet paramMap_TYPE) {
+ return addNodeWithoutOrder(propName, propValue, propValueList, null, null,
+ paramMap_TYPE, null);
+ }
+
+ public PropertyNodesVerifier addNodeWithoutOrder(String propName, String propValue,
+ List<String> propValueList, byte[] propValue_bytes,
+ ContentValues paramMap, TypeSet paramMap_TYPE, GroupSet propGroupSet) {
+ mUnorderedNodeList.add(new PropertyNode(propName, propValue,
+ propValueList, propValue_bytes, paramMap, paramMap_TYPE, propGroupSet));
+ return this;
+ }
+
+ public void verify(VNode vnode) {
+ for (PropertyNode actualNode : vnode.propList) {
+ verifyNode(actualNode.propName, actualNode);
+ }
+ if (!mOrderedNodeMap.isEmpty() || !mUnorderedNodeList.isEmpty()) {
+ List<String> expectedProps = new ArrayList<String>();
+ for (List<PropertyNode> nodes : mOrderedNodeMap.values()) {
+ for (PropertyNode node : nodes) {
+ if (!expectedProps.contains(node.propName)) {
+ expectedProps.add(node.propName);
+ }
+ }
+ }
+ for (PropertyNode node : mUnorderedNodeList) {
+ if (!expectedProps.contains(node.propName)) {
+ expectedProps.add(node.propName);
+ }
+ }
+ mTestCase.fail("Expected property " + Arrays.toString(expectedProps.toArray())
+ + " was not found.");
+ }
+ }
+
+ private void verifyNode(final String propName, final PropertyNode actualNode) {
+ List<PropertyNode> expectedNodeList = mOrderedNodeMap.get(propName);
+ final int size = (expectedNodeList != null ? expectedNodeList.size() : 0);
+ if (size > 0) {
+ for (int i = 0; i < size; i++) {
+ PropertyNode expectedNode = expectedNodeList.get(i);
+ List<PropertyNode> expectedButDifferentValueList =
+ new ArrayList<PropertyNode>();
+ if (expectedNode.propName.equals(propName)) {
+ if (expectedNode.equals(actualNode)) {
+ expectedNodeList.remove(i);
+ if (expectedNodeList.size() == 0) {
+ mOrderedNodeMap.remove(propName);
+ }
+ return;
+ } else {
+ expectedButDifferentValueList.add(expectedNode);
+ }
+ }
+
+ // "actualNode" is not in ordered expected list.
+ // Try looking over unordered expected list.
+ if (tryFoundExpectedNodeFromUnorderedList(actualNode,
+ expectedButDifferentValueList)) {
+ return;
+ }
+
+ if (!expectedButDifferentValueList.isEmpty()) {
+ // Same propName exists but with different value(s).
+ failWithExpectedNodeList(propName, actualNode,
+ expectedButDifferentValueList);
+ } else {
+ // There's no expected node with same propName.
+ mTestCase.fail("Unexpected property \"" + propName + "\" exists.");
+ }
+ }
+ } else {
+ List<PropertyNode> expectedButDifferentValueList =
+ new ArrayList<PropertyNode>();
+ if (tryFoundExpectedNodeFromUnorderedList(actualNode, expectedButDifferentValueList)) {
+ return;
+ } else {
+ if (!expectedButDifferentValueList.isEmpty()) {
+ // Same propName exists but with different value(s).
+ failWithExpectedNodeList(propName, actualNode,
+ expectedButDifferentValueList);
+ } else {
+ // There's no expected node with same propName.
+ mTestCase.fail("Unexpected property \"" + propName + "\" exists.");
+ }
+ }
+ }
+ }
+
+ private boolean tryFoundExpectedNodeFromUnorderedList(PropertyNode actualNode,
+ List<PropertyNode> expectedButDifferentValueList) {
+ final String propName = actualNode.propName;
+ int unorderedListSize = mUnorderedNodeList.size();
+ for (int i = 0; i < unorderedListSize; i++) {
+ PropertyNode unorderedExpectedNode = mUnorderedNodeList.get(i);
+ if (unorderedExpectedNode.propName.equals(propName)) {
+ if (unorderedExpectedNode.equals(actualNode)) {
+ mUnorderedNodeList.remove(i);
+ return true;
+ }
+ expectedButDifferentValueList.add(unorderedExpectedNode);
+ }
+ }
+ return false;
+ }
+
+ private void failWithExpectedNodeList(String propName, PropertyNode actualNode,
+ List<PropertyNode> expectedNodeList) {
+ StringBuilder builder = new StringBuilder();
+ for (PropertyNode expectedNode : expectedNodeList) {
+ builder.append("expected: ");
+ builder.append(expectedNode.toString());
+ builder.append("\n");
+ }
+ mTestCase.fail("Property \"" + propName + "\" has wrong value.\n"
+ + builder.toString()
+ + " actual: " + actualNode.toString());
+ }
+}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardExporterTests.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardExporterTests.java
new file mode 100644
index 0000000..976474f
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardExporterTests.java
@@ -0,0 +1,1211 @@
+/*
+ * 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 com.android.unit_tests.vcard.PropertyNodesVerifier.TypeSet;
+
+import android.content.ContentProvider;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Entity;
+import android.content.EntityIterator;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.database.CursorWindow;
+import android.database.IBulkCursor;
+import android.database.IContentObserver;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.pim.vcard.VCardComposer;
+import android.pim.vcard.VCardConfig;
+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.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.test.AndroidTestCase;
+import android.test.mock.MockContentResolver;
+import android.test.mock.MockContext;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ * Almost a dead copy of android.test.mock.MockContentProvider, but different in that this
+ * class extends ContentProvider, not implementing IContentProvider,
+ * so that MockContentResolver is able to accept this class :(
+ */
+class MockContentProvider extends ContentProvider {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public int bulkInsert(Uri url, ContentValues[] initialValues) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @SuppressWarnings("unused")
+ public IBulkCursor bulkQuery(Uri url, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder, IContentObserver observer,
+ CursorWindow window) throws RemoteException {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @Override
+ @SuppressWarnings("unused")
+ public int delete(Uri url, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @Override
+ public String getType(Uri url) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @Override
+ public Uri insert(Uri url, ContentValues initialValues) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri url, String mode) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @Override
+ public AssetFileDescriptor openAssetFile(Uri uri, String mode) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @Override
+ public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @Override
+ public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public EntityIterator queryEntities(Uri url, String selection, String[] selectionArgs,
+ String sortOrder) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ @Override
+ public int update(Uri url, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public IBinder asBinder() {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+}
+
+/**
+ * Tests for the code related to vCard exporter, inculding vCard composer.
+ * This test class depends on vCard importer code, so if tests for vCard importer fail,
+ * the result of this class will not be reliable.
+ */
+public class VCardExporterTests extends AndroidTestCase {
+ private static final int V21 = 0;
+ private static final int V30 = 1;
+
+ private static final byte[] sPhotoByteArray =
+ VCardImporterTests.sPhotoByteArrayForComplicatedCase;
+
+ public class ExportTestResolver extends MockContentResolver {
+ ExportTestProvider mProvider = new ExportTestProvider();
+ public ExportTestResolver() {
+ addProvider(VCardComposer.VCARD_TEST_AUTHORITY, mProvider);
+ addProvider(RawContacts.CONTENT_URI.getAuthority(), mProvider);
+ }
+
+ public ContentValues buildData(String mimeType) {
+ return mProvider.buildData(mimeType);
+ }
+ }
+
+ public static class MockEntityIterator implements EntityIterator {
+ Collection<Entity> mEntityCollection;
+ Iterator<Entity> mIterator;
+
+ // TODO: Support multiple vCard entries.
+ public MockEntityIterator(Collection<ContentValues> contentValuesCollection) {
+ mEntityCollection = new ArrayList<Entity>();
+ Entity entity = new Entity(new ContentValues());
+ for (ContentValues contentValues : contentValuesCollection) {
+ entity.addSubValue(Data.CONTENT_URI, contentValues);
+ }
+ mEntityCollection.add(entity);
+ mIterator = mEntityCollection.iterator();
+ }
+
+ public boolean hasNext() {
+ return mIterator.hasNext();
+ }
+
+ public Entity next() {
+ return mIterator.next();
+ }
+
+ public void reset() {
+ mIterator = mEntityCollection.iterator();
+ }
+
+ public void close() {
+ }
+ }
+
+ public class ExportTestProvider extends MockContentProvider {
+ List<ContentValues> mContentValuesList = new ArrayList<ContentValues>();
+ public ContentValues buildData(String mimeType) {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(Data.MIMETYPE, mimeType);
+ mContentValuesList.add(contentValues);
+ return contentValues;
+ }
+
+ @Override
+ public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
+ String sortOrder) {
+ assert(uri != null);
+ assert(ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()));
+ final String authority = uri.getAuthority();
+ assert(RawContacts.CONTENT_URI.getAuthority().equals(authority));
+
+ return new MockEntityIterator(mContentValuesList);
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ assert(VCardComposer.CONTACTS_TEST_CONTENT_URI.equals(uri));
+ // Support multiple rows.
+ return new MockCursor() {
+ int mCurrentPosition = -1;
+
+ @Override
+ public int getCount() {
+ return 1;
+ }
+
+ @Override
+ public boolean moveToFirst() {
+ mCurrentPosition = 0;
+ return true;
+ }
+
+ @Override
+ public boolean moveToNext() {
+ if (mCurrentPosition == 0 || mCurrentPosition == -1) {
+ mCurrentPosition++;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isBeforeFirst() {
+ return mCurrentPosition < 0;
+ }
+
+ @Override
+ public boolean isAfterLast() {
+ return mCurrentPosition > 0;
+ }
+
+ @Override
+ public int getColumnIndex(String columnName) {
+ assertEquals(Contacts._ID, columnName);
+ return 0;
+ }
+
+ @Override
+ public int getInt(int columnIndex) {
+ assertEquals(0, columnIndex);
+ return 0;
+ }
+
+ @Override
+ public String getString(int columnIndex) {
+ return String.valueOf(getInt(columnIndex));
+ }
+
+ @Override
+ public void close() {
+ }
+ };
+ }
+ }
+
+ public static class VCardVerificationHandler implements VCardComposer.OneEntryHandler {
+ final private TestCase mTestCase;
+ final private List<PropertyNodesVerifier> mPropertyNodesVerifierList;
+ final private boolean mIsV30;
+ int mCount;
+
+ public VCardVerificationHandler(TestCase testCase, int version) {
+ mTestCase = testCase;
+ mPropertyNodesVerifierList = new ArrayList<PropertyNodesVerifier>();
+ mIsV30 = (version == V30);
+ mCount = 1;
+ }
+
+ public PropertyNodesVerifier addNewVerifier() {
+ PropertyNodesVerifier verifier = new PropertyNodesVerifier(mTestCase);
+ mPropertyNodesVerifierList.add(verifier);
+ verifier.addNodeWithOrder("VERSION", mIsV30 ? "3.0" : "2.1");
+ return verifier;
+ }
+
+ public PropertyNodesVerifier addNewVerifierWithEmptyName() {
+ PropertyNodesVerifier verifier = addNewVerifier();
+ if (mIsV30) {
+ verifier.addNodeWithOrder("N", "").addNodeWithOrder("FN", "");
+ }
+ return verifier;
+ }
+
+ public boolean onInit(Context context) {
+ return true;
+ }
+
+ public boolean onEntryCreated(String vcard) {
+ if (mPropertyNodesVerifierList.size() == 0) {
+ mTestCase.fail("Too many vCard entries seems to be inserted(No."
+ + mCount + " of the entries (No.1 is the first entry))");
+ }
+ PropertyNodesVerifier propertyNodesVerifier =
+ mPropertyNodesVerifierList.get(0);
+ mPropertyNodesVerifierList.remove(0);
+ VCardParser parser = (mIsV30 ? new VCardParser_V30(true) : new VCardParser_V21());
+ VNodeBuilder builder = new VNodeBuilder();
+ InputStream is;
+ try {
+ is = new ByteArrayInputStream(vcard.getBytes("UTF-8"));
+ mTestCase.assertEquals(true, parser.parse(is, null, builder));
+ is.close();
+ mTestCase.assertEquals(1, builder.vNodeList.size());
+ propertyNodesVerifier.verify(builder.vNodeList.get(0));
+ } catch (IOException e) {
+ mTestCase.fail("Unexpected IOException: " + e.getMessage());
+ } catch (VCardException e) {
+ mTestCase.fail("Unexpected VCardException: " + e.getMessage());
+ } finally {
+ mCount++;
+ }
+ return true;
+ }
+
+ public void onTerminate() {
+ }
+ }
+
+ private class CustomMockContext extends MockContext {
+ final ContentResolver mResolver;
+ public CustomMockContext(ContentResolver resolver) {
+ mResolver = resolver;
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return mResolver;
+ }
+ }
+
+ //// Followings are actual tests ////
+
+ private void verifyOneComposition(ExportTestResolver resolver,
+ VCardVerificationHandler handler, int version) {
+ final boolean isV30 = (version == V30);
+
+ int vcardType = (isV30 ? VCardConfig.VCARD_TYPE_V30_GENERIC_UTF8
+ : VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
+ VCardComposer composer = new VCardComposer(new CustomMockContext(resolver), vcardType);
+ composer.addHandler(handler);
+ if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) {
+ fail("init() failed. Reason: " + composer.getErrorReason());
+ }
+ assertFalse(composer.isAfterLast());
+ assertTrue(composer.createOneEntry());
+ assertTrue(composer.isAfterLast());
+ composer.terminate();
+ }
+
+ public void testSimpleV21() {
+ ExportTestResolver resolver = new ExportTestResolver();
+ ContentValues contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "Ando");
+ contentValues.put(StructuredName.GIVEN_NAME, "Roid");
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, V21);
+ handler.addNewVerifier()
+ .addNodeWithoutOrder("FN", "Roid Ando")
+ .addNodeWithoutOrder("N", "Ando;Roid;;;", Arrays.asList("Ando", "Roid", "", "", ""));
+
+ verifyOneComposition(resolver, handler, V21);
+ }
+
+ private void testStructuredNameBasic(int version) {
+ final boolean isV30 = (version == V30);
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "AppropriateFamilyName");
+ contentValues.put(StructuredName.GIVEN_NAME, "AppropriateGivenName");
+ contentValues.put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName");
+ contentValues.put(StructuredName.PREFIX, "AppropriatePrefix");
+ contentValues.put(StructuredName.SUFFIX, "AppropriateSuffix");
+ contentValues.put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily");
+ contentValues.put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven");
+ contentValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle");
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ PropertyNodesVerifier verifier = handler.addNewVerifier()
+ .addNodeWithOrder("N",
+ "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
+ + "AppropriatePrefix;AppropriateSuffix",
+ Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
+ "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
+ .addNodeWithOrder("FN",
+ "AppropriatePrefix AppropriateGivenName "
+ + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix")
+ .addNodeWithoutOrder("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
+ .addNodeWithoutOrder("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
+ .addNodeWithoutOrder("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
+
+ if (isV30) {
+ verifier.addNodeWithoutOrder("SORT-STRING",
+ "AppropriatePhoneticGiven AppropriatePhoneticMiddle "
+ + "AppropriatePhoneticFamily");
+ }
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testStructuredNameBasicV21() {
+ testStructuredNameBasic(V21);
+ }
+
+ public void testStructuredNameBasicV30() {
+ testStructuredNameBasic(V30);
+ }
+
+ /**
+ * Test that only "primary" StructuredName is emitted, so that our vCard file
+ * will not confuse the external importer, assuming there may be some importer
+ * which presume that there's only one property toward each of "N", "FN", etc.
+ * Note that more than one "N", "FN", etc. properties are acceptable in vCard spec.
+ */
+ private void testStructuredNameUsePrimaryCommon(int version) {
+ final boolean isV30 = (version == V30);
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1");
+ contentValues.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1");
+ contentValues.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName1");
+ contentValues.put(StructuredName.PREFIX, "DoNotEmitPrefix1");
+ contentValues.put(StructuredName.SUFFIX, "DoNotEmitSuffix1");
+ contentValues.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily1");
+ contentValues.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven1");
+ contentValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle1");
+
+ // With "IS_PRIMARY=1". This is what we should use.
+ contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "AppropriateFamilyName");
+ contentValues.put(StructuredName.GIVEN_NAME, "AppropriateGivenName");
+ contentValues.put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName");
+ contentValues.put(StructuredName.PREFIX, "AppropriatePrefix");
+ contentValues.put(StructuredName.SUFFIX, "AppropriateSuffix");
+ contentValues.put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily");
+ contentValues.put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven");
+ contentValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle");
+ contentValues.put(StructuredName.IS_PRIMARY, 1);
+
+ // With "IS_PRIMARY=1", but we should ignore this time, since this is second, not first.
+ contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName2");
+ contentValues.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName2");
+ contentValues.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName2");
+ contentValues.put(StructuredName.PREFIX, "DoNotEmitPrefix2");
+ contentValues.put(StructuredName.SUFFIX, "DoNotEmitSuffix2");
+ contentValues.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily2");
+ contentValues.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven2");
+ contentValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle2");
+ contentValues.put(StructuredName.IS_PRIMARY, 1);
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ PropertyNodesVerifier verifier = handler.addNewVerifier()
+ .addNodeWithOrder("N",
+ "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
+ + "AppropriatePrefix;AppropriateSuffix",
+ Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
+ "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
+ .addNodeWithOrder("FN",
+ "AppropriatePrefix AppropriateGivenName "
+ + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix")
+ .addNodeWithoutOrder("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
+ .addNodeWithoutOrder("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
+ .addNodeWithoutOrder("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
+
+ if (isV30) {
+ verifier.addNodeWithoutOrder("SORT-STRING",
+ "AppropriatePhoneticGiven AppropriatePhoneticMiddle "
+ + "AppropriatePhoneticFamily");
+ }
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testStructuredNameUsePrimaryV21() {
+ testStructuredNameUsePrimaryCommon(V21);
+ }
+
+ public void testStructuredNameUsePrimaryV30() {
+ testStructuredNameUsePrimaryCommon(V30);
+ }
+
+ /**
+ * Tests that only "super primary" StructuredName is emitted.
+ * See also the comment in {@link #testStructuredNameUsePrimaryCommon(int)}.
+ */
+ private void testStructuredNameUseSuperPrimaryCommon(int version) {
+ final boolean isV30 = (version == V30);
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1");
+ contentValues.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1");
+ contentValues.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName1");
+ contentValues.put(StructuredName.PREFIX, "DoNotEmitPrefix1");
+ contentValues.put(StructuredName.SUFFIX, "DoNotEmitSuffix1");
+ contentValues.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily1");
+ contentValues.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven1");
+ contentValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle1");
+
+ // With "IS_PRIMARY=1", but we should ignore this time.
+ contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName2");
+ contentValues.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName2");
+ contentValues.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName2");
+ contentValues.put(StructuredName.PREFIX, "DoNotEmitPrefix2");
+ contentValues.put(StructuredName.SUFFIX, "DoNotEmitSuffix2");
+ contentValues.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily2");
+ contentValues.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven2");
+ contentValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle2");
+ contentValues.put(StructuredName.IS_PRIMARY, 1);
+
+ // With "IS_SUPER_PRIMARY=1". This is what we should use.
+ contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "AppropriateFamilyName");
+ contentValues.put(StructuredName.GIVEN_NAME, "AppropriateGivenName");
+ contentValues.put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName");
+ contentValues.put(StructuredName.PREFIX, "AppropriatePrefix");
+ contentValues.put(StructuredName.SUFFIX, "AppropriateSuffix");
+ contentValues.put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily");
+ contentValues.put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven");
+ contentValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle");
+ contentValues.put(StructuredName.IS_SUPER_PRIMARY, 1);
+
+ contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName3");
+ contentValues.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName3");
+ contentValues.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName3");
+ contentValues.put(StructuredName.PREFIX, "DoNotEmitPrefix3");
+ contentValues.put(StructuredName.SUFFIX, "DoNotEmitSuffix3");
+ contentValues.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily3");
+ contentValues.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven3");
+ contentValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle3");
+ contentValues.put(StructuredName.IS_PRIMARY, 1);
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ PropertyNodesVerifier verifier = handler.addNewVerifier()
+ .addNodeWithOrder("N",
+ "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
+ + "AppropriatePrefix;AppropriateSuffix",
+ Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
+ "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
+ .addNodeWithOrder("FN",
+ "AppropriatePrefix AppropriateGivenName "
+ + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix")
+ .addNodeWithoutOrder("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
+ .addNodeWithoutOrder("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
+ .addNodeWithoutOrder("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
+
+ if (isV30) {
+ verifier.addNodeWithoutOrder("SORT-STRING",
+ "AppropriatePhoneticGiven AppropriatePhoneticMiddle"
+ + " AppropriatePhoneticFamily");
+ }
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testStructuredNameUseSuperPrimaryV21() {
+ testStructuredNameUseSuperPrimaryCommon(V21);
+ }
+
+ public void testStructuredNameUseSuperPrimaryV30() {
+ testStructuredNameUseSuperPrimaryCommon(V30);
+ }
+
+ /**
+ * There's no property for nickname in vCard 2.1, so we don't have any requirement on it.
+ */
+ public void testNickNameV30() {
+ ExportTestResolver resolver = new ExportTestResolver();
+ ContentValues contentValues = resolver.buildData(Nickname.CONTENT_ITEM_TYPE);
+ contentValues.put(Nickname.NAME, "Nicky");
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, V30);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithOrder("NICKNAME", "Nicky");
+
+ verifyOneComposition(resolver, handler, V30);
+ }
+
+ private void testPhoneBasicCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "1");
+ contentValues.put(Phone.TYPE, Phone.TYPE_HOME);
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("TEL", "1", new TypeSet("HOME", "VOICE"));
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testPhoneBasicV21() {
+ testPhoneBasicCommon(V21);
+ }
+
+ public void testPhoneBasicV30() {
+ testPhoneBasicCommon(V30);
+ }
+
+ /**
+ * Tests that vCard composer emits corresponding type param which we expect.
+ */
+ private void testPhoneVariousTypeSupport(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "10");
+ contentValues.put(Phone.TYPE, Phone.TYPE_HOME);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "20");
+ contentValues.put(Phone.TYPE, Phone.TYPE_WORK);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "30");
+ contentValues.put(Phone.TYPE, Phone.TYPE_FAX_HOME);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "40");
+ contentValues.put(Phone.TYPE, Phone.TYPE_FAX_WORK);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "50");
+ contentValues.put(Phone.TYPE, Phone.TYPE_MOBILE);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "60");
+ contentValues.put(Phone.TYPE, Phone.TYPE_PAGER);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "70");
+ contentValues.put(Phone.TYPE, Phone.TYPE_OTHER);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "80");
+ contentValues.put(Phone.TYPE, Phone.TYPE_CAR);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "90");
+ contentValues.put(Phone.TYPE, Phone.TYPE_COMPANY_MAIN);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "100");
+ contentValues.put(Phone.TYPE, Phone.TYPE_ISDN);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "110");
+ contentValues.put(Phone.TYPE, Phone.TYPE_MAIN);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "120");
+ contentValues.put(Phone.TYPE, Phone.TYPE_OTHER_FAX);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "130");
+ contentValues.put(Phone.TYPE, Phone.TYPE_TELEX);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "140");
+ contentValues.put(Phone.TYPE, Phone.TYPE_WORK_MOBILE);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "150");
+ contentValues.put(Phone.TYPE, Phone.TYPE_WORK_PAGER);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "160");
+ contentValues.put(Phone.TYPE, Phone.TYPE_MMS);
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("TEL", "10", new TypeSet("HOME"))
+ .addNodeWithoutOrder("TEL", "20", new TypeSet("WORK"))
+ .addNodeWithoutOrder("TEL", "30", new TypeSet("HOME", "FAX"))
+ .addNodeWithoutOrder("TEL", "40", new TypeSet("WORK", "FAX"))
+ .addNodeWithoutOrder("TEL", "50", new TypeSet("CELL"))
+ .addNodeWithoutOrder("TEL", "60", new TypeSet("PAGER"))
+ .addNodeWithoutOrder("TEL", "70", new TypeSet("VOICE"))
+ .addNodeWithoutOrder("TEL", "80", new TypeSet("CAR"))
+ .addNodeWithoutOrder("TEL", "90", new TypeSet("WORK", "PREF"))
+ .addNodeWithoutOrder("TEL", "100", new TypeSet("ISDN"))
+ .addNodeWithoutOrder("TEL", "110", new TypeSet("PREF"))
+ .addNodeWithoutOrder("TEL", "120", new TypeSet("FAX"))
+ .addNodeWithoutOrder("TEL", "130", new TypeSet("TLX"))
+ .addNodeWithoutOrder("TEL", "140", new TypeSet("WORK", "MOBILE"))
+ .addNodeWithoutOrder("TEL", "150", new TypeSet("WORK", "PAGER"))
+ .addNodeWithoutOrder("TEL", "160", new TypeSet("MSG"));
+ }
+
+ public void testPhoneVariousTypeSupportV21() {
+ testPhoneVariousTypeSupport(V21);
+ }
+
+ public void testPhoneVariousTypeSupportV30() {
+ testPhoneVariousTypeSupport(V30);
+ }
+
+ /**
+ * Tests that "PREF"s are emitted appropriately.
+ */
+ private void testPhonePrefHandlingCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "1");
+ contentValues.put(Phone.TYPE, Phone.TYPE_HOME);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "2");
+ contentValues.put(Phone.TYPE, Phone.TYPE_WORK);
+ contentValues.put(Phone.IS_PRIMARY, 1);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "3");
+ contentValues.put(Phone.TYPE, Phone.TYPE_FAX_HOME);
+ contentValues.put(Phone.IS_PRIMARY, 1);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "4");
+ contentValues.put(Phone.TYPE, Phone.TYPE_FAX_WORK);
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("TEL", "4", new TypeSet("WORK", "FAX"))
+ .addNodeWithoutOrder("TEL", "3", new TypeSet("HOME", "FAX", "PREF"))
+ .addNodeWithoutOrder("TEL", "2", new TypeSet("WORK", "VOICE", "PREF"))
+ .addNodeWithoutOrder("TEL", "1", new TypeSet("HOME", "VOICE"));
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testPhonePrefHandlingV21() {
+ testPhonePrefHandlingCommon(V21);
+ }
+
+ public void testPhonePrefHandlingV30() {
+ testPhonePrefHandlingCommon(V30);
+ }
+
+ private void testEmailBasicCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+ ContentValues contentValues = resolver.buildData(Email.CONTENT_ITEM_TYPE);
+ contentValues.put(Email.DATA, "sample@example.com");
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("EMAIL", "sample@example.com");
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testEmailBasicV21() {
+ testEmailBasicCommon(V21);
+ }
+
+ public void testEmailBasicV30() {
+ testEmailBasicCommon(V30);
+ }
+
+ private void testEmailVariousTypeSupportCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Email.CONTENT_ITEM_TYPE);
+ contentValues.put(Email.DATA, "type_home@example.com");
+ contentValues.put(Email.TYPE, Email.TYPE_HOME);
+
+ contentValues = resolver.buildData(Email.CONTENT_ITEM_TYPE);
+ contentValues.put(Email.DATA, "type_work@example.com");
+ contentValues.put(Email.TYPE, Email.TYPE_WORK);
+
+ contentValues = resolver.buildData(Email.CONTENT_ITEM_TYPE);
+ contentValues.put(Email.DATA, "type_mobile@example.com");
+ contentValues.put(Email.TYPE, Email.TYPE_MOBILE);
+
+ contentValues = resolver.buildData(Email.CONTENT_ITEM_TYPE);
+ contentValues.put(Email.DATA, "type_other@example.com");
+ contentValues.put(Email.TYPE, Email.TYPE_OTHER);
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("EMAIL", "type_home@example.com", new TypeSet("HOME"))
+ .addNodeWithoutOrder("EMAIL", "type_work@example.com", new TypeSet("WORK"))
+ .addNodeWithoutOrder("EMAIL", "type_mobile@example.com", new TypeSet("CELL"))
+ .addNodeWithoutOrder("EMAIL", "type_other@example.com");
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testEmailVariousTypeSupportV21() {
+ testEmailVariousTypeSupportCommon(V21);
+ }
+
+ public void testEmailVariousTypeSupportV30() {
+ testEmailVariousTypeSupportCommon(V30);
+ }
+
+ private void testEmailPrefHandlingCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Email.CONTENT_ITEM_TYPE);
+ contentValues.put(Email.DATA, "type_home@example.com");
+ contentValues.put(Email.TYPE, Email.TYPE_HOME);
+ contentValues.put(Email.IS_PRIMARY, 1);
+
+ contentValues = resolver.buildData(Email.CONTENT_ITEM_TYPE);
+ contentValues.put(Email.DATA, "type_notype@example.com");
+ contentValues.put(Email.IS_PRIMARY, 1);
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("EMAIL", "type_notype@example.com", new TypeSet("PREF"))
+ .addNodeWithoutOrder("EMAIL", "type_home@example.com", new TypeSet("HOME", "PREF"));
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testEmailPrefHandlingV21() {
+ testEmailPrefHandlingCommon(V21);
+ }
+
+ public void testEmailPrefHandlingV30() {
+ testEmailPrefHandlingCommon(V30);
+ }
+
+ private void testPostalOnlyWithStructuredDataCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ // adr-value = 0*6(text-value ";") text-value
+ // ; PO Box, Extended Address, Street, Locality, Region, Postal Code,
+ // ; Country Name
+ ContentValues contentValues = resolver.buildData(StructuredPostal.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredPostal.POBOX, "Pobox");
+ contentValues.put(StructuredPostal.NEIGHBORHOOD, "Neighborhood");
+ contentValues.put(StructuredPostal.STREET, "Street");
+ contentValues.put(StructuredPostal.CITY, "City");
+ contentValues.put(StructuredPostal.REGION, "Region");
+ contentValues.put(StructuredPostal.POSTCODE, "100");
+ contentValues.put(StructuredPostal.COUNTRY, "Country");
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("ADR", "Pobox;Neighborhood;Street;City;Region;100;Country",
+ Arrays.asList("Pobox", "Neighborhood", "Street", "City",
+ "Region", "100", "Country"), new TypeSet("HOME"));
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testPostalOnlyWithStructuredDataV21() {
+ testPostalOnlyWithStructuredDataCommon(V21);
+ }
+
+ public void testPostalOnlyWithStructuredDataV30() {
+ testPostalOnlyWithStructuredDataCommon(V30);
+ }
+
+ private void testPostalOnlyWithFormattedAddressCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(StructuredPostal.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredPostal.FORMATTED_ADDRESS,
+ "Formatted address CA 123-334 United Statue");
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithOrder("ADR", ";Formatted address CA 123-334 United Statue;;;;;",
+ Arrays.asList("", "Formatted address CA 123-334 United Statue",
+ "", "", "", "", ""), new TypeSet("HOME"));
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testPostalOnlyWithFormattedAddressV21() {
+ testPostalOnlyWithFormattedAddressCommon(V21);
+ }
+
+ public void testPostalOnlyWithFormattedAddressV30() {
+ testPostalOnlyWithFormattedAddressCommon(V30);
+ }
+
+ /**
+ * Tests that the vCard composer honors formatted data when it is available
+ * even when it is partial.
+ */
+ private void testPostalWithBothStructuredAndFormattedCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(StructuredPostal.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredPostal.POBOX, "Pobox");
+ contentValues.put(StructuredPostal.COUNTRY, "Country");
+ contentValues.put(StructuredPostal.FORMATTED_ADDRESS,
+ "Formatted address CA 123-334 United Statue"); // Should be ignored
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("ADR", "Pobox;;;;;;Country",
+ Arrays.asList("Pobox", "", "", "", "", "", "Country"), new TypeSet("HOME"));
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testPostalWithBothStructuredAndFormattedV21() {
+ testPostalWithBothStructuredAndFormattedCommon(V21);
+ }
+
+ public void testPostalWithBothStructuredAndFormattedV30() {
+ testPostalWithBothStructuredAndFormattedCommon(V30);
+ }
+
+ private void testOrganizationCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+ ContentValues contentValues = resolver.buildData(Organization.CONTENT_ITEM_TYPE);
+ contentValues.put(Organization.COMPANY, "CompanyX");
+ contentValues.put(Organization.DEPARTMENT, "DepartmentY");
+ contentValues.put(Organization.TITLE, "TitleZ");
+ contentValues.put(Organization.JOB_DESCRIPTION, "Description Rambda"); // Ignored.
+ contentValues.put(Organization.OFFICE_LOCATION, "Mountain View"); // Ignored.
+ contentValues.put(Organization.PHONETIC_NAME, "PhoneticName!"); // Ignored
+ contentValues.put(Organization.SYMBOL, "(^o^)/~~"); // Ignore him (her).
+
+ contentValues = resolver.buildData(Organization.CONTENT_ITEM_TYPE);
+ contentValues.putNull(Organization.COMPANY);
+ contentValues.put(Organization.DEPARTMENT, "DepartmentXX");
+ contentValues.putNull(Organization.TITLE);
+
+ contentValues = resolver.buildData(Organization.CONTENT_ITEM_TYPE);
+ contentValues.put(Organization.COMPANY, "CompanyXYZ");
+ contentValues.putNull(Organization.DEPARTMENT);
+ contentValues.put(Organization.TITLE, "TitleXYZYX");
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+
+ // Currently we do not use group but depend on the order.
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithOrder("ORG", "CompanyX;DepartmentY",
+ Arrays.asList("CompanyX", "DepartmentY"))
+ .addNodeWithOrder("TITLE", "TitleZ")
+ .addNodeWithOrder("ORG", "DepartmentXX")
+ .addNodeWithOrder("ORG", "CompanyXYZ")
+ .addNodeWithOrder("TITLE", "TitleXYZYX");
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testOrganizationV21() {
+ testOrganizationCommon(V21);
+ }
+
+ public void testOrganizationV30() {
+ testOrganizationCommon(V30);
+ }
+
+ private void testImVariousTypeSupportCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_AIM);
+ contentValues.put(Im.DATA, "aim");
+
+ contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_MSN);
+ contentValues.put(Im.DATA, "msn");
+
+ contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_YAHOO);
+ contentValues.put(Im.DATA, "yahoo");
+
+ contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_SKYPE);
+ contentValues.put(Im.DATA, "skype");
+
+ contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_QQ);
+ contentValues.put(Im.DATA, "qq");
+
+ contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK);
+ contentValues.put(Im.DATA, "google talk");
+
+ contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_ICQ);
+ contentValues.put(Im.DATA, "icq");
+
+ contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_JABBER);
+ contentValues.put(Im.DATA, "jabber");
+
+ contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_NETMEETING);
+ contentValues.put(Im.DATA, "netmeeting");
+
+ // No determined way to express unknown type...
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("X-JABBER", "jabber")
+ .addNodeWithoutOrder("X-ICQ", "icq")
+ .addNodeWithoutOrder("X-GOOGLE-TALK", "google talk")
+ .addNodeWithoutOrder("X-QQ", "qq")
+ .addNodeWithoutOrder("X-SKYPE-USERNAME", "skype")
+ .addNodeWithoutOrder("X-YAHOO", "yahoo")
+ .addNodeWithoutOrder("X-MSN", "msn")
+ .addNodeWithoutOrder("X-NETMEETING", "netmeeting")
+ .addNodeWithoutOrder("X-AIM", "aim");
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testImBasiV21() {
+ testImVariousTypeSupportCommon(V21);
+ }
+
+ public void testImBasicV30() {
+ testImVariousTypeSupportCommon(V30);
+ }
+
+ private void testImPrefHandlingCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_AIM);
+ contentValues.put(Im.DATA, "aim1");
+
+ contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_AIM);
+ contentValues.put(Im.DATA, "aim2");
+ contentValues.put(Im.TYPE, Im.TYPE_HOME);
+ contentValues.put(Im.IS_PRIMARY, 1);
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("X-AIM", "aim1")
+ .addNodeWithoutOrder("X-AIM", "aim2", new TypeSet("HOME", "PREF"));
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testImPrefHandlingV21() {
+ testImPrefHandlingCommon(V21);
+ }
+
+ public void testImPrefHandlingV30() {
+ testImPrefHandlingCommon(V30);
+ }
+
+ private void testWebsiteCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Website.CONTENT_ITEM_TYPE);
+ contentValues.put(Website.URL, "http://website.example.android.com/index.html");
+ contentValues.put(Website.TYPE, Website.TYPE_BLOG);
+
+ contentValues = resolver.buildData(Website.CONTENT_ITEM_TYPE);
+ contentValues.put(Website.URL, "ftp://ftp.example.android.com/index.html");
+ contentValues.put(Website.TYPE, Website.TYPE_FTP);
+
+ // We drop TYPE information since vCard (especially 3.0) does not allow us to emit it.
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("URL", "ftp://ftp.example.android.com/index.html")
+ .addNodeWithoutOrder("URL", "http://website.example.android.com/index.html");
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testWebsiteV21() {
+ testWebsiteCommon(V21);
+ }
+
+ public void testWebsiteV30() {
+ testWebsiteCommon(V30);
+ }
+
+ private void testEventCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Event.CONTENT_ITEM_TYPE);
+ contentValues.put(Event.TYPE, Event.TYPE_ANNIVERSARY);
+ contentValues.put(Event.START_DATE, "1982-06-16");
+
+ contentValues = resolver.buildData(Event.CONTENT_ITEM_TYPE);
+ contentValues.put(Event.TYPE, Event.TYPE_BIRTHDAY);
+ contentValues.put(Event.START_DATE, "2008-10-22");
+
+ contentValues = resolver.buildData(Event.CONTENT_ITEM_TYPE);
+ contentValues.put(Event.TYPE, Event.TYPE_OTHER);
+ contentValues.put(Event.START_DATE, "2018-03-12");
+
+ contentValues = resolver.buildData(Event.CONTENT_ITEM_TYPE);
+ contentValues.put(Event.TYPE, Event.TYPE_CUSTOM);
+ contentValues.put(Event.LABEL, "The last day");
+ contentValues.put(Event.START_DATE, "When the Tower of Hanoi with 64 rings is completed.");
+
+ contentValues = resolver.buildData(Event.CONTENT_ITEM_TYPE);
+ contentValues.put(Event.TYPE, Event.TYPE_BIRTHDAY);
+ contentValues.put(Event.START_DATE, "2009-05-19");
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("BDAY", "2008-10-22");
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testEventV21() {
+ testEventCommon(V21);
+ }
+
+ public void testEventV30() {
+ testEventCommon(V30);
+ }
+
+ private void testNoteCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Note.CONTENT_ITEM_TYPE);
+ contentValues.put(Note.NOTE, "note1");
+
+ contentValues = resolver.buildData(Note.CONTENT_ITEM_TYPE);
+ contentValues.put(Note.NOTE, "note2");
+ contentValues.put(Note.IS_PRIMARY, 1); // Just ignored.
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithOrder("NOTE", "note1")
+ .addNodeWithOrder("NOTE", "note2");
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testNoteV21() {
+ testNoteCommon(V21);
+ }
+
+ public void testNoteV30() {
+ testNoteCommon(V30);
+ }
+
+ // TODO: test for non-ascii...
+
+ private void testPhotoCommon(int version) {
+ final boolean isV30 = version == V30;
+ ExportTestResolver resolver = new ExportTestResolver();
+ ContentValues contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "PhotoTest");
+
+ contentValues = resolver.buildData(Photo.CONTENT_ITEM_TYPE);
+ contentValues.put(Photo.PHOTO, sPhotoByteArray);
+
+ ContentValues contentValuesForPhoto = new ContentValues();
+ contentValuesForPhoto.put("ENCODING", (isV30 ? "b" : "BASE64"));
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifier()
+ .addNodeWithoutOrder("FN", "PhotoTest")
+ .addNodeWithoutOrder("N", "PhotoTest;;;;", Arrays.asList("PhotoTest", "", "", "", ""))
+ .addNodeWithOrder("PHOTO", null, null, sPhotoByteArray,
+ contentValuesForPhoto, new TypeSet("JPEG"), null);
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testPhotoV21() {
+ testPhotoCommon(V21);
+ }
+
+ public void testPhotoV30() {
+ testPhotoCommon(V30);
+ }
+}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardImporterTests.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardImporterTests.java
new file mode 100644
index 0000000..51bce72
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardImporterTests.java
@@ -0,0 +1,1300 @@
+/*
+ * 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 com.android.unit_tests.R;
+import com.android.unit_tests.vcard.PropertyNodesVerifier.TypeSet;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentValues;
+import android.net.Uri;
+import android.pim.vcard.EntryCommitter;
+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.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.Relation;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.test.AndroidTestCase;
+import android.test.mock.MockContentProvider;
+import android.test.mock.MockContentResolver;
+import android.text.TextUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+
+public class VCardImporterTests extends AndroidTestCase {
+ // Push data into int array at first since values like 0x80 are
+ // interpreted as int by the compiler and casting all of them is
+ // cumbersome...
+ private static final int[] sPhotoIntArrayForComplicatedCase = {
+ 0xff, 0xd8, 0xff, 0xe1, 0x0a, 0x0f, 0x45, 0x78, 0x69, 0x66, 0x00,
+ 0x00, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d,
+ 0x01, 0x0e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0xaa, 0x01, 0x0f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+ 0x00, 0xba, 0x01, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00,
+ 0x00, 0x00, 0xc2, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0xd0, 0x01, 0x28, 0x00, 0x03, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x31, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xd8, 0x01, 0x32, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xe6, 0x02, 0x13,
+ 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x82,
+ 0x98, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xfa,
+ 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
+ 0x84, 0xc4, 0xa5, 0x00, 0x07, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x00, 0x04, 0x1e, 0x32, 0x30, 0x30, 0x38, 0x31,
+ 0x30, 0x32, 0x39, 0x31, 0x33, 0x35, 0x35, 0x33, 0x31, 0x00, 0x00,
+ 0x44, 0x6f, 0x43, 0x6f, 0x4d, 0x6f, 0x00, 0x00, 0x44, 0x39, 0x30,
+ 0x35, 0x69, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x44, 0x39, 0x30,
+ 0x35, 0x69, 0x20, 0x56, 0x65, 0x72, 0x31, 0x2e, 0x30, 0x30, 0x00,
+ 0x32, 0x30, 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20,
+ 0x31, 0x33, 0x3a, 0x35, 0x35, 0x3a, 0x34, 0x37, 0x00, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x00, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x49, 0x4d, 0x00, 0x30, 0x33,
+ 0x30, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x14, 0x00,
+ 0x14, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+ 0x00, 0x34, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x01, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x09, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x0f, 0x0b, 0x00,
+ 0x00, 0x27, 0x10, 0x00, 0x00, 0x05, 0x97, 0x00, 0x00, 0x27, 0x10,
+ 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x1c,
+ 0x01, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x02, 0x5e, 0x00, 0x00,
+ 0x27, 0x10, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x27, 0x10, 0x00,
+ 0x00, 0x03, 0xcb, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x1b, 0xe5,
+ 0x00, 0x00, 0x27, 0x10, 0x00, 0x28, 0x82, 0x9a, 0x00, 0x05, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x6a, 0x82, 0x9d, 0x00, 0x05,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x72, 0x88, 0x22, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x90, 0x00,
+ 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30, 0x32, 0x32, 0x30, 0x90,
+ 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x03, 0x7a,
+ 0x90, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x03,
+ 0x8e, 0x91, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x01, 0x02,
+ 0x03, 0x00, 0x91, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x03, 0xa2, 0x92, 0x01, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x03, 0xaa, 0x92, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x03, 0xb2, 0x92, 0x04, 0x00, 0x0a, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x03, 0xba, 0x92, 0x05, 0x00, 0x05, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xc2, 0x92, 0x07, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x92, 0x08, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x92, 0x09,
+ 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x92,
+ 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xca,
+ 0x92, 0x7c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x92, 0x86, 0x00, 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00,
+ 0x03, 0xd2, 0xa0, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30,
+ 0x31, 0x30, 0x30, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x60, 0x00, 0x00, 0xa0, 0x03, 0x00, 0x03, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x48, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0xa2, 0x0e, 0x00, 0x05,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xe8, 0xa2, 0x0f, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xf0, 0xa2, 0x10,
+ 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0xa2,
+ 0x17, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00,
+ 0xa3, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00,
+ 0x00, 0xa3, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0xa4, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0xa4, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0xa4, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x04, 0x00, 0x05, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x03, 0xf8, 0xa4, 0x05, 0x00, 0x03, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x00, 0xa4, 0x06, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x07, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x08,
+ 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4,
+ 0x09, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0xa4, 0x0a, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0xa4, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00,
+ 0x00, 0x27, 0x10, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x00, 0x64,
+ 0x32, 0x30, 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20,
+ 0x31, 0x33, 0x3a, 0x35, 0x35, 0x3a, 0x33, 0x31, 0x00, 0x32, 0x30,
+ 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20, 0x31, 0x33,
+ 0x3a, 0x35, 0x35, 0x3a, 0x34, 0x37, 0x00, 0x00, 0x00, 0x29, 0x88,
+ 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0xb2, 0x00, 0x00, 0x00,
+ 0x64, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x25, 0x00,
+ 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0e, 0x92, 0x00, 0x00, 0x03, 0xe8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x30, 0x30,
+ 0x38, 0x31, 0x30, 0x32, 0x39, 0x31, 0x33, 0x35, 0x35, 0x33, 0x31,
+ 0x00, 0x00, 0x20, 0x2a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x2a,
+ 0xe2, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x04, 0x52, 0x39, 0x38, 0x00, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00,
+ 0x00, 0x04, 0x30, 0x31, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06,
+ 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x04, 0x6c, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x04, 0x74, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x04, 0x7c, 0x02, 0x02, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x8b, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x01, 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84,
+ 0x00, 0x20, 0x16, 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, 0x1a, 0x1c,
+ 0x24, 0x22, 0x20, 0x26, 0x30, 0x50, 0x34, 0x30, 0x2c, 0x2c, 0x30,
+ 0x62, 0x46, 0x4a, 0x3a, 0x50, 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66,
+ 0x70, 0x6e, 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, 0xae, 0x8a, 0x6e,
+ 0x70, 0xa0, 0xda, 0xa2, 0xae, 0xbe, 0xc4, 0xce, 0xd0, 0xce, 0x7c,
+ 0x9a, 0xe2, 0xf2, 0xe0, 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, 0x01,
+ 0x22, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34, 0x34, 0x5e, 0xc6,
+ 0x84, 0x70, 0x84, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xff, 0xc0,
+ 0x00, 0x11, 0x08, 0x00, 0x78, 0x00, 0xa0, 0x03, 0x01, 0x21, 0x00,
+ 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x01, 0xa2, 0x00,
+ 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03,
+ 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01,
+ 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31,
+ 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81,
+ 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
+ 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19,
+ 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
+ 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92,
+ 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4,
+ 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+ 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
+ 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
+ 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1,
+ 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, 0x00,
+ 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04,
+ 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77,
+ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12,
+ 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14,
+ 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15,
+ 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17,
+ 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
+ 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
+ 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3,
+ 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5,
+ 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
+ 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2,
+ 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00,
+ 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00,
+ 0x14, 0x54, 0xaa, 0x2a, 0x46, 0x48, 0xa2, 0xa4, 0x55, 0xa6, 0x04,
+ 0x8a, 0x29, 0xe0, 0x53, 0x10, 0xe0, 0x29, 0xc0, 0x50, 0x03, 0xb1,
+ 0x46, 0x29, 0x80, 0x84, 0x52, 0x11, 0x40, 0x0d, 0x22, 0x9a, 0x45,
+ 0x20, 0x23, 0x61, 0x51, 0x30, 0xa0, 0x08, 0xc8, 0xa8, 0xd8, 0x52,
+ 0x02, 0x26, 0x15, 0x0b, 0x0a, 0x00, 0xb4, 0xa2, 0xa5, 0x5a, 0x00,
+ 0x91, 0x45, 0x4a, 0xa2, 0x81, 0x92, 0x01, 0x4e, 0x02, 0x98, 0x87,
+ 0x0a, 0x70, 0xa0, 0x07, 0x62, 0x8c, 0x50, 0x21, 0x0d, 0x25, 0x00,
+ 0x34, 0x8a, 0x61, 0x14, 0x0c, 0x63, 0x0a, 0x89, 0x85, 0x00, 0x46,
+ 0xd5, 0x1b, 0x52, 0x02, 0x16, 0xa8, 0x98, 0x50, 0x05, 0x94, 0xa9,
+ 0x16, 0x80, 0x25, 0x5a, 0x95, 0x68, 0x18, 0xf1, 0x4f, 0x14, 0xc4,
+ 0x3b, 0xb5, 0x22, 0xb6, 0x38, 0x34, 0x00, 0xe3, 0x22, 0x8e, 0xf4,
+ 0x79, 0x8a, 0x7b, 0xd1, 0x71, 0x03, 0x30, 0xc7, 0x14, 0x83, 0xa5,
+ 0x00, 0x06, 0x98, 0x68, 0x01, 0x8d, 0x51, 0x35, 0x03, 0x22, 0x6a,
+ 0x8d, 0xa9, 0x01, 0x13, 0x54, 0x4d, 0x40, 0x13, 0xa5, 0x4a, 0x28,
+ 0x02, 0x45, 0x35, 0x2a, 0x9a, 0x00, 0x78, 0x34, 0xf0, 0x69, 0x80,
+ 0x34, 0x81, 0x45, 0x40, 0xce, 0x58, 0xe6, 0xa2, 0x4c, 0x06, 0xe4,
+ 0xfa, 0xd1, 0x93, 0x50, 0x21, 0xca, 0xe4, 0x55, 0x84, 0x90, 0x30,
+ 0xab, 0x8b, 0x18, 0xa6, 0x9a, 0x6a, 0xc4, 0x31, 0xaa, 0x26, 0xa0,
+ 0x64, 0x4d, 0x51, 0xb5, 0x20, 0x23, 0x6a, 0x89, 0xa8, 0x02, 0x44,
+ 0x35, 0x2a, 0x9a, 0x00, 0x95, 0x4d, 0x48, 0xa6, 0x80, 0x24, 0x53,
+ 0x4e, 0xce, 0x05, 0x30, 0x2b, 0x3b, 0xee, 0x6a, 0x91, 0x5d, 0x76,
+ 0x63, 0xbd, 0x65, 0x7d, 0x40, 0x66, 0x68, 0xa9, 0x02, 0x45, 0x2b,
+ 0xb3, 0x9e, 0xb4, 0xc5, 0x6d, 0xad, 0x9a, 0xa0, 0x2c, 0x06, 0xc8,
+ 0xcd, 0x04, 0xd6, 0xa2, 0x23, 0x63, 0x51, 0xb1, 0xa0, 0x64, 0x4d,
+ 0x51, 0x93, 0x48, 0x08, 0xda, 0xa2, 0x6a, 0x00, 0x72, 0x1a, 0x99,
+ 0x4d, 0x00, 0x48, 0xa6, 0xa4, 0x53, 0x4c, 0x07, 0x86, 0x03, 0xbd,
+ 0x2b, 0x9c, 0xa7, 0x14, 0x98, 0x10, 0x85, 0x34, 0xe0, 0xa6, 0xb3,
+ 0xb0, 0x0b, 0xb5, 0xa8, 0x0a, 0xd4, 0x58, 0x42, 0xed, 0x3e, 0x94,
+ 0xd2, 0xa6, 0x8b, 0x01, 0x34, 0x44, 0xed, 0xe6, 0x9c, 0x4d, 0x6a,
+ 0x80, 0x8d, 0x8d, 0x46, 0xc6, 0x80, 0x23, 0x63, 0x51, 0x9a, 0x06,
+ 0x46, 0xd5, 0x13, 0x52, 0x01, 0x54, 0xd4, 0xaa, 0x68, 0x02, 0x40,
+ 0x6a, 0x40, 0x78, 0xa0, 0x08, 0x59, 0xce, 0xee, 0xb5, 0x2a, 0x39,
+ 0xd9, 0x59, 0xa7, 0xa8, 0x00, 0x73, 0xeb, 0x4e, 0x0e, 0x7d, 0x69,
+ 0x5c, 0x05, 0xf3, 0x0f, 0xad, 0x1e, 0x61, 0xf5, 0xa7, 0x71, 0x0b,
+ 0xe6, 0x35, 0x21, 0x90, 0xd3, 0xb8, 0x0e, 0x32, 0x10, 0x95, 0x10,
+ 0x91, 0xb3, 0xd6, 0x9b, 0x60, 0x4b, 0x9c, 0x8a, 0x63, 0x1a, 0xb0,
+ 0x18, 0x4d, 0x46, 0xc6, 0x80, 0x22, 0x6a, 0x61, 0xa4, 0x31, 0xaa,
+ 0x6a, 0x55, 0x34, 0x01, 0x2a, 0x9a, 0x7e, 0x78, 0xa0, 0x08, 0x09,
+ 0xf9, 0xaa, 0x58, 0xcf, 0xca, 0x6b, 0x3e, 0xa0, 0x00, 0xd3, 0x81,
+ 0xa9, 0x01, 0x73, 0x46, 0x69, 0x80, 0xb9, 0xa4, 0xcd, 0x00, 0x2b,
+ 0x1f, 0x92, 0xa3, 0x07, 0x9a, 0x6f, 0x70, 0x26, 0xcf, 0x14, 0xd2,
+ 0x6b, 0x51, 0x0c, 0x63, 0x51, 0xb1, 0xa0, 0x08, 0xda, 0x98, 0x69,
+ 0x0c, 0x8d, 0x4d, 0x4a, 0xa6, 0x80, 0x24, 0x53, 0x52, 0x03, 0xc5,
+ 0x02, 0x21, 0x27, 0xe6, 0xa9, 0x23, 0x3f, 0x29, 0xac, 0xfa, 0x8c,
+ 0x01, 0xe6, 0x9c, 0x0d, 0x48, 0x0a, 0x0d, 0x2e, 0x68, 0x01, 0x73,
+ 0x49, 0x9a, 0x60, 0x2b, 0x1f, 0x92, 0x98, 0x3a, 0xd3, 0x7b, 0x81,
+ 0x36, 0x78, 0xa6, 0x93, 0x5a, 0x88, 0x8c, 0x9a, 0x63, 0x1a, 0x00,
+ 0x8c, 0xd3, 0x0d, 0x21, 0x91, 0x29, 0xa9, 0x14, 0xd0, 0x04, 0x8a,
+ 0x69, 0xe0, 0xd3, 0x11, 0x1b, 0x1e, 0x6a, 0x48, 0xcf, 0xca, 0x6b,
+ 0x3e, 0xa3, 0x10, 0x1a, 0x70, 0x35, 0x20, 0x38, 0x1a, 0x5c, 0xd2,
+ 0x01, 0x73, 0x49, 0x9a, 0x60, 0x39, 0x8f, 0xca, 0x29, 0x8b, 0xf7,
+ 0xaa, 0xba, 0x88, 0x96, 0x9a, 0x6b, 0x40, 0x18, 0xc6, 0xa3, 0x26,
+ 0x80, 0x18, 0x69, 0xa6, 0x90, 0xc8, 0x14, 0xd4, 0x8a, 0x69, 0x80,
+ 0xf0, 0x6a, 0x40, 0x68, 0x10, 0xbb, 0x41, 0xa7, 0xe3, 0x0b, 0xc5,
+ 0x2b, 0x01, 0x10, 0xa7, 0x03, 0x59, 0x0c, 0x76, 0x69, 0x73, 0x40,
+ 0x0b, 0x9a, 0x28, 0x11, 0x28, 0x19, 0x5e, 0x69, 0x02, 0x81, 0x5a,
+ 0xd8, 0x00, 0xd3, 0x4d, 0x50, 0x0c, 0x6a, 0x8c, 0xd2, 0x01, 0xa6,
+ 0x98, 0x69, 0x0c, 0xae, 0xa6, 0xa4, 0x06, 0x80, 0x1e, 0xa6, 0x9e,
+ 0x0d, 0x31, 0x12, 0x03, 0x4f, 0x06, 0x80, 0x13, 0x60, 0x34, 0xd3,
+ 0xc1, 0xa8, 0x92, 0x01, 0xf1, 0x8d, 0xdd, 0x69, 0xcc, 0xa1, 0x69,
+ 0x5b, 0x4b, 0x80, 0x83, 0x93, 0x52, 0x04, 0x14, 0xe2, 0xae, 0x03,
+ 0xa9, 0x0d, 0x68, 0x03, 0x4d, 0x34, 0xd0, 0x03, 0x0d, 0x30, 0xd2,
+ 0x01, 0x86, 0x9a, 0x68, 0x19, 0x58, 0x1a, 0x78, 0xa4, 0x04, 0x8a,
+ 0x69, 0xe0, 0xd3, 0x10, 0xe0, 0x69, 0xe0, 0xd0, 0x03, 0xc1, 0xa8,
+ 0xdb, 0xad, 0x4c, 0x81, 0x12, 0x45, 0xd6, 0x9d, 0x25, 0x1d, 0x00,
+ 0x6a, 0xf5, 0xa9, 0xe8, 0x80, 0x31, 0x29, 0x0d, 0x58, 0x08, 0x69,
+ 0x86, 0x80, 0x1a, 0x69, 0x86, 0x90, 0x0c, 0x34, 0xd3, 0x48, 0x65,
+ 0x51, 0x4f, 0x06, 0x98, 0x0f, 0x14, 0xf0, 0x68, 0x10, 0xf0, 0x69,
+ 0xe0, 0xd0, 0x03, 0x81, 0xa5, 0x2b, 0x9a, 0x1a, 0xb8, 0x87, 0xa8,
+ 0xdb, 0x4a, 0x46, 0x68, 0xb6, 0x80, 0x2a, 0xa8, 0x14, 0xea, 0x12,
+ 0xb0, 0x05, 0x21, 0xa6, 0x02, 0x1a, 0x61, 0xa0, 0x06, 0x9a, 0x61,
+ 0xa4, 0x31, 0x86, 0x9a, 0x69, 0x0c, 0xa8, 0x0d, 0x3c, 0x53, 0x01,
+ 0xe2, 0x9e, 0x28, 0x10, 0xf1, 0x4e, 0x06, 0x98, 0x0f, 0x06, 0x9e,
+ 0x0d, 0x02, 0x1c, 0x29, 0xc2, 0x80, 0x16, 0x96, 0x80, 0x0a, 0x4a,
+ 0x00, 0x43, 0x4d, 0x34, 0x0c, 0x61, 0xa6, 0x1a, 0x40, 0x34, 0xd3,
+ 0x4d, 0x21, 0x80, 0xff, 0xd9, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0a,
+ 0x07, 0x07, 0x08, 0x07, 0x06, 0x0a, 0x08, 0x08, 0x08, 0x0b, 0x0a,
+ 0x0a, 0x0b, 0x0e, 0x18, 0x10, 0x0e, 0x0d, 0x0d, 0x0e, 0x1d, 0x15,
+ 0x16, 0x11, 0x18, 0x23, 0x1f, 0x25, 0x24, 0x22, 0x1f, 0x22, 0x21,
+ 0x26, 0x2b, 0x37, 0x2f, 0x26, 0x29, 0x34, 0x29, 0x21, 0x22, 0x30,
+ 0x41, 0x31, 0x34, 0x39, 0x3b, 0x3e, 0x3e, 0x3e, 0x25, 0x2e, 0x44,
+ 0x49, 0x43, 0x3c, 0x48, 0x37, 0x3d, 0x3e, 0x3b, 0x01, 0x0a, 0x0b,
+ 0x0b, 0x0e, 0x0d, 0x0e, 0x1c, 0x10, 0x10, 0x1c, 0x3b, 0x28, 0x22,
+ 0x28, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xc0, 0x00, 0x11,
+ 0x08, 0x00, 0x48, 0x00, 0x60, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11,
+ 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01,
+ 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02,
+ 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01,
+ 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
+ 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1,
+ 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33,
+ 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54,
+ 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
+ 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94,
+ 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
+ 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8,
+ 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
+ 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3,
+ 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, 0x00, 0x03, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03,
+ 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01,
+ 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51,
+ 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+ 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72,
+ 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19,
+ 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54,
+ 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
+ 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
+ 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
+ 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
+ 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2,
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4,
+ 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03,
+ 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0x9e, 0xd2,
+ 0x2e, 0x07, 0x15, 0xaf, 0x6d, 0x08, 0xe2, 0xb3, 0x45, 0x1a, 0xf6,
+ 0xd0, 0x00, 0x01, 0xc5, 0x68, 0x45, 0x17, 0x4a, 0xb4, 0x22, 0xe4,
+ 0x70, 0x8c, 0x74, 0xa9, 0x3c, 0xa1, 0x8e, 0x95, 0x48, 0x96, 0x31,
+ 0xe2, 0x18, 0xe9, 0x55, 0xa5, 0x8c, 0x7a, 0x50, 0x05, 0x0b, 0x88,
+ 0x86, 0x0f, 0x15, 0x8f, 0x75, 0x1f, 0x26, 0x93, 0x19, 0x91, 0x77,
+ 0x18, 0xc1, 0xac, 0x4b, 0xc8, 0xfa, 0xd6, 0x63, 0x37, 0x6d, 0x31,
+ 0xb4, 0x73, 0x5b, 0x36, 0xa0, 0x1c, 0x50, 0x80, 0xd7, 0x83, 0xa0,
+ 0xab, 0xd1, 0x62, 0xad, 0x09, 0x8f, 0x17, 0x29, 0x03, 0xb2, 0xcc,
+ 0xe0, 0x77, 0x14, 0xa3, 0x56, 0xb3, 0x27, 0x1e, 0x67, 0xe9, 0x52,
+ 0xea, 0xc6, 0x3a, 0x36, 0x48, 0xef, 0x3d, 0x27, 0x70, 0x22, 0x60,
+ 0x47, 0x52, 0x69, 0xb2, 0xe2, 0xad, 0x3b, 0xea, 0x80, 0xa3, 0x38,
+ 0xe0, 0xd6, 0x3d, 0xd8, 0x1c, 0xd0, 0xca, 0x46, 0x3d, 0xd0, 0x18,
+ 0x35, 0x89, 0x78, 0xa3, 0x9a, 0xcd, 0x8c, 0xd2, 0xb3, 0x93, 0x2a,
+ 0x2b, 0x66, 0xd5, 0xf1, 0x8a, 0x10, 0x1a, 0xd6, 0xf2, 0x03, 0x8a,
+ 0x9e, 0xe6, 0xf4, 0x5a, 0xdb, 0xef, 0xfe, 0x23, 0xc0, 0xa7, 0x27,
+ 0xcb, 0x16, 0xc4, 0xcc, 0xdd, 0xe2, 0x78, 0x9a, 0x69, 0x66, 0xcc,
+ 0x99, 0xe1, 0x4d, 0x47, 0xba, 0xbc, 0xd9, 0x6a, 0xee, 0x26, 0x59,
+ 0x59, 0x4d, 0xac, 0x69, 0x34, 0x52, 0xe5, 0x8f, 0x55, 0xad, 0x58,
+ 0xae, 0x85, 0xc4, 0x22, 0x41, 0xdf, 0xad, 0x76, 0x61, 0xe5, 0x6f,
+ 0x74, 0x45, 0x69, 0xdc, 0x00, 0x79, 0xac, 0x8b, 0xa6, 0xc9, 0x35,
+ 0xd4, 0x34, 0x64, 0xdc, 0x37, 0x06, 0xb1, 0xae, 0x88, 0xc1, 0xac,
+ 0xd8, 0xc9, 0x2c, 0xa6, 0xe0, 0x73, 0x5b, 0x36, 0xf3, 0x74, 0xe6,
+ 0x84, 0x05, 0xe3, 0xa9, 0x47, 0x6a, 0x14, 0xb6, 0x49, 0x3d, 0x85,
+ 0x3a, 0xee, 0xee, 0x2b, 0xa8, 0xe2, 0x6f, 0x30, 0x81, 0xe9, 0x8a,
+ 0xca, 0xa4, 0xe2, 0xd3, 0x8b, 0x01, 0xb1, 0xf9, 0x04, 0x7f, 0xaf,
+ 0x23, 0xf0, 0xa9, 0x54, 0x41, 0x9c, 0xfd, 0xa3, 0xf4, 0xae, 0x65,
+ 0x18, 0xf7, 0x25, 0x8a, 0xe2, 0x02, 0x38, 0xb8, 0xfd, 0x2a, 0x7b,
+ 0x5b, 0xa8, 0x6d, 0x6d, 0x5d, 0x9a, 0x5d, 0xcb, 0xbb, 0xd2, 0xb6,
+ 0xa6, 0xa3, 0x19, 0x5e, 0xe2, 0x03, 0x7b, 0x1d, 0xc2, 0x17, 0x8d,
+ 0xb8, 0xac, 0xfb, 0x89, 0x39, 0x35, 0xd6, 0x9a, 0x6a, 0xe8, 0x66,
+ 0x55, 0xcb, 0xf5, 0xac, 0x7b, 0x96, 0xeb, 0x50, 0xc6, 0x88, 0x6d,
+ 0x66, 0xe9, 0xcd, 0x6c, 0xdb, 0x4f, 0xd3, 0x9a, 0x00, 0x2f, 0xe6,
+ 0xf9, 0xa3, 0xe7, 0xb5, 0x4a, 0x93, 0x7f, 0xa2, 0xc6, 0x73, 0xdc,
+ 0xd7, 0x15, 0x55, 0xef, 0x48, 0x7d, 0x09, 0x52, 0x6e, 0x3a, 0xd4,
+ 0xab, 0x2f, 0xbd, 0x61, 0x16, 0x0c, 0x73, 0x49, 0xc5, 0x24, 0x92,
+ 0x7f, 0xa2, 0x63, 0xfd, 0xaa, 0xd6, 0x2f, 0x71, 0x0e, 0xb1, 0x93,
+ 0xf7, 0x2d, 0xf5, 0xa4, 0x9e, 0x4e, 0xb5, 0xdd, 0x4b, 0xf8, 0x68,
+ 0x4c, 0xcb, 0xb9, 0x93, 0xad, 0x65, 0xce, 0xd9, 0x26, 0xa9, 0x8d,
+ 0x19, 0xf6, 0xf2, 0xf4, 0xe6, 0xb5, 0xad, 0xe7, 0xc6, 0x39, 0xa0,
+ 0x18, 0xeb, 0xc9, 0x77, 0x6c, 0x35, 0x2a, 0x4b, 0xfe, 0x8a, 0x9c,
+ 0xff, 0x00, 0x11, 0xae, 0x3a, 0x8b, 0xde, 0x61, 0xd0, 0x9e, 0x39,
+ 0xb8, 0xeb, 0x53, 0xac, 0xb9, 0xae, 0x5b, 0x00, 0xf3, 0x27, 0x14,
+ 0x92, 0xc9, 0xfe, 0x8a, 0x3f, 0xde, 0x35, 0xac, 0x3a, 0x88, 0x92,
+ 0xcd, 0xb1, 0x6e, 0x7d, 0xcd, 0x32, 0x67, 0xeb, 0xcd, 0x7a, 0x14,
+ 0xfe, 0x04, 0x26, 0x66, 0xce, 0xf9, 0x26, 0xb3, 0xe6, 0x6e, 0xb4,
+ 0xd9, 0x48, 0xc8, 0x82, 0x4e, 0x07, 0x35, 0xa7, 0x6f, 0x2f, 0x02,
+ 0x9a, 0x06, 0x5f, 0x8c, 0xa4, 0x83, 0x0e, 0x32, 0x2a, 0x69, 0xe3,
+ 0xdd, 0x12, 0x08, 0x97, 0x85, 0xec, 0x2a, 0x2a, 0x42, 0xf1, 0x76,
+ 0x26, 0xe4, 0x6a, 0x59, 0x0e, 0x18, 0x10, 0x6a, 0xd2, 0x89, 0x02,
+ 0x6e, 0x2a, 0x71, 0xeb, 0x5c, 0x1c, 0x8c, 0xa6, 0x48, 0xbb, 0xdc,
+ 0x61, 0x41, 0x35, 0x72, 0x28, 0x87, 0xd9, 0xf6, 0x4a, 0xb9, 0xe7,
+ 0x38, 0xae, 0x8c, 0x3d, 0x36, 0xdd, 0xde, 0xc4, 0xb0, 0x21, 0x51,
+ 0x76, 0xa8, 0xc0, 0xaa, 0x93, 0x31, 0xe6, 0xbb, 0x2d, 0x65, 0x61,
+ 0x19, 0xd3, 0x1e, 0xb5, 0x46, 0x5a, 0x96, 0x5a, 0x30, 0xa0, 0x7e,
+ 0x05, 0x69, 0x5b, 0xc9, 0xc6, 0x28, 0x40, 0xcd, 0x08, 0x64, 0x3c,
+ 0x73, 0x57, 0xe1, 0x94, 0xf1, 0xcd, 0x5a, 0x21, 0x8c, 0xb9, 0x63,
+ 0xe7, 0x67, 0x1d, 0xab, 0x40, 0xb1, 0xfb, 0x00, 0x1d, 0xf0, 0x2b,
+ 0x99, 0x2d, 0x66, 0x3e, 0x88, 0x75, 0x81, 0x3f, 0x31, 0xf6, 0xab,
+ 0x64, 0xd6, 0xb4, 0x17, 0xee, 0xd0, 0x9e, 0xe4, 0x32, 0x1a, 0xa7,
+ 0x31, 0xad, 0x18, 0x14, 0x26, 0xef, 0x54, 0xa5, 0xa8, 0x65, 0xa3,
+ 0x9c, 0x81, 0xfa, 0x56, 0x8c, 0x2d, 0xce, 0x68, 0x40, 0xcb, 0xf1,
+ 0x37, 0xbd, 0x5e, 0x85, 0xea, 0xd1, 0x0c, 0xbb, 0x19, 0x56, 0x23,
+ 0x20, 0x1f, 0xad, 0x5c, 0x42, 0x08, 0x03, 0xb5, 0x55, 0x91, 0x04,
+ 0xc9, 0x80, 0x38, 0x00, 0x0a, 0x71, 0x34, 0x6c, 0x32, 0x27, 0xe9,
+ 0x55, 0x25, 0x15, 0x2c, 0x68, 0xa3, 0x30, 0xeb, 0x54, 0xa5, 0x15,
+ 0x0c, 0xd1, 0x00, 0xff, 0xd9};
+
+ /* package */ static final byte[] sPhotoByteArrayForComplicatedCase;
+
+ static {
+ final int length = sPhotoIntArrayForComplicatedCase.length;
+ sPhotoByteArrayForComplicatedCase = new byte[length];
+ for (int i = 0; i < length; i++) {
+ sPhotoByteArrayForComplicatedCase[i] = (byte)sPhotoIntArrayForComplicatedCase[i];
+ }
+ }
+
+
+
+ public class VerificationResolver extends MockContentResolver {
+ VerificationProvider mVerificationProvider = new VerificationProvider();
+ @Override
+ public ContentProviderResult[] applyBatch(String authority,
+ ArrayList<ContentProviderOperation> operations) {
+ equalsString(authority, RawContacts.CONTENT_URI.toString());
+ return mVerificationProvider.applyBatch(operations);
+ }
+
+ public void addExpectedContentValues(ContentValues expectedContentValues) {
+ mVerificationProvider.addExpectedContentValues(expectedContentValues);
+ }
+
+ public void verify() {
+ mVerificationProvider.verify();
+ }
+ }
+
+ private static final Set<String> sKnownMimeTypeSet =
+ new HashSet<String>(Arrays.asList(StructuredName.CONTENT_ITEM_TYPE,
+ Nickname.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE,
+ Email.CONTENT_ITEM_TYPE, StructuredPostal.CONTENT_ITEM_TYPE,
+ Im.CONTENT_ITEM_TYPE, Organization.CONTENT_ITEM_TYPE,
+ Event.CONTENT_ITEM_TYPE, Photo.CONTENT_ITEM_TYPE,
+ Note.CONTENT_ITEM_TYPE, Website.CONTENT_ITEM_TYPE,
+ Relation.CONTENT_ITEM_TYPE, Event.CONTENT_ITEM_TYPE,
+ GroupMembership.CONTENT_ITEM_TYPE));
+
+ private static boolean equalsForContentValues(
+ ContentValues expected, ContentValues actual) {
+ if (expected == actual) {
+ return true;
+ } else if (expected == null || actual == null || expected.size() != actual.size()) {
+ return false;
+ }
+ for (Entry<String, Object> entry : expected.valueSet()) {
+ final String key = entry.getKey();
+ final Object value = entry.getValue();
+ if (!actual.containsKey(key)) {
+ return false;
+ }
+ if (value instanceof byte[]) {
+ Object actualValue = actual.get(key);
+ if (!Arrays.equals((byte[])value, (byte[])actualValue)) {
+ return false;
+ }
+ } else if (!value.equals(actual.get(key))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ class VerificationProvider extends MockContentProvider {
+ final Map<String, Collection<ContentValues>> mMimeTypeToExpectedContentValues;
+
+ public VerificationProvider() {
+ mMimeTypeToExpectedContentValues =
+ new HashMap<String, Collection<ContentValues>>();
+ for (String acceptanbleMimeType : sKnownMimeTypeSet) {
+ // Do not use HashSet since the current implementation changes the content of
+ // ContentValues after the insertion, which make the result of hashCode()
+ // changes...
+ mMimeTypeToExpectedContentValues.put(
+ acceptanbleMimeType, new ArrayList<ContentValues>());
+ }
+ }
+
+ public void addExpectedContentValues(ContentValues expectedContentValues) {
+ final String mimeType = expectedContentValues.getAsString(Data.MIMETYPE);
+ if (!sKnownMimeTypeSet.contains(mimeType)) {
+ fail(String.format(
+ "Unknow MimeType %s in the test code. Test code should be broken.",
+ mimeType));
+ }
+
+ final Collection<ContentValues> contentValuesCollection =
+ mMimeTypeToExpectedContentValues.get(mimeType);
+ contentValuesCollection.add(expectedContentValues);
+ }
+
+ @Override
+ public ContentProviderResult[] applyBatch(
+ ArrayList<ContentProviderOperation> operations) {
+ if (operations == null) {
+ fail("There is no operation.");
+ }
+
+ final int size = operations.size();
+ ContentProviderResult[] fakeResultArray = new ContentProviderResult[size];
+ for (int i = 0; i < size; i++) {
+ Uri uri = Uri.withAppendedPath(RawContacts.CONTENT_URI, String.valueOf(i));
+ fakeResultArray[i] = new ContentProviderResult(uri);
+ }
+
+ for (int i = 0; i < size; i++) {
+ ContentProviderOperation operation = operations.get(i);
+ ContentValues actualContentValues = operation.resolveValueBackReferences(
+ fakeResultArray, i);
+ final Uri uri = operation.getUri();
+ if (uri.equals(RawContacts.CONTENT_URI)) {
+ assertNull(actualContentValues.get(RawContacts.ACCOUNT_NAME));
+ assertNull(actualContentValues.get(RawContacts.ACCOUNT_TYPE));
+ } else if (uri.equals(Data.CONTENT_URI)) {
+ final String mimeType = actualContentValues.getAsString(Data.MIMETYPE);
+ if (!sKnownMimeTypeSet.contains(mimeType)) {
+ fail(String.format(
+ "Unknown MimeType %s. Probably added after developing this test",
+ mimeType));
+ }
+ // Remove data meaningless in this unit tests.
+ // Specifically, Data.DATA1 - DATA7 are set to null or empty String
+ // regardless of the input, but it may change depending on how
+ // resolver-related code handles it.
+ // Here, we ignore these implementation-dependent specs and
+ // just check whether vCard importer correctly inserts rellevent data.
+ Set<String> keyToBeRemoved = new HashSet<String>();
+ for (Entry<String, Object> entry : actualContentValues.valueSet()) {
+ Object value = entry.getValue();
+ if (value == null || TextUtils.isEmpty(value.toString())) {
+ keyToBeRemoved.add(entry.getKey());
+ }
+ }
+ for (String key: keyToBeRemoved) {
+ actualContentValues.remove(key);
+ }
+ /* For testing
+ Log.d("@@@",
+ String.format("MimeType: %s, data: %s",
+ mimeType, actualContentValues.toString()));
+ */
+ // Remove RAW_CONTACT_ID entry just for safety, since we do not care
+ // how resolver-related code handles the entry in this unit test,
+ if (actualContentValues.containsKey(Data.RAW_CONTACT_ID)) {
+ actualContentValues.remove(Data.RAW_CONTACT_ID);
+ }
+ final Collection<ContentValues> contentValuesCollection =
+ mMimeTypeToExpectedContentValues.get(mimeType);
+ if (contentValuesCollection == null) {
+ fail("ContentValues for MimeType " + mimeType
+ + " is not expected at all (" + actualContentValues + ")");
+ }
+ boolean checked = false;
+ for (ContentValues expectedContentValues : contentValuesCollection) {
+ /* For testing
+ Log.d("@@@", "expected: "
+ + convertToEasilyReadableString(expectedContentValues));
+ Log.d("@@@", "actual : "
+ + convertToEasilyReadableString(actualContentValues));
+ */
+ if (equalsForContentValues(expectedContentValues,
+ actualContentValues)) {
+ assertTrue(contentValuesCollection.remove(expectedContentValues));
+ checked = true;
+ break;
+ }
+ }
+ if (!checked) {
+ final String failMsg =
+ "Unexpected ContentValues for MimeType " + mimeType
+ + ": " + actualContentValues;
+ fail(failMsg);
+ }
+ } else {
+ fail("Unexpected Uri has come: " + uri);
+ }
+ } // for (int i = 0; i < size; i++) {
+ return null;
+ }
+
+ public void verify() {
+ StringBuilder builder = new StringBuilder();
+ for (Collection<ContentValues> contentValuesCollection :
+ mMimeTypeToExpectedContentValues.values()) {
+ for (ContentValues expectedContentValues: contentValuesCollection) {
+ builder.append(convertToEasilyReadableString(expectedContentValues));
+ builder.append("\n");
+ }
+ }
+ if (builder.length() > 0) {
+ final String failMsg =
+ "There is(are) remaining expected ContentValues instance(s): \n"
+ + builder.toString();
+ fail(failMsg);
+ }
+ }
+ }
+
+ /**
+ * Utility method to print ContentValues whose content is printed with sorted keys.
+ */
+ private static String convertToEasilyReadableString(ContentValues contentValues) {
+ if (contentValues == null) {
+ return "null";
+ }
+ String mimeTypeValue = "";
+ SortedMap<String, String> sortedMap = new TreeMap<String, String>();
+ for (Entry<String, Object> entry : contentValues.valueSet()) {
+ final String key = entry.getKey();
+ final String value = entry.getValue().toString();
+ if (Data.MIMETYPE.equals(key)) {
+ mimeTypeValue = value;
+ } else {
+ assertNotNull(key);
+ sortedMap.put(key, (value != null ? value.toString() : ""));
+ }
+ }
+ StringBuilder builder = new StringBuilder();
+ builder.append(Data.MIMETYPE);
+ builder.append('=');
+ builder.append(mimeTypeValue);
+ for (Entry<String, String> entry : sortedMap.entrySet()) {
+ final String key = entry.getKey();
+ final String value = entry.getValue();
+ builder.append(' ');
+ builder.append(key);
+ builder.append('=');
+ builder.append(value);
+ }
+ return builder.toString();
+ }
+
+ 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 class ContactStructVerifier {
+ private final int mResourceId;
+ private final int mVCardType;
+ private final VerificationResolver mResolver;
+ // private final String mCharset;
+ public ContactStructVerifier(int resId, int vCardType) {
+ mResourceId = resId;
+ mVCardType = vCardType;
+ mResolver = new VerificationResolver();
+ }
+
+ public ContentValues createExpected(String mimeType) {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(Data.MIMETYPE, mimeType);
+ mResolver.addExpectedContentValues(contentValues);
+ return contentValues;
+ }
+
+ public void verify() throws IOException, VCardException {
+ InputStream is = getContext().getResources().openRawResource(mResourceId);
+ final VCardParser vCardParser;
+ if (VCardConfig.isV30(mVCardType)) {
+ vCardParser = new VCardParser_V30(true); // use StrictParsing
+ } else {
+ vCardParser = new VCardParser_V21();
+ }
+ VCardDataBuilder builder =
+ new VCardDataBuilder(null, null, false, mVCardType, null);
+ builder.addEntryHandler(new EntryCommitter(mResolver));
+ try {
+ vCardParser.parse(is, builder);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ mResolver.verify();
+ }
+ }
+
+ public void testV21SimpleCase1_Parsing() throws IOException, VCardException {
+ VCardParser_V21 parser = new VCardParser_V21();
+ VNodeBuilder builder = new VNodeBuilder();
+ 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());
+ PropertyNodesVerifier verifier = new PropertyNodesVerifier(this)
+ .addNodeWithOrder("N", "Ando;Roid;", Arrays.asList("Ando", "Roid", ""));
+ verifier.verify(builder.vNodeList.get(0));
+ }
+
+ public void testV21SimpleCase1_Type_Generic() throws IOException, VCardException {
+ ContactStructVerifier verifier = new ContactStructVerifier(
+ R.raw.v21_simple_1, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
+ ContentValues contentValues =
+ verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "Ando");
+ contentValues.put(StructuredName.GIVEN_NAME, "Roid");
+ contentValues.put(StructuredName.DISPLAY_NAME, "Roid Ando");
+ verifier.verify();
+ }
+
+ public void testV21SimpleCase1_Type_Japanese() throws IOException, VCardException {
+ ContactStructVerifier verifier = new ContactStructVerifier(
+ R.raw.v21_simple_1, VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS);
+ ContentValues contentValues =
+ verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "Ando");
+ contentValues.put(StructuredName.GIVEN_NAME, "Roid");
+ // If name-related strings only contains printable Ascii, the order is remained to be US's:
+ // "Prefix Given Middle Family Suffix"
+ contentValues.put(StructuredName.DISPLAY_NAME, "Roid Ando");
+ verifier.verify();
+ }
+
+ public void testV21SimpleCase2() throws IOException, VCardException {
+ ContactStructVerifier verifier = new ContactStructVerifier(
+ R.raw.v21_simple_2, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
+ ContentValues contentValues =
+ verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.DISPLAY_NAME, "Ando Roid");
+ verifier.verify();
+ }
+
+ public void testV21SimpleCase3() throws IOException, VCardException {
+ ContactStructVerifier verifier = new ContactStructVerifier(
+ R.raw.v21_simple_3, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
+ ContentValues contentValues =
+ verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "Ando");
+ contentValues.put(StructuredName.GIVEN_NAME, "Roid");
+ // "FN" field should be prefered since it should contain the original order intended by
+ // the author of the file.
+ contentValues.put(StructuredName.DISPLAY_NAME, "Ando Roid");
+ verifier.verify();
+ }
+
+ /**
+ * Tests ';' is properly handled by VCardParser implementation.
+ */
+ public void testV21BackslashCase_Parsing() throws IOException, VCardException {
+ VCardParser_V21 parser = new VCardParser_V21();
+ VNodeBuilder builder = new VNodeBuilder();
+ InputStream is = getContext().getResources().openRawResource(R.raw.v21_backslash);
+ assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
+ is.close();
+ assertEquals(1, builder.vNodeList.size());
+ PropertyNodesVerifier verifier = new PropertyNodesVerifier(this)
+ .addNodeWithOrder("VERSION", "2.1")
+ .addNodeWithOrder("N", ";A;B\\;C\\;;D;:E;\\\\;",
+ Arrays.asList("", "A;B\\", "C\\;", "D", ":E", "\\\\", ""))
+ .addNodeWithOrder("FN", "A;B\\C\\;D:E\\\\");
+ verifier.verify(builder.vNodeList.get(0));
+ }
+
+ /**
+ * Tests ContactStruct correctly ignores redundant fields in "N" property values and
+ * inserts name related data.
+ */
+ public void testV21BackslashCase() throws IOException, VCardException {
+ ContactStructVerifier verifier = new ContactStructVerifier(
+ R.raw.v21_backslash, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
+ ContentValues contentValues =
+ verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
+ // FAMILY_NAME is empty and removed in this test...
+ contentValues.put(StructuredName.GIVEN_NAME, "A;B\\");
+ contentValues.put(StructuredName.MIDDLE_NAME, "C\\;");
+ contentValues.put(StructuredName.PREFIX, "D");
+ contentValues.put(StructuredName.SUFFIX, ":E");
+ contentValues.put(StructuredName.DISPLAY_NAME, "A;B\\C\\;D:E\\\\");
+ verifier.verify();
+ }
+
+ public void testOrgBeforTitle() throws IOException, VCardException {
+ ContactStructVerifier verifier = new ContactStructVerifier(
+ R.raw.v21_org_before_title, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
+ ContentValues contentValues =
+ verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.DISPLAY_NAME, "Normal Guy");
+
+ contentValues = verifier.createExpected(Organization.CONTENT_ITEM_TYPE);
+ contentValues.put(Organization.COMPANY, "Company");
+ contentValues.put(Organization.DEPARTMENT, "Organization Devision Room Sheet No.");
+ contentValues.put(Organization.TITLE, "Excellent Janitor");
+ contentValues.put(Organization.TYPE, Organization.TYPE_WORK);
+ verifier.verify();
+ }
+
+ public void testTitleBeforOrg() throws IOException, VCardException {
+ ContactStructVerifier verifier = new ContactStructVerifier(
+ R.raw.v21_title_before_org, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
+ ContentValues contentValues =
+ verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.DISPLAY_NAME, "Nice Guy");
+
+ contentValues = verifier.createExpected(Organization.CONTENT_ITEM_TYPE);
+ contentValues.put(Organization.COMPANY, "Marverous");
+ contentValues.put(Organization.DEPARTMENT, "Perfect Great Good Bad Poor");
+ contentValues.put(Organization.TITLE, "Cool Title");
+ contentValues.put(Organization.TYPE, Organization.TYPE_WORK);
+ verifier.verify();
+ }
+
+ /**
+ * Verifies that vCard importer correctly interpret "PREF" attribute to IS_PRIMARY.
+ * The data contain three cases: one "PREF", no "PREF" and multiple "PREF", in each type.
+ */
+ public void testV21PrefToIsPrimary() throws IOException, VCardException {
+ ContactStructVerifier verifier = new ContactStructVerifier(
+ R.raw.v21_pref_handling, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
+ ContentValues contentValues =
+ verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.DISPLAY_NAME, "Smith");
+
+ contentValues = verifier.createExpected(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "1");
+ contentValues.put(Phone.TYPE, Phone.TYPE_HOME);
+
+ contentValues = verifier.createExpected(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "2");
+ contentValues.put(Phone.TYPE, Phone.TYPE_WORK);
+ contentValues.put(Phone.IS_PRIMARY, 1);
+
+ contentValues = verifier.createExpected(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "3");
+ contentValues.put(Phone.TYPE, Phone.TYPE_ISDN);
+
+ contentValues = verifier.createExpected(Email.CONTENT_ITEM_TYPE);
+ contentValues.put(Email.DATA, "test@example.com");
+ contentValues.put(Email.TYPE, Email.TYPE_HOME);
+ contentValues.put(Email.IS_PRIMARY, 1);
+
+ contentValues = verifier.createExpected(Email.CONTENT_ITEM_TYPE);
+ contentValues.put(Email.DATA, "test2@examination.com");
+ contentValues.put(Email.TYPE, Email.TYPE_MOBILE);
+ contentValues.put(Email.IS_PRIMARY, 1);
+
+ contentValues = verifier.createExpected(Organization.CONTENT_ITEM_TYPE);
+ contentValues.put(Organization.COMPANY, "Company");
+ contentValues.put(Organization.TITLE, "Engineer");
+ contentValues.put(Organization.TYPE, Organization.TYPE_WORK);
+
+ contentValues = verifier.createExpected(Organization.CONTENT_ITEM_TYPE);
+ contentValues.put(Organization.COMPANY, "Mystery");
+ contentValues.put(Organization.TITLE, "Blogger");
+ contentValues.put(Organization.TYPE, Organization.TYPE_WORK);
+
+ contentValues = verifier.createExpected(Organization.CONTENT_ITEM_TYPE);
+ contentValues.put(Organization.COMPANY, "Poetry");
+ contentValues.put(Organization.TITLE, "Poet");
+ contentValues.put(Organization.TYPE, Organization.TYPE_WORK);
+ verifier.verify();
+ }
+
+ /**
+ * Tests all the properties in a complicated vCard are correctly parsed by the VCardParser.
+ */
+ public void testV21ComplicatedCase_Parsing() throws IOException, VCardException {
+ VCardParser_V21 parser = new VCardParser_V21();
+ VNodeBuilder builder = new VNodeBuilder();
+ InputStream is = getContext().getResources().openRawResource(R.raw.v21_complicated);
+ assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
+ is.close();
+ assertEquals(1, builder.vNodeList.size());
+ ContentValues contentValuesForQP = new ContentValues();
+ contentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
+ ContentValues contentValuesForPhoto = new ContentValues();
+ contentValuesForPhoto.put("ENCODING", "BASE64");
+ PropertyNodesVerifier verifier = new PropertyNodesVerifier(this)
+ .addNodeWithOrder("VERSION", "2.1")
+ .addNodeWithOrder("N", "Gump;Forrest;Hoge;Pos;Tao",
+ Arrays.asList("Gump", "Forrest", "Hoge", "Pos", "Tao"))
+ .addNodeWithOrder("FN", "Joe Due")
+ .addNodeWithOrder("ORG", "Gump Shrimp Co.;Sales Dept.;Manager;Fish keeper",
+ Arrays.asList("Gump Shrimp Co.", "Sales Dept.;Manager", "Fish keeper"))
+ .addNodeWithOrder("ROLE", "Fish Cake Keeper!")
+ .addNodeWithOrder("TITLE", "Shrimp Man")
+ .addNodeWithOrder("X-CLASS", "PUBLIC")
+ .addNodeWithOrder("TEL", "(111) 555-1212", new TypeSet("WORK", "VOICE"))
+ .addNodeWithOrder("TEL", "(404) 555-1212", new TypeSet("HOME", "VOICE"))
+ .addNodeWithOrder("TEL", "0311111111", new TypeSet("CELL"))
+ .addNodeWithOrder("TEL", "0322222222", new TypeSet("VIDEO"))
+ .addNodeWithOrder("TEL", "0333333333", new TypeSet("VOICE"))
+ .addNodeWithOrder("ADR", ";;100 Waters Edge;Baytown;LA;30314;United States of America",
+ Arrays.asList("", "", "100 Waters Edge", "Baytown",
+ "LA", "30314", "United States of America"),
+ null, null, new TypeSet("WORK"), null)
+ .addNodeWithOrder("LABEL",
+ "100 Waters Edge\r\nBaytown, LA 30314\r\nUnited States of America",
+ null, null, contentValuesForQP, new TypeSet("WORK"), null)
+ .addNodeWithOrder("ADR",
+ ";;42 Plantation St.;Baytown;LA;30314;United States of America",
+ Arrays.asList("", "", "42 Plantation St.", "Baytown",
+ "LA", "30314", "United States of America"), null, null,
+ new TypeSet("HOME"), null)
+ .addNodeWithOrder("LABEL",
+ "42 Plantation St.\r\nBaytown, LA 30314\r\nUnited States of America",
+ null, null, contentValuesForQP,
+ new TypeSet("HOME"), null)
+ .addNodeWithOrder("EMAIL", "forrestgump@walladalla.com", new TypeSet("PREF", "INTERNET"))
+ .addNodeWithOrder("EMAIL", "cell@example.com", new TypeSet("CELL"))
+ .addNodeWithOrder("NOTE", "The following note is the example from RFC 2045.")
+ .addNodeWithOrder("NOTE",
+ "Now's the time for all folk to come to the aid of their country.",
+ null, null, contentValuesForQP, null, null)
+ .addNodeWithOrder("PHOTO", null,
+ null, sPhotoByteArrayForComplicatedCase, contentValuesForPhoto,
+ new TypeSet("JPEG"), null)
+ .addNodeWithOrder("X-ATTRIBUTE", "Some String")
+ .addNodeWithOrder("BDAY", "19800101")
+ .addNodeWithOrder("GEO", "35.6563854,139.6994233")
+ .addNodeWithOrder("URL", "http://www.example.com/")
+ .addNodeWithOrder("REV", "20080424T195243Z");
+ verifier.verify(builder.vNodeList.get(0));
+ }
+
+ /**
+ * Checks ContactStruct correctly inserts values in a complicated vCard
+ * into ContentResolver.
+ */
+ public void testV21ComplicatedCase() throws IOException, VCardException {
+ ContactStructVerifier verifier = new ContactStructVerifier(
+ R.raw.v21_complicated, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
+ ContentValues contentValues =
+ verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "Gump");
+ contentValues.put(StructuredName.GIVEN_NAME, "Forrest");
+ contentValues.put(StructuredName.MIDDLE_NAME, "Hoge");
+ contentValues.put(StructuredName.PREFIX, "Pos");
+ contentValues.put(StructuredName.SUFFIX, "Tao");
+ contentValues.put(StructuredName.DISPLAY_NAME, "Joe Due");
+
+ contentValues = verifier.createExpected(Organization.CONTENT_ITEM_TYPE);
+ contentValues.put(Organization.TYPE, Organization.TYPE_WORK);
+ contentValues.put(Organization.COMPANY, "Gump Shrimp Co.");
+ contentValues.put(Organization.DEPARTMENT, "Sales Dept.;Manager Fish keeper");
+ contentValues.put(Organization.TITLE, "Shrimp Man");
+
+ contentValues = verifier.createExpected(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.TYPE, Phone.TYPE_WORK);
+ // Phone number is expected to be formated with NAMP format in default.
+ contentValues.put(Phone.NUMBER, "111-555-1212");
+
+ contentValues = verifier.createExpected(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.TYPE, Phone.TYPE_HOME);
+ contentValues.put(Phone.NUMBER, "404-555-1212");
+
+ contentValues = verifier.createExpected(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.TYPE, Phone.TYPE_MOBILE);
+ contentValues.put(Phone.NUMBER, "031-111-1111");
+
+ contentValues = verifier.createExpected(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.TYPE, Phone.TYPE_CUSTOM);
+ contentValues.put(Phone.LABEL, "VIDEO");
+ contentValues.put(Phone.NUMBER, "032-222-2222");
+
+ contentValues = verifier.createExpected(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.TYPE, Phone.TYPE_CUSTOM);
+ contentValues.put(Phone.LABEL, "VOICE");
+ contentValues.put(Phone.NUMBER, "033-333-3333");
+
+ contentValues = verifier.createExpected(StructuredPostal.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK);
+ contentValues.put(StructuredPostal.COUNTRY, "United States of America");
+ contentValues.put(StructuredPostal.POSTCODE, "30314");
+ contentValues.put(StructuredPostal.REGION, "LA");
+ contentValues.put(StructuredPostal.CITY, "Baytown");
+ contentValues.put(StructuredPostal.STREET, "100 Waters Edge");
+ contentValues.put(StructuredPostal.FORMATTED_ADDRESS,
+ "100 Waters Edge Baytown LA 30314 United States of America");
+
+ contentValues = verifier.createExpected(StructuredPostal.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME);
+ contentValues.put(StructuredPostal.COUNTRY, "United States of America");
+ contentValues.put(StructuredPostal.POSTCODE, "30314");
+ contentValues.put(StructuredPostal.REGION, "LA");
+ contentValues.put(StructuredPostal.CITY, "Baytown");
+ contentValues.put(StructuredPostal.STREET, "42 Plantation St.");
+ contentValues.put(StructuredPostal.FORMATTED_ADDRESS,
+ "42 Plantation St. Baytown LA 30314 United States of America");
+
+ contentValues = verifier.createExpected(Email.CONTENT_ITEM_TYPE);
+ // "TYPE=INTERNET" -> TYPE_CUSTOM + the label "INTERNET"
+ contentValues.put(Email.TYPE, Email.TYPE_CUSTOM);
+ contentValues.put(Email.LABEL, "INTERNET");
+ contentValues.put(Email.DATA, "forrestgump@walladalla.com");
+ contentValues.put(Email.IS_PRIMARY, 1);
+
+ contentValues = verifier.createExpected(Email.CONTENT_ITEM_TYPE);
+ contentValues.put(Email.TYPE, Email.TYPE_MOBILE);
+ contentValues.put(Email.DATA, "cell@example.com");
+
+ contentValues = verifier.createExpected(Note.CONTENT_ITEM_TYPE);
+ contentValues.put(Note.NOTE, "The following note is the example from RFC 2045.");
+
+ contentValues = verifier.createExpected(Note.CONTENT_ITEM_TYPE);
+ contentValues.put(Note.NOTE,
+ "Now's the time for all folk to come to the aid of their country.");
+
+ contentValues = verifier.createExpected(Photo.CONTENT_ITEM_TYPE);
+ // No information about its image format can be inserted.
+ contentValues.put(Photo.PHOTO, sPhotoByteArrayForComplicatedCase);
+
+ contentValues = verifier.createExpected(Event.CONTENT_ITEM_TYPE);
+ contentValues.put(Event.START_DATE, "19800101");
+ contentValues.put(Event.TYPE, Event.TYPE_BIRTHDAY);
+
+ contentValues = verifier.createExpected(Website.CONTENT_ITEM_TYPE);
+ contentValues.put(Website.URL, "http://www.example.com/");
+ contentValues.put(Website.TYPE, Website.TYPE_HOMEPAGE);
+ verifier.verify();
+ }
+
+ public void testV30Simple_Parsing() throws IOException, VCardException {
+ VCardParser_V21 parser = new VCardParser_V30();
+ VNodeBuilder builder = new VNodeBuilder();
+ InputStream is = getContext().getResources().openRawResource(R.raw.v30_simple);
+ assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
+ is.close();
+ assertEquals(1, builder.vNodeList.size());
+ PropertyNodesVerifier verifier = new PropertyNodesVerifier(this)
+ .addNodeWithOrder("VERSION", "3.0")
+ .addNodeWithOrder("FN", "And Roid")
+ .addNodeWithOrder("N", "And;Roid;;;", Arrays.asList("And", "Roid", "", "", ""))
+ .addNodeWithOrder("ORG", "Open;Handset; Alliance",
+ Arrays.asList("Open", "Handset", " Alliance"))
+ .addNodeWithOrder("SORT-STRING", "android")
+ .addNodeWithOrder("TEL", "0300000000", new TypeSet("PREF", "VOICE"))
+ .addNodeWithOrder("CLASS", "PUBLIC")
+ .addNodeWithOrder("X-GNO", "0")
+ .addNodeWithOrder("X-GN", "group0")
+ .addNodeWithOrder("X-REDUCTION", "0")
+ .addNodeWithOrder("REV", "20081031T065854Z");
+ verifier.verify(builder.vNodeList.get(0));
+ }
+
+ public void testV30Simple() throws IOException, VCardException {
+ ContactStructVerifier verifier = new ContactStructVerifier(
+ R.raw.v30_simple, VCardConfig.VCARD_TYPE_V30_GENERIC_UTF8);
+ ContentValues contentValues =
+ verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "And");
+ contentValues.put(StructuredName.GIVEN_NAME, "Roid");
+ contentValues.put(StructuredName.DISPLAY_NAME, "And Roid");
+
+ contentValues = verifier.createExpected(Organization.CONTENT_ITEM_TYPE);
+ contentValues.put(Organization.COMPANY, "Open");
+ contentValues.put(Organization.DEPARTMENT, "Handset Alliance");
+ contentValues.put(Organization.TYPE, Organization.TYPE_WORK);
+
+ contentValues = verifier.createExpected(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.TYPE, Phone.TYPE_CUSTOM);
+ contentValues.put(Phone.LABEL, "VOICE");
+ contentValues.put(Phone.NUMBER, "030-000-0000");
+ contentValues.put(Phone.IS_PRIMARY, 1);
+ verifier.verify();
+ }
+
+ public void testV21Japanese1_Parsing() throws IOException, VCardException {
+ VCardParser_V21 parser = new VCardParser_V21();
+ 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();
+ assertEquals(1, builder.vNodeList.size());
+ ContentValues contentValuesForShiftJis = new ContentValues();
+ contentValuesForShiftJis.put("CHARSET", "SHIFT_JIS");
+ ContentValues contentValuesForQP = new ContentValues();
+ contentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
+ contentValuesForQP.put("CHARSET", "SHIFT_JIS");
+ // Though Japanese careers append ";;;;" at the end of the value of "SOUND",
+ // vCard 2.1/3.0 specification does not allow multiple values.
+ // Do not need to handle it as multiple values.
+ PropertyNodesVerifier verifier = new PropertyNodesVerifier(this)
+ .addNodeWithOrder("VERSION", "2.1", null, null, null, null, null)
+ .addNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9;;;;",
+ Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9", "", "", "", ""),
+ null, contentValuesForShiftJis, null, null)
+ .addNodeWithOrder("SOUND", "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E;;;;",
+ null, null, contentValuesForShiftJis,
+ new TypeSet("X-IRMC-N"), null)
+ .addNodeWithOrder("TEL", "0300000000", null, null, null,
+ new TypeSet("VOICE", "PREF"), null);
+ verifier.verify(builder.vNodeList.get(0));
+ }
+
+ private void testV21Japanese1Common(ContactStructVerifier verifier, boolean japanese)
+ throws IOException, VCardException {
+ ContentValues contentValues =
+ verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9");
+ contentValues.put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9");
+ // While vCard parser does not split "SOUND" property values, ContactStruct care it.
+ contentValues.put(StructuredName.PHONETIC_FAMILY_NAME,
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E");
+
+ contentValues = verifier.createExpected(Phone.CONTENT_ITEM_TYPE);
+ // Phone number formatting is different.
+ if (japanese) {
+ contentValues.put(Phone.NUMBER, "03-0000-0000");
+ } else {
+ contentValues.put(Phone.NUMBER, "030-000-0000");
+ }
+ contentValues.put(Phone.TYPE, Phone.TYPE_CUSTOM);
+ contentValues.put(Phone.LABEL, "VOICE");
+ contentValues.put(Phone.IS_PRIMARY, 1);
+ verifier.verify();
+ }
+ /**
+ * Verifies vCard with Japanese can be parsed correctly with
+ * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_GENERIC_UTF8}.
+ */
+ public void testV21Japanese1_Type_Generic_Utf8() throws IOException, VCardException {
+ ContactStructVerifier verifier = new ContactStructVerifier(
+ R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
+ testV21Japanese1Common(verifier, false);
+ }
+
+ /**
+ * Verifies vCard with Japanese can be parsed correctly with
+ * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE_SJIS}.
+ */
+ public void testV21Japanese1_Type_Japanese_Sjis() throws IOException, VCardException {
+ ContactStructVerifier verifier = new ContactStructVerifier(
+ R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS);
+ testV21Japanese1Common(verifier, true);
+ }
+
+ /**
+ * Verifies vCard with Japanese can be parsed correctly with
+ * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE_UTF8}.
+ * since vCard 2.1 specifies the charset of each line if it contains non-Ascii.
+ */
+ public void testV21Japanese1_Type_Japanese_Utf8() throws IOException, VCardException {
+ ContactStructVerifier verifier = new ContactStructVerifier(
+ R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8);
+ testV21Japanese1Common(verifier, true);
+ }
+
+ public void testV21Japanese2_Parsing() throws IOException, VCardException {
+ VCardParser_V21 parser = new VCardParser_V21();
+ 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();
+ assertEquals(1, builder.vNodeList.size());
+ ContentValues contentValuesForShiftJis = new ContentValues();
+ contentValuesForShiftJis.put("CHARSET", "SHIFT_JIS");
+ ContentValues contentValuesForQP = new ContentValues();
+ contentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
+ contentValuesForQP.put("CHARSET", "SHIFT_JIS");
+ PropertyNodesVerifier verifier = new PropertyNodesVerifier(this)
+ .addNodeWithOrder("VERSION", "2.1")
+ .addNodeWithOrder("N", "\u5B89\u85E4;\u30ED\u30A4\u30C9\u0031;;;",
+ Arrays.asList("\u5B89\u85E4", "\u30ED\u30A4\u30C9\u0031",
+ "", "", ""),
+ null, contentValuesForShiftJis, null, null)
+ .addNodeWithOrder("FN", "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9\u0020\u0031",
+ null, null, contentValuesForShiftJis, null, null)
+ .addNodeWithOrder("SOUND",
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73;\uFF9B\uFF72\uFF84\uFF9E\u0031;;;",
+ null, null, contentValuesForShiftJis,
+ new TypeSet("X-IRMC-N"), null)
+ .addNodeWithOrder("ADR",
+ ";\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
+ "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
+ "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC\u0036" +
+ "\u968E;;;;150-8512;",
+ Arrays.asList("",
+ "\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
+ "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
+ "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" +
+ "\u0036\u968E", "", "", "", "150-8512", ""),
+ null, contentValuesForQP, new TypeSet("HOME"), null)
+ .addNodeWithOrder("NOTE", "\u30E1\u30E2", null, null, contentValuesForQP, null, null);
+ verifier.verify(builder.vNodeList.get(0));
+ }
+
+ public void testV21Japanese2_Type_Generic_Utf8() throws IOException, VCardException {
+ ContactStructVerifier verifier = new ContactStructVerifier(
+ R.raw.v21_japanese_2, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
+ ContentValues contentValues =
+ verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "\u5B89\u85E4");
+ contentValues.put(StructuredName.GIVEN_NAME, "\u30ED\u30A4\u30C9\u0031");
+ contentValues.put(StructuredName.DISPLAY_NAME,
+ "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9\u0020\u0031");
+ // ContactStruct should correctly split "SOUND" property into several elements,
+ // even though VCardParser side does not care it.
+ contentValues.put(StructuredName.PHONETIC_FAMILY_NAME,
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73");
+ contentValues.put(StructuredName.PHONETIC_GIVEN_NAME,
+ "\uFF9B\uFF72\uFF84\uFF9E\u0031");
+
+ contentValues = verifier.createExpected(StructuredPostal.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredPostal.POSTCODE, "150-8512");
+ contentValues.put(StructuredPostal.NEIGHBORHOOD,
+ "\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
+ "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
+ "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" +
+ "\u0036\u968E");
+ contentValues.put(StructuredPostal.FORMATTED_ADDRESS,
+ "\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
+ "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
+ "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" +
+ "\u0036\u968E 150-8512");
+ contentValues.put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME);
+ contentValues = verifier.createExpected(Note.CONTENT_ITEM_TYPE);
+ contentValues.put(Note.NOTE, "\u30E1\u30E2");
+ verifier.verify();
+ }
+
+ // Following tests are old ones, though they still work fine.
+
+ public void testV21MultipleEntryCase() throws IOException, VCardException {
+ VCardParser_V21 parser = new VCardParser_V21();
+ 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();
+ assertEquals(3, builder.vNodeList.size());
+ ContentValues contentValuesForShiftJis = new ContentValues();
+ contentValuesForShiftJis.put("CHARSET", "SHIFT_JIS");
+ PropertyNodesVerifier verifier = new PropertyNodesVerifier(this)
+ .addNodeWithOrder("VERSION", "2.1")
+ .addNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033;;;;",
+ Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0033", "", "", "", ""),
+ null, contentValuesForShiftJis, null, null)
+ .addNodeWithOrder("SOUND",
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0033;;;;",
+ null, null, contentValuesForShiftJis,
+ new TypeSet("X-IRMC-N"), null)
+ .addNodeWithOrder("TEL", "9", new TypeSet("X-NEC-SECRET"))
+ .addNodeWithOrder("TEL", "10", new TypeSet("X-NEC-HOTEL"))
+ .addNodeWithOrder("TEL", "11", new TypeSet("X-NEC-SCHOOL"))
+ .addNodeWithOrder("TEL", "12", new TypeSet("FAX", "HOME"));
+ verifier.verify(builder.vNodeList.get(0));
+
+ verifier = new PropertyNodesVerifier(this)
+ .addNodeWithOrder("VERSION", "2.1")
+ .addNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034;;;;",
+ Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0034", "", "", "", ""),
+ null, contentValuesForShiftJis, null, null)
+ .addNodeWithOrder("SOUND",
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0034;;;;",
+ null, null, contentValuesForShiftJis,
+ new TypeSet("X-IRMC-N"), null)
+ .addNodeWithOrder("TEL", "13", new TypeSet("MODEM"))
+ .addNodeWithOrder("TEL", "14", new TypeSet("PAGER"))
+ .addNodeWithOrder("TEL", "15", new TypeSet("X-NEC-FAMILY"))
+ .addNodeWithOrder("TEL", "16", new TypeSet("X-NEC-GIRL"));
+ verifier.verify(builder.vNodeList.get(1));
+ verifier = new PropertyNodesVerifier(this)
+ .addNodeWithOrder("VERSION", "2.1")
+ .addNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035;;;;",
+ Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0035", "", "", "", ""),
+ null, contentValuesForShiftJis, null, null)
+ .addNodeWithOrder("SOUND",
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0035;;;;",
+ null, null, contentValuesForShiftJis,
+ new TypeSet("X-IRMC-N"), null)
+ .addNodeWithOrder("TEL", "17", new TypeSet("X-NEC-BOY"))
+ .addNodeWithOrder("TEL", "18", new TypeSet("X-NEC-FRIEND"))
+ .addNodeWithOrder("TEL", "19", new TypeSet("X-NEC-PHS"))
+ .addNodeWithOrder("TEL", "20", new TypeSet("X-NEC-RESTAURANT"));
+ verifier.verify(builder.vNodeList.get(2));
+ }
+}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTests.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTests.java
index 7589ba8..b4bb14b 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTests.java
@@ -1,923 +1,15 @@
-/*
- * 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.ContactStruct;
-import android.pim.vcard.EntryHandler;
-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.AndroidTests;
-import com.android.unit_tests.R;
+import android.test.suitebuilder.TestSuiteBuilder;
-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.List;
+import junit.framework.TestSuite;
-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 onParsingStart() {
- }
- public void onEntryCreated(ContactStruct contactStruct) {
- contacts.add(contactStruct);
- }
- public void onParsingEnd() {
- }
+public class VCardTests extends TestSuite {
+ public static TestSuite suite() {
+ TestSuiteBuilder suiteBuilder = new TestSuiteBuilder(AndroidTests.class);
+ suiteBuilder.includeAllPackagesUnderHere();
+ return suiteBuilder.build();
}
- /*
- 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, ArrayList<PropertyNode>> mPropertyNodeMap;
- public PropertyNodesVerifier(PropertyNode... nodes) {
- mPropertyNodeMap = new HashMap<String, ArrayList<PropertyNode>>();
- for (PropertyNode propertyNode : nodes) {
- String propName = propertyNode.propName;
- ArrayList<PropertyNode> expectedNodes =
- mPropertyNodeMap.get(propName);
- if (expectedNodes == null) {
- expectedNodes = new ArrayList<PropertyNode>();
- mPropertyNodeMap.put(propName, expectedNodes);
- }
- expectedNodes.add(propertyNode);
- }
- }
-
- public void verify(VNode vnode) {
- for (PropertyNode propertyNode : vnode.propList) {
- String propName = propertyNode.propName;
- ArrayList<PropertyNode> nodes = mPropertyNodeMap.get(propName);
- if (nodes == null) {
- fail("Unexpected propName \"" + propName + "\" exists.");
- }
- boolean successful = false;
- int size = nodes.size();
- for (int i = 0; i < size; i++) {
- PropertyNode expectedNode = nodes.get(i);
- if (expectedNode.propName.equals(propName)) {
- if (expectedNode.equals(propertyNode)) {
- successful = true;
- nodes.remove(i);
- if (nodes.size() == 0) {
- mPropertyNodeMap.remove(propName);
- }
- break;
- } else {
- fail("Property \"" + propName + "\" has wrong value.\n"
- + "expected: " + expectedNode.toString()
- + "\n actual: " + propertyNode.toString());
- }
- }
- }
- if (!successful) {
- fail("Unexpected property \"" + propName + "\" exists.");
- }
- }
- if (mPropertyNodeMap.size() != 0) {
- ArrayList<String> expectedProps = new ArrayList<String>();
- for (ArrayList<PropertyNode> nodes : mPropertyNodeMap.values()) {
- for (PropertyNode node : nodes) {
- expectedProps.add(node.propName);
- }
- }
- fail("expected props " + Arrays.toString(expectedProps.toArray()) +
- " was not found");
- }
- }
- }
-
- /*
- 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, 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(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();
- VNodeBuilder builder = new VNodeBuilder();
- InputStream is = getContext().getResources().openRawResource(R.raw.v21_backslash);
- assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
- is.close();
- assertEquals(1, builder.vNodeList.size());
- PropertyNodesVerifier verifier = new PropertyNodesVerifier(
- new PropertyNode("VERSION", "2.1",
- null, null, null, null, null),
- new PropertyNode("N", ";A;B\\;C\\;;D;:E;\\\\;",
- Arrays.asList("", "A;B\\", "C\\;", "D", ":E", "\\\\", ""),
- null, null, null, null),
- new PropertyNode("FN", "A;B\\C\\;D:E\\\\",
- null, null, null, null, null));
- verifier.verify(builder.vNodeList.get(0));
- }
-
- public void testV21ComplicatedCase() throws IOException, VCardException {
- VCardParser_V21 parser = new VCardParser_V21();
- VNodeBuilder builder = new VNodeBuilder();
- InputStream is = getContext().getResources().openRawResource(R.raw.v21_complicated);
- assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
- is.close();
- assertEquals(1, builder.vNodeList.size());
- ContentValues contentValuesForQP = new ContentValues();
- contentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
- ContentValues contentValuesForPhoto = new ContentValues();
- contentValuesForPhoto.put("ENCODING", "BASE64");
- // Push data into int array at first since values like 0x80 are
- // interpreted as int by the compiler and casting all of them is
- // cumbersome...
- int[] photoIntArray = {
- 0xff, 0xd8, 0xff, 0xe1, 0x0a, 0x0f, 0x45, 0x78, 0x69, 0x66, 0x00,
- 0x00, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d,
- 0x01, 0x0e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
- 0xaa, 0x01, 0x0f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
- 0x00, 0xba, 0x01, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00,
- 0x00, 0x00, 0xc2, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x01, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x00, 0x00, 0xd0, 0x01, 0x28, 0x00, 0x03, 0x00,
- 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x31, 0x00, 0x02,
- 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xd8, 0x01, 0x32, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xe6, 0x02, 0x13,
- 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x82,
- 0x98, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xfa,
- 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
- 0x84, 0xc4, 0xa5, 0x00, 0x07, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00,
- 0x01, 0x08, 0x00, 0x00, 0x04, 0x1e, 0x32, 0x30, 0x30, 0x38, 0x31,
- 0x30, 0x32, 0x39, 0x31, 0x33, 0x35, 0x35, 0x33, 0x31, 0x00, 0x00,
- 0x44, 0x6f, 0x43, 0x6f, 0x4d, 0x6f, 0x00, 0x00, 0x44, 0x39, 0x30,
- 0x35, 0x69, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x44, 0x39, 0x30,
- 0x35, 0x69, 0x20, 0x56, 0x65, 0x72, 0x31, 0x2e, 0x30, 0x30, 0x00,
- 0x32, 0x30, 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20,
- 0x31, 0x33, 0x3a, 0x35, 0x35, 0x3a, 0x34, 0x37, 0x00, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x00, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x49, 0x4d, 0x00, 0x30, 0x33,
- 0x30, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x14, 0x00,
- 0x14, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
- 0x00, 0x34, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
- 0x00, 0x00, 0x00, 0x01, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x11, 0x09, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x0f, 0x0b, 0x00,
- 0x00, 0x27, 0x10, 0x00, 0x00, 0x05, 0x97, 0x00, 0x00, 0x27, 0x10,
- 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x1c,
- 0x01, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x02, 0x5e, 0x00, 0x00,
- 0x27, 0x10, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x27, 0x10, 0x00,
- 0x00, 0x03, 0xcb, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x1b, 0xe5,
- 0x00, 0x00, 0x27, 0x10, 0x00, 0x28, 0x82, 0x9a, 0x00, 0x05, 0x00,
- 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x6a, 0x82, 0x9d, 0x00, 0x05,
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x72, 0x88, 0x22, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x90, 0x00,
- 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30, 0x32, 0x32, 0x30, 0x90,
- 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x03, 0x7a,
- 0x90, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x03,
- 0x8e, 0x91, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x01, 0x02,
- 0x03, 0x00, 0x91, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00,
- 0x00, 0x03, 0xa2, 0x92, 0x01, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x03, 0xaa, 0x92, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x03, 0xb2, 0x92, 0x04, 0x00, 0x0a, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x00, 0x03, 0xba, 0x92, 0x05, 0x00, 0x05, 0x00,
- 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xc2, 0x92, 0x07, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x92, 0x08, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x92, 0x09,
- 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x92,
- 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xca,
- 0x92, 0x7c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x92, 0x86, 0x00, 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00,
- 0x03, 0xd2, 0xa0, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30,
- 0x31, 0x30, 0x30, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x60, 0x00, 0x00, 0xa0, 0x03, 0x00, 0x03, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x48, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x04, 0x00,
- 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0xa2, 0x0e, 0x00, 0x05,
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xe8, 0xa2, 0x0f, 0x00,
- 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xf0, 0xa2, 0x10,
- 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0xa2,
- 0x17, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00,
- 0xa3, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00,
- 0x00, 0xa3, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00,
- 0x00, 0x00, 0xa4, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0xa4, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x00, 0xa4, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x04, 0x00, 0x05, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x00, 0x03, 0xf8, 0xa4, 0x05, 0x00, 0x03, 0x00,
- 0x00, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x00, 0xa4, 0x06, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x07, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x08,
- 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4,
- 0x09, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0xa4, 0x0a, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0xa4, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00,
- 0x00, 0x27, 0x10, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x00, 0x64,
- 0x32, 0x30, 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20,
- 0x31, 0x33, 0x3a, 0x35, 0x35, 0x3a, 0x33, 0x31, 0x00, 0x32, 0x30,
- 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20, 0x31, 0x33,
- 0x3a, 0x35, 0x35, 0x3a, 0x34, 0x37, 0x00, 0x00, 0x00, 0x29, 0x88,
- 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0xb2, 0x00, 0x00, 0x00,
- 0x64, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x25, 0x00,
- 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0e, 0x92, 0x00, 0x00, 0x03, 0xe8,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x30, 0x30,
- 0x38, 0x31, 0x30, 0x32, 0x39, 0x31, 0x33, 0x35, 0x35, 0x33, 0x31,
- 0x00, 0x00, 0x20, 0x2a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x2a,
- 0xe2, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x04, 0x52, 0x39, 0x38, 0x00, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00,
- 0x00, 0x04, 0x30, 0x31, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x06, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06,
- 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00,
- 0x00, 0x04, 0x6c, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x04, 0x74, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x00, 0x04, 0x7c, 0x02, 0x02, 0x00, 0x04, 0x00,
- 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x8b, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x48, 0x00, 0x00, 0x00, 0x01, 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84,
- 0x00, 0x20, 0x16, 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, 0x1a, 0x1c,
- 0x24, 0x22, 0x20, 0x26, 0x30, 0x50, 0x34, 0x30, 0x2c, 0x2c, 0x30,
- 0x62, 0x46, 0x4a, 0x3a, 0x50, 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66,
- 0x70, 0x6e, 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, 0xae, 0x8a, 0x6e,
- 0x70, 0xa0, 0xda, 0xa2, 0xae, 0xbe, 0xc4, 0xce, 0xd0, 0xce, 0x7c,
- 0x9a, 0xe2, 0xf2, 0xe0, 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, 0x01,
- 0x22, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34, 0x34, 0x5e, 0xc6,
- 0x84, 0x70, 0x84, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xff, 0xc0,
- 0x00, 0x11, 0x08, 0x00, 0x78, 0x00, 0xa0, 0x03, 0x01, 0x21, 0x00,
- 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x01, 0xa2, 0x00,
- 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
- 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03,
- 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01,
- 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31,
- 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81,
- 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
- 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19,
- 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37,
- 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
- 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65,
- 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
- 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92,
- 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4,
- 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
- 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
- 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
- 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1,
- 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, 0x00,
- 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
- 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04,
- 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77,
- 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12,
- 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14,
- 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15,
- 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17,
- 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37,
- 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
- 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65,
- 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
- 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
- 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3,
- 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5,
- 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
- 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
- 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2,
- 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00,
- 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00,
- 0x14, 0x54, 0xaa, 0x2a, 0x46, 0x48, 0xa2, 0xa4, 0x55, 0xa6, 0x04,
- 0x8a, 0x29, 0xe0, 0x53, 0x10, 0xe0, 0x29, 0xc0, 0x50, 0x03, 0xb1,
- 0x46, 0x29, 0x80, 0x84, 0x52, 0x11, 0x40, 0x0d, 0x22, 0x9a, 0x45,
- 0x20, 0x23, 0x61, 0x51, 0x30, 0xa0, 0x08, 0xc8, 0xa8, 0xd8, 0x52,
- 0x02, 0x26, 0x15, 0x0b, 0x0a, 0x00, 0xb4, 0xa2, 0xa5, 0x5a, 0x00,
- 0x91, 0x45, 0x4a, 0xa2, 0x81, 0x92, 0x01, 0x4e, 0x02, 0x98, 0x87,
- 0x0a, 0x70, 0xa0, 0x07, 0x62, 0x8c, 0x50, 0x21, 0x0d, 0x25, 0x00,
- 0x34, 0x8a, 0x61, 0x14, 0x0c, 0x63, 0x0a, 0x89, 0x85, 0x00, 0x46,
- 0xd5, 0x1b, 0x52, 0x02, 0x16, 0xa8, 0x98, 0x50, 0x05, 0x94, 0xa9,
- 0x16, 0x80, 0x25, 0x5a, 0x95, 0x68, 0x18, 0xf1, 0x4f, 0x14, 0xc4,
- 0x3b, 0xb5, 0x22, 0xb6, 0x38, 0x34, 0x00, 0xe3, 0x22, 0x8e, 0xf4,
- 0x79, 0x8a, 0x7b, 0xd1, 0x71, 0x03, 0x30, 0xc7, 0x14, 0x83, 0xa5,
- 0x00, 0x06, 0x98, 0x68, 0x01, 0x8d, 0x51, 0x35, 0x03, 0x22, 0x6a,
- 0x8d, 0xa9, 0x01, 0x13, 0x54, 0x4d, 0x40, 0x13, 0xa5, 0x4a, 0x28,
- 0x02, 0x45, 0x35, 0x2a, 0x9a, 0x00, 0x78, 0x34, 0xf0, 0x69, 0x80,
- 0x34, 0x81, 0x45, 0x40, 0xce, 0x58, 0xe6, 0xa2, 0x4c, 0x06, 0xe4,
- 0xfa, 0xd1, 0x93, 0x50, 0x21, 0xca, 0xe4, 0x55, 0x84, 0x90, 0x30,
- 0xab, 0x8b, 0x18, 0xa6, 0x9a, 0x6a, 0xc4, 0x31, 0xaa, 0x26, 0xa0,
- 0x64, 0x4d, 0x51, 0xb5, 0x20, 0x23, 0x6a, 0x89, 0xa8, 0x02, 0x44,
- 0x35, 0x2a, 0x9a, 0x00, 0x95, 0x4d, 0x48, 0xa6, 0x80, 0x24, 0x53,
- 0x4e, 0xce, 0x05, 0x30, 0x2b, 0x3b, 0xee, 0x6a, 0x91, 0x5d, 0x76,
- 0x63, 0xbd, 0x65, 0x7d, 0x40, 0x66, 0x68, 0xa9, 0x02, 0x45, 0x2b,
- 0xb3, 0x9e, 0xb4, 0xc5, 0x6d, 0xad, 0x9a, 0xa0, 0x2c, 0x06, 0xc8,
- 0xcd, 0x04, 0xd6, 0xa2, 0x23, 0x63, 0x51, 0xb1, 0xa0, 0x64, 0x4d,
- 0x51, 0x93, 0x48, 0x08, 0xda, 0xa2, 0x6a, 0x00, 0x72, 0x1a, 0x99,
- 0x4d, 0x00, 0x48, 0xa6, 0xa4, 0x53, 0x4c, 0x07, 0x86, 0x03, 0xbd,
- 0x2b, 0x9c, 0xa7, 0x14, 0x98, 0x10, 0x85, 0x34, 0xe0, 0xa6, 0xb3,
- 0xb0, 0x0b, 0xb5, 0xa8, 0x0a, 0xd4, 0x58, 0x42, 0xed, 0x3e, 0x94,
- 0xd2, 0xa6, 0x8b, 0x01, 0x34, 0x44, 0xed, 0xe6, 0x9c, 0x4d, 0x6a,
- 0x80, 0x8d, 0x8d, 0x46, 0xc6, 0x80, 0x23, 0x63, 0x51, 0x9a, 0x06,
- 0x46, 0xd5, 0x13, 0x52, 0x01, 0x54, 0xd4, 0xaa, 0x68, 0x02, 0x40,
- 0x6a, 0x40, 0x78, 0xa0, 0x08, 0x59, 0xce, 0xee, 0xb5, 0x2a, 0x39,
- 0xd9, 0x59, 0xa7, 0xa8, 0x00, 0x73, 0xeb, 0x4e, 0x0e, 0x7d, 0x69,
- 0x5c, 0x05, 0xf3, 0x0f, 0xad, 0x1e, 0x61, 0xf5, 0xa7, 0x71, 0x0b,
- 0xe6, 0x35, 0x21, 0x90, 0xd3, 0xb8, 0x0e, 0x32, 0x10, 0x95, 0x10,
- 0x91, 0xb3, 0xd6, 0x9b, 0x60, 0x4b, 0x9c, 0x8a, 0x63, 0x1a, 0xb0,
- 0x18, 0x4d, 0x46, 0xc6, 0x80, 0x22, 0x6a, 0x61, 0xa4, 0x31, 0xaa,
- 0x6a, 0x55, 0x34, 0x01, 0x2a, 0x9a, 0x7e, 0x78, 0xa0, 0x08, 0x09,
- 0xf9, 0xaa, 0x58, 0xcf, 0xca, 0x6b, 0x3e, 0xa0, 0x00, 0xd3, 0x81,
- 0xa9, 0x01, 0x73, 0x46, 0x69, 0x80, 0xb9, 0xa4, 0xcd, 0x00, 0x2b,
- 0x1f, 0x92, 0xa3, 0x07, 0x9a, 0x6f, 0x70, 0x26, 0xcf, 0x14, 0xd2,
- 0x6b, 0x51, 0x0c, 0x63, 0x51, 0xb1, 0xa0, 0x08, 0xda, 0x98, 0x69,
- 0x0c, 0x8d, 0x4d, 0x4a, 0xa6, 0x80, 0x24, 0x53, 0x52, 0x03, 0xc5,
- 0x02, 0x21, 0x27, 0xe6, 0xa9, 0x23, 0x3f, 0x29, 0xac, 0xfa, 0x8c,
- 0x01, 0xe6, 0x9c, 0x0d, 0x48, 0x0a, 0x0d, 0x2e, 0x68, 0x01, 0x73,
- 0x49, 0x9a, 0x60, 0x2b, 0x1f, 0x92, 0x98, 0x3a, 0xd3, 0x7b, 0x81,
- 0x36, 0x78, 0xa6, 0x93, 0x5a, 0x88, 0x8c, 0x9a, 0x63, 0x1a, 0x00,
- 0x8c, 0xd3, 0x0d, 0x21, 0x91, 0x29, 0xa9, 0x14, 0xd0, 0x04, 0x8a,
- 0x69, 0xe0, 0xd3, 0x11, 0x1b, 0x1e, 0x6a, 0x48, 0xcf, 0xca, 0x6b,
- 0x3e, 0xa3, 0x10, 0x1a, 0x70, 0x35, 0x20, 0x38, 0x1a, 0x5c, 0xd2,
- 0x01, 0x73, 0x49, 0x9a, 0x60, 0x39, 0x8f, 0xca, 0x29, 0x8b, 0xf7,
- 0xaa, 0xba, 0x88, 0x96, 0x9a, 0x6b, 0x40, 0x18, 0xc6, 0xa3, 0x26,
- 0x80, 0x18, 0x69, 0xa6, 0x90, 0xc8, 0x14, 0xd4, 0x8a, 0x69, 0x80,
- 0xf0, 0x6a, 0x40, 0x68, 0x10, 0xbb, 0x41, 0xa7, 0xe3, 0x0b, 0xc5,
- 0x2b, 0x01, 0x10, 0xa7, 0x03, 0x59, 0x0c, 0x76, 0x69, 0x73, 0x40,
- 0x0b, 0x9a, 0x28, 0x11, 0x28, 0x19, 0x5e, 0x69, 0x02, 0x81, 0x5a,
- 0xd8, 0x00, 0xd3, 0x4d, 0x50, 0x0c, 0x6a, 0x8c, 0xd2, 0x01, 0xa6,
- 0x98, 0x69, 0x0c, 0xae, 0xa6, 0xa4, 0x06, 0x80, 0x1e, 0xa6, 0x9e,
- 0x0d, 0x31, 0x12, 0x03, 0x4f, 0x06, 0x80, 0x13, 0x60, 0x34, 0xd3,
- 0xc1, 0xa8, 0x92, 0x01, 0xf1, 0x8d, 0xdd, 0x69, 0xcc, 0xa1, 0x69,
- 0x5b, 0x4b, 0x80, 0x83, 0x93, 0x52, 0x04, 0x14, 0xe2, 0xae, 0x03,
- 0xa9, 0x0d, 0x68, 0x03, 0x4d, 0x34, 0xd0, 0x03, 0x0d, 0x30, 0xd2,
- 0x01, 0x86, 0x9a, 0x68, 0x19, 0x58, 0x1a, 0x78, 0xa4, 0x04, 0x8a,
- 0x69, 0xe0, 0xd3, 0x10, 0xe0, 0x69, 0xe0, 0xd0, 0x03, 0xc1, 0xa8,
- 0xdb, 0xad, 0x4c, 0x81, 0x12, 0x45, 0xd6, 0x9d, 0x25, 0x1d, 0x00,
- 0x6a, 0xf5, 0xa9, 0xe8, 0x80, 0x31, 0x29, 0x0d, 0x58, 0x08, 0x69,
- 0x86, 0x80, 0x1a, 0x69, 0x86, 0x90, 0x0c, 0x34, 0xd3, 0x48, 0x65,
- 0x51, 0x4f, 0x06, 0x98, 0x0f, 0x14, 0xf0, 0x68, 0x10, 0xf0, 0x69,
- 0xe0, 0xd0, 0x03, 0x81, 0xa5, 0x2b, 0x9a, 0x1a, 0xb8, 0x87, 0xa8,
- 0xdb, 0x4a, 0x46, 0x68, 0xb6, 0x80, 0x2a, 0xa8, 0x14, 0xea, 0x12,
- 0xb0, 0x05, 0x21, 0xa6, 0x02, 0x1a, 0x61, 0xa0, 0x06, 0x9a, 0x61,
- 0xa4, 0x31, 0x86, 0x9a, 0x69, 0x0c, 0xa8, 0x0d, 0x3c, 0x53, 0x01,
- 0xe2, 0x9e, 0x28, 0x10, 0xf1, 0x4e, 0x06, 0x98, 0x0f, 0x06, 0x9e,
- 0x0d, 0x02, 0x1c, 0x29, 0xc2, 0x80, 0x16, 0x96, 0x80, 0x0a, 0x4a,
- 0x00, 0x43, 0x4d, 0x34, 0x0c, 0x61, 0xa6, 0x1a, 0x40, 0x34, 0xd3,
- 0x4d, 0x21, 0x80, 0xff, 0xd9, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0a,
- 0x07, 0x07, 0x08, 0x07, 0x06, 0x0a, 0x08, 0x08, 0x08, 0x0b, 0x0a,
- 0x0a, 0x0b, 0x0e, 0x18, 0x10, 0x0e, 0x0d, 0x0d, 0x0e, 0x1d, 0x15,
- 0x16, 0x11, 0x18, 0x23, 0x1f, 0x25, 0x24, 0x22, 0x1f, 0x22, 0x21,
- 0x26, 0x2b, 0x37, 0x2f, 0x26, 0x29, 0x34, 0x29, 0x21, 0x22, 0x30,
- 0x41, 0x31, 0x34, 0x39, 0x3b, 0x3e, 0x3e, 0x3e, 0x25, 0x2e, 0x44,
- 0x49, 0x43, 0x3c, 0x48, 0x37, 0x3d, 0x3e, 0x3b, 0x01, 0x0a, 0x0b,
- 0x0b, 0x0e, 0x0d, 0x0e, 0x1c, 0x10, 0x10, 0x1c, 0x3b, 0x28, 0x22,
- 0x28, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
- 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
- 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
- 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
- 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xc0, 0x00, 0x11,
- 0x08, 0x00, 0x48, 0x00, 0x60, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11,
- 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01,
- 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02,
- 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01,
- 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
- 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1,
- 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33,
- 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
- 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
- 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54,
- 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67,
- 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
- 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94,
- 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
- 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8,
- 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
- 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
- 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3,
- 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, 0x00, 0x03, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
- 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03,
- 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01,
- 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51,
- 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
- 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72,
- 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19,
- 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39,
- 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54,
- 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67,
- 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
- 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
- 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
- 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
- 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
- 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2,
- 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4,
- 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03,
- 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0x9e, 0xd2,
- 0x2e, 0x07, 0x15, 0xaf, 0x6d, 0x08, 0xe2, 0xb3, 0x45, 0x1a, 0xf6,
- 0xd0, 0x00, 0x01, 0xc5, 0x68, 0x45, 0x17, 0x4a, 0xb4, 0x22, 0xe4,
- 0x70, 0x8c, 0x74, 0xa9, 0x3c, 0xa1, 0x8e, 0x95, 0x48, 0x96, 0x31,
- 0xe2, 0x18, 0xe9, 0x55, 0xa5, 0x8c, 0x7a, 0x50, 0x05, 0x0b, 0x88,
- 0x86, 0x0f, 0x15, 0x8f, 0x75, 0x1f, 0x26, 0x93, 0x19, 0x91, 0x77,
- 0x18, 0xc1, 0xac, 0x4b, 0xc8, 0xfa, 0xd6, 0x63, 0x37, 0x6d, 0x31,
- 0xb4, 0x73, 0x5b, 0x36, 0xa0, 0x1c, 0x50, 0x80, 0xd7, 0x83, 0xa0,
- 0xab, 0xd1, 0x62, 0xad, 0x09, 0x8f, 0x17, 0x29, 0x03, 0xb2, 0xcc,
- 0xe0, 0x77, 0x14, 0xa3, 0x56, 0xb3, 0x27, 0x1e, 0x67, 0xe9, 0x52,
- 0xea, 0xc6, 0x3a, 0x36, 0x48, 0xef, 0x3d, 0x27, 0x70, 0x22, 0x60,
- 0x47, 0x52, 0x69, 0xb2, 0xe2, 0xad, 0x3b, 0xea, 0x80, 0xa3, 0x38,
- 0xe0, 0xd6, 0x3d, 0xd8, 0x1c, 0xd0, 0xca, 0x46, 0x3d, 0xd0, 0x18,
- 0x35, 0x89, 0x78, 0xa3, 0x9a, 0xcd, 0x8c, 0xd2, 0xb3, 0x93, 0x2a,
- 0x2b, 0x66, 0xd5, 0xf1, 0x8a, 0x10, 0x1a, 0xd6, 0xf2, 0x03, 0x8a,
- 0x9e, 0xe6, 0xf4, 0x5a, 0xdb, 0xef, 0xfe, 0x23, 0xc0, 0xa7, 0x27,
- 0xcb, 0x16, 0xc4, 0xcc, 0xdd, 0xe2, 0x78, 0x9a, 0x69, 0x66, 0xcc,
- 0x99, 0xe1, 0x4d, 0x47, 0xba, 0xbc, 0xd9, 0x6a, 0xee, 0x26, 0x59,
- 0x59, 0x4d, 0xac, 0x69, 0x34, 0x52, 0xe5, 0x8f, 0x55, 0xad, 0x58,
- 0xae, 0x85, 0xc4, 0x22, 0x41, 0xdf, 0xad, 0x76, 0x61, 0xe5, 0x6f,
- 0x74, 0x45, 0x69, 0xdc, 0x00, 0x79, 0xac, 0x8b, 0xa6, 0xc9, 0x35,
- 0xd4, 0x34, 0x64, 0xdc, 0x37, 0x06, 0xb1, 0xae, 0x88, 0xc1, 0xac,
- 0xd8, 0xc9, 0x2c, 0xa6, 0xe0, 0x73, 0x5b, 0x36, 0xf3, 0x74, 0xe6,
- 0x84, 0x05, 0xe3, 0xa9, 0x47, 0x6a, 0x14, 0xb6, 0x49, 0x3d, 0x85,
- 0x3a, 0xee, 0xee, 0x2b, 0xa8, 0xe2, 0x6f, 0x30, 0x81, 0xe9, 0x8a,
- 0xca, 0xa4, 0xe2, 0xd3, 0x8b, 0x01, 0xb1, 0xf9, 0x04, 0x7f, 0xaf,
- 0x23, 0xf0, 0xa9, 0x54, 0x41, 0x9c, 0xfd, 0xa3, 0xf4, 0xae, 0x65,
- 0x18, 0xf7, 0x25, 0x8a, 0xe2, 0x02, 0x38, 0xb8, 0xfd, 0x2a, 0x7b,
- 0x5b, 0xa8, 0x6d, 0x6d, 0x5d, 0x9a, 0x5d, 0xcb, 0xbb, 0xd2, 0xb6,
- 0xa6, 0xa3, 0x19, 0x5e, 0xe2, 0x03, 0x7b, 0x1d, 0xc2, 0x17, 0x8d,
- 0xb8, 0xac, 0xfb, 0x89, 0x39, 0x35, 0xd6, 0x9a, 0x6a, 0xe8, 0x66,
- 0x55, 0xcb, 0xf5, 0xac, 0x7b, 0x96, 0xeb, 0x50, 0xc6, 0x88, 0x6d,
- 0x66, 0xe9, 0xcd, 0x6c, 0xdb, 0x4f, 0xd3, 0x9a, 0x00, 0x2f, 0xe6,
- 0xf9, 0xa3, 0xe7, 0xb5, 0x4a, 0x93, 0x7f, 0xa2, 0xc6, 0x73, 0xdc,
- 0xd7, 0x15, 0x55, 0xef, 0x48, 0x7d, 0x09, 0x52, 0x6e, 0x3a, 0xd4,
- 0xab, 0x2f, 0xbd, 0x61, 0x16, 0x0c, 0x73, 0x49, 0xc5, 0x24, 0x92,
- 0x7f, 0xa2, 0x63, 0xfd, 0xaa, 0xd6, 0x2f, 0x71, 0x0e, 0xb1, 0x93,
- 0xf7, 0x2d, 0xf5, 0xa4, 0x9e, 0x4e, 0xb5, 0xdd, 0x4b, 0xf8, 0x68,
- 0x4c, 0xcb, 0xb9, 0x93, 0xad, 0x65, 0xce, 0xd9, 0x26, 0xa9, 0x8d,
- 0x19, 0xf6, 0xf2, 0xf4, 0xe6, 0xb5, 0xad, 0xe7, 0xc6, 0x39, 0xa0,
- 0x18, 0xeb, 0xc9, 0x77, 0x6c, 0x35, 0x2a, 0x4b, 0xfe, 0x8a, 0x9c,
- 0xff, 0x00, 0x11, 0xae, 0x3a, 0x8b, 0xde, 0x61, 0xd0, 0x9e, 0x39,
- 0xb8, 0xeb, 0x53, 0xac, 0xb9, 0xae, 0x5b, 0x00, 0xf3, 0x27, 0x14,
- 0x92, 0xc9, 0xfe, 0x8a, 0x3f, 0xde, 0x35, 0xac, 0x3a, 0x88, 0x92,
- 0xcd, 0xb1, 0x6e, 0x7d, 0xcd, 0x32, 0x67, 0xeb, 0xcd, 0x7a, 0x14,
- 0xfe, 0x04, 0x26, 0x66, 0xce, 0xf9, 0x26, 0xb3, 0xe6, 0x6e, 0xb4,
- 0xd9, 0x48, 0xc8, 0x82, 0x4e, 0x07, 0x35, 0xa7, 0x6f, 0x2f, 0x02,
- 0x9a, 0x06, 0x5f, 0x8c, 0xa4, 0x83, 0x0e, 0x32, 0x2a, 0x69, 0xe3,
- 0xdd, 0x12, 0x08, 0x97, 0x85, 0xec, 0x2a, 0x2a, 0x42, 0xf1, 0x76,
- 0x26, 0xe4, 0x6a, 0x59, 0x0e, 0x18, 0x10, 0x6a, 0xd2, 0x89, 0x02,
- 0x6e, 0x2a, 0x71, 0xeb, 0x5c, 0x1c, 0x8c, 0xa6, 0x48, 0xbb, 0xdc,
- 0x61, 0x41, 0x35, 0x72, 0x28, 0x87, 0xd9, 0xf6, 0x4a, 0xb9, 0xe7,
- 0x38, 0xae, 0x8c, 0x3d, 0x36, 0xdd, 0xde, 0xc4, 0xb0, 0x21, 0x51,
- 0x76, 0xa8, 0xc0, 0xaa, 0x93, 0x31, 0xe6, 0xbb, 0x2d, 0x65, 0x61,
- 0x19, 0xd3, 0x1e, 0xb5, 0x46, 0x5a, 0x96, 0x5a, 0x30, 0xa0, 0x7e,
- 0x05, 0x69, 0x5b, 0xc9, 0xc6, 0x28, 0x40, 0xcd, 0x08, 0x64, 0x3c,
- 0x73, 0x57, 0xe1, 0x94, 0xf1, 0xcd, 0x5a, 0x21, 0x8c, 0xb9, 0x63,
- 0xe7, 0x67, 0x1d, 0xab, 0x40, 0xb1, 0xfb, 0x00, 0x1d, 0xf0, 0x2b,
- 0x99, 0x2d, 0x66, 0x3e, 0x88, 0x75, 0x81, 0x3f, 0x31, 0xf6, 0xab,
- 0x64, 0xd6, 0xb4, 0x17, 0xee, 0xd0, 0x9e, 0xe4, 0x32, 0x1a, 0xa7,
- 0x31, 0xad, 0x18, 0x14, 0x26, 0xef, 0x54, 0xa5, 0xa8, 0x65, 0xa3,
- 0x9c, 0x81, 0xfa, 0x56, 0x8c, 0x2d, 0xce, 0x68, 0x40, 0xcb, 0xf1,
- 0x37, 0xbd, 0x5e, 0x85, 0xea, 0xd1, 0x0c, 0xbb, 0x19, 0x56, 0x23,
- 0x20, 0x1f, 0xad, 0x5c, 0x42, 0x08, 0x03, 0xb5, 0x55, 0x91, 0x04,
- 0xc9, 0x80, 0x38, 0x00, 0x0a, 0x71, 0x34, 0x6c, 0x32, 0x27, 0xe9,
- 0x55, 0x25, 0x15, 0x2c, 0x68, 0xa3, 0x30, 0xeb, 0x54, 0xa5, 0x15,
- 0x0c, 0xd1, 0x00, 0xff, 0xd9};
- int length = photoIntArray.length;
- byte[] photoByteArray = new byte[length];
- for (int i = 0; i < length; i++) {
- photoByteArray[i] = (byte)photoIntArray[i];
- }
- PropertyNodesVerifier verifier = new PropertyNodesVerifier(
- new PropertyNode("VERSION", "2.1",
- null, null, null, null, null),
- new PropertyNode("N", "Gump;Forrest;Hoge;Pos;Tao",
- Arrays.asList("Gump", "Forrest",
- "Hoge", "Pos", "Tao"),
- null, null, null, null),
- new PropertyNode("FN", "Joe Due",
- null, null, null, null, null),
- new PropertyNode("ORG",
- "Gump Shrimp Co.;Sales Dept.;Manager;Fish keeper",
- Arrays.asList("Gump Shrimp Co.",
- "Sales Dept.;Manager",
- "Fish keeper"),
- null, null, null, null),
- new PropertyNode("ROLE", "Fish Cake Keeper!",
- null, null, null, null, null),
- new PropertyNode("TITLE", "Shrimp Man",
- null, null, null, null, null),
- new PropertyNode("X-CLASS", "PUBLIC",
- null, null, null, null, null),
- new PropertyNode("TEL", "(111) 555-1212",
- null, null, null,
- new HashSet<String>(Arrays.asList("WORK", "VOICE")), null),
- new PropertyNode("TEL", "(404) 555-1212",
- null, null, null,
- new HashSet<String>(Arrays.asList("HOME", "VOICE")), null),
- new PropertyNode("TEL", "0311111111",
- null, null, null,
- new HashSet<String>(Arrays.asList("CELL")), null),
- new PropertyNode("TEL", "0322222222",
- null, null, null,
- new HashSet<String>(Arrays.asList("VIDEO")), null),
- new PropertyNode("TEL", "0333333333",
- null, null, null,
- new HashSet<String>(Arrays.asList("VOICE")), null),
- new PropertyNode("ADR",
- ";;100 Waters Edge;Baytown;LA;30314;United States of America",
- Arrays.asList("", "", "100 Waters Edge", "Baytown",
- "LA", "30314", "United States of America"),
- null, null,
- new HashSet<String>(Arrays.asList("WORK")), null),
- new PropertyNode("LABEL",
- "100 Waters Edge\r\nBaytown, LA 30314\r\nUnited States of America",
- null, null, contentValuesForQP,
- new HashSet<String>(Arrays.asList("WORK")), null),
- new PropertyNode("ADR",
- ";;42 Plantation St.;Baytown;LA;30314;United States of America",
- Arrays.asList("", "", "42 Plantation St.", "Baytown",
- "LA", "30314", "United States of America"), null, null,
- new HashSet<String>(Arrays.asList("HOME")), null),
- new PropertyNode("LABEL",
- "42 Plantation St.\r\nBaytown, LA 30314\r\nUnited States of America",
- null, null, contentValuesForQP,
- new HashSet<String>(Arrays.asList("HOME")), null),
- new PropertyNode("EMAIL", "forrestgump@walladalla.com",
- null, null, null,
- new HashSet<String>(Arrays.asList("PREF", "INTERNET")), null),
- new PropertyNode("EMAIL", "cell@example.com",
- null, null, null,
- new HashSet<String>(Arrays.asList("CELL")), null),
- new PropertyNode("NOTE", "The following note is the example from RFC 2045.",
- null, null, null, null, null),
- new PropertyNode("NOTE",
- "Now's the time for all folk to come to the aid of their country.",
- null, null, contentValuesForQP, null, null),
- new PropertyNode("PHOTO", null,
- null, photoByteArray, contentValuesForPhoto,
- new HashSet<String>(Arrays.asList("JPEG")), null),
- new PropertyNode("X-ATTRIBUTE", "Some String",
- null, null, null, null, null),
- new PropertyNode("BDAY", "19800101",
- null, null, null, null, null),
- new PropertyNode("GEO", "35.6563854,139.6994233",
- null, null, null, null, null),
- new PropertyNode("URL", "http://www.example.com/",
- null, null, null, null, null),
- new PropertyNode("REV", "20080424T195243Z",
- null, null, null, null, null));
- verifier.verify(builder.vNodeList.get(0));
- }
-
- public void testV21Japanese1() throws IOException, VCardException {
- VCardParser_V21 parser = new VCardParser_V21();
- 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();
- assertEquals(1, builder.vNodeList.size());
- ContentValues contentValuesForShiftJis = new ContentValues();
- contentValuesForShiftJis.put("CHARSET", "SHIFT_JIS");
- ContentValues contentValuesForQP = new ContentValues();
- contentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
- contentValuesForQP.put("CHARSET", "SHIFT_JIS");
- // Though Japanese careers append ";;;;" at the end of the value of "SOUND",
- // vCard 2.1/3.0 specification does not allow multiple values.
- // Do not need to handle it as multiple values.
- PropertyNodesVerifier verifier = new PropertyNodesVerifier(
- new PropertyNode("VERSION", "2.1",
- null, null, null, null, null),
- new PropertyNode("N", "\u5B89\u85E4\u30ED\u30A4\u30C9;;;;",
- Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9", "", "", "", ""),
- null, contentValuesForShiftJis, null, null),
- new PropertyNode("SOUND",
- "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E;;;;",
- null, null, contentValuesForShiftJis,
- new HashSet<String>(Arrays.asList("X-IRMC-N")), null),
- new PropertyNode("TEL", "0300000000",
- null, null, null,
- new HashSet<String>(Arrays.asList("VOICE", "PREF")), null));
- verifier.verify(builder.vNodeList.get(0));
- }
-
- public void testV21Japanese2() throws IOException, VCardException {
- VCardParser_V21 parser = new VCardParser_V21();
- 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();
- assertEquals(1, builder.vNodeList.size());
- ContentValues contentValuesForShiftJis = new ContentValues();
- contentValuesForShiftJis.put("CHARSET", "SHIFT_JIS");
- ContentValues contentValuesForQP = new ContentValues();
- contentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
- contentValuesForQP.put("CHARSET", "SHIFT_JIS");
- PropertyNodesVerifier verifier = new PropertyNodesVerifier(
- new PropertyNode("VERSION", "2.1",
- null, null, null, null, null),
- new PropertyNode("N", "\u5B89\u85E4;\u30ED\u30A4\u30C9\u0031;;;",
- Arrays.asList("\u5B89\u85E4", "\u30ED\u30A4\u30C9\u0031",
- "", "", ""),
- null, contentValuesForShiftJis, null, null),
- new PropertyNode("FN",
- "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9\u0020\u0031",
- null, null, contentValuesForShiftJis, null, null),
- new PropertyNode("SOUND",
- ("\uFF71\uFF9D\uFF84\uFF9E\uFF73" +
- ";\uFF9B\uFF72\uFF84\uFF9E\u0031;;;"),
- null, null, contentValuesForShiftJis,
- new HashSet<String>(Arrays.asList("X-IRMC-N")), null),
- new PropertyNode("ADR",
- (";\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
- "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
- "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC\u0036" +
- "\u968E;;;;150-8512;"),
- Arrays.asList("",
- "\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
- "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
- "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" +
- "\u0036\u968E", "", "", "", "150-8512", ""),
- null, contentValuesForQP,
- new HashSet<String>(Arrays.asList("HOME")), null),
- new PropertyNode("NOTE", "\u30E1\u30E2",
- null, null, contentValuesForQP, null, null));
- verifier.verify(builder.vNodeList.get(0));
- }
-
- public void testV21MultipleEntryCase() throws IOException, VCardException {
- VCardParser_V21 parser = new VCardParser_V21();
- 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();
- assertEquals(3, builder.vNodeList.size());
- ContentValues contentValuesForShiftJis = new ContentValues();
- contentValuesForShiftJis.put("CHARSET", "SHIFT_JIS");
- PropertyNodesVerifier verifier = new PropertyNodesVerifier(
- new PropertyNode("VERSION", "2.1",
- null, null, null, null, null),
- new PropertyNode("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033;;;;",
- Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0033", "", "", "", ""),
- null, contentValuesForShiftJis, null, null),
- new PropertyNode("SOUND",
- "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0033;;;;",
- null, null, contentValuesForShiftJis,
- new HashSet<String>(Arrays.asList("X-IRMC-N")), null),
- new PropertyNode("TEL", "9",
- null, null, null,
- new HashSet<String>(Arrays.asList("X-NEC-SECRET")), null),
- new PropertyNode("TEL", "10",
- null, null, null,
- new HashSet<String>(Arrays.asList("X-NEC-HOTEL")), null),
- new PropertyNode("TEL", "11",
- null, null, null,
- new HashSet<String>(Arrays.asList("X-NEC-SCHOOL")), null),
- new PropertyNode("TEL", "12",
- null, null, null,
- new HashSet<String>(Arrays.asList("FAX", "HOME")), null));
- verifier.verify(builder.vNodeList.get(0));
-
- verifier = new PropertyNodesVerifier(
- new PropertyNode("VERSION", "2.1",
- null, null, null, null, null),
- new PropertyNode("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034;;;;",
- Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0034", "", "", "", ""),
- null, contentValuesForShiftJis, null, null),
- new PropertyNode("SOUND",
- "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0034;;;;",
- null, null, contentValuesForShiftJis,
- new HashSet<String>(Arrays.asList("X-IRMC-N")), null),
- new PropertyNode("TEL", "13",
- null, null, null,
- new HashSet<String>(Arrays.asList("MODEM")), null),
- new PropertyNode("TEL", "14",
- null, null, null,
- new HashSet<String>(Arrays.asList("PAGER")), null),
- new PropertyNode("TEL", "15",
- null, null, null,
- new HashSet<String>(Arrays.asList("X-NEC-FAMILY")), null),
- new PropertyNode("TEL", "16",
- null, null, null,
- new HashSet<String>(Arrays.asList("X-NEC-GIRL")), null));
- verifier.verify(builder.vNodeList.get(1));
- verifier = new PropertyNodesVerifier(
- new PropertyNode("VERSION", "2.1",
- null, null, null, null, null),
- new PropertyNode("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035;;;;",
- Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0035", "", "", "", ""),
- null, contentValuesForShiftJis, null, null),
- new PropertyNode("SOUND",
- "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0035;;;;",
- null, null, contentValuesForShiftJis,
- new HashSet<String>(Arrays.asList("X-IRMC-N")), null),
- new PropertyNode("TEL", "17",
- null, null, null,
- new HashSet<String>(Arrays.asList("X-NEC-BOY")), null),
- new PropertyNode("TEL", "18",
- null, null, null,
- new HashSet<String>(Arrays.asList("X-NEC-FRIEND")), null),
- new PropertyNode("TEL", "19",
- null, null, null,
- new HashSet<String>(Arrays.asList("X-NEC-PHS")), null),
- new PropertyNode("TEL", "20",
- null, null, null,
- new HashSet<String>(Arrays.asList("X-NEC-RESTAURANT")), null));
- verifier.verify(builder.vNodeList.get(2));
- }
-
- public void testV30SimpleCase() throws IOException, VCardException {
- VCardParser_V21 parser = new VCardParser_V30();
- VNodeBuilder builder = new VNodeBuilder();
- InputStream is = getContext().getResources().openRawResource(R.raw.v30_simple);
- assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
- is.close();
- assertEquals(1, builder.vNodeList.size());
- PropertyNodesVerifier verifier = new PropertyNodesVerifier(
- new PropertyNode("VERSION", "3.0",
- null, null, null, null, null),
- new PropertyNode("FN", "And Roid",
- null, null, null, null, null),
- new PropertyNode("N", "And;Roid;;;",
- Arrays.asList("And", "Roid", "", "", ""),
- null, null, null, null),
- new PropertyNode("ORG", "Open;Handset; Alliance",
- Arrays.asList("Open", "Handset", " Alliance"),
- null, null, null, null),
- new PropertyNode("SORT-STRING", "android", null, null, null, null, null),
- new PropertyNode("TEL", "0300000000",
- null, null, null,
- new HashSet<String>(Arrays.asList("PREF", "VOICE")), null),
- new PropertyNode("CLASS", "PUBLIC", null, null, null, null, null),
- new PropertyNode("X-GNO", "0", null, null, null, null, null),
- new PropertyNode("X-GN", "group0", null, null, null, null, null),
- new PropertyNode("X-REDUCTION", "0",
- null, null, null, null, null),
- new PropertyNode("REV", "20081031T065854Z",
- null, null, null, null, null));
- verifier.verify(builder.vNodeList.get(0));
- }
-}
+}
\ No newline at end of file
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java
index 3eb827b..7587320 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java
@@ -18,7 +18,7 @@
import java.util.ArrayList;
/**
- * @hide old class. Just for testing
+ * Previously used in main vCard handling code but now exists only for testing.
*/
public class VNode {
public String VName;
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java
index 6d69223..ce4de03 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java
@@ -36,7 +36,8 @@
* 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
+ *
+ * Previously used in main vCard handling code but now exists only for testing.
*/
public class VNodeBuilder implements VCardBuilder {
static private String LOG_TAG = "VDATABuilder";
@@ -189,6 +190,7 @@
private String handleOneValue(String value, String targetCharset, String encoding) {
if (encoding != null) {
+ encoding = encoding.toUpperCase();
if (encoding.equals("BASE64") || encoding.equals("B")) {
// Assume BASE64 is used only when the number of values is 1.
mCurrentPropNode.propValue_bytes =
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index 234e5b2..1ac13f2 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -80,11 +80,13 @@
void setValues(bool val) { mValues = val; }
int getCompressionMethod(void) const { return mCompressionMethod; }
void setCompressionMethod(int val) { mCompressionMethod = val; }
+ bool getJunkPath(void) const { return mJunkPath; }
+ void setJunkPath(bool val) { mJunkPath = val; }
const char* getOutputAPKFile() const { return mOutputAPKFile; }
void setOutputAPKFile(const char* val) { mOutputAPKFile = val; }
- /*
- * Input options.
+ /*
+ * Input options.
*/
const char* getAssetSourceDir() const { return mAssetSourceDir; }
void setAssetSourceDir(const char* dir) { mAssetSourceDir = dir; }
@@ -119,7 +121,7 @@
void setVersionCode(const char* val) { mVersionCode = val; }
const char* getVersionName() const { return mVersionName; }
void setVersionName(const char* val) { mVersionName = val; }
-
+
/*
* Set and get the file specification.
*
@@ -161,6 +163,7 @@
bool mPseudolocalize;
bool mValues;
int mCompressionMethod;
+ bool mJunkPath;
const char* mOutputAPKFile;
const char* mAssetSourceDir;
const char* mProguardFile;
@@ -173,13 +176,13 @@
android::Vector<const char*> mJarFiles;
android::Vector<const char*> mNoCompressExtensions;
android::Vector<const char*> mResourceSourceDirs;
-
+
const char* mMinSdkVersion;
const char* mTargetSdkVersion;
const char* mMaxSdkVersion;
const char* mVersionCode;
const char* mVersionName;
-
+
/* file specification */
int mArgc;
char* const* mArgv;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 4742341..1a536d6 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -848,7 +848,7 @@
printf("uses-feature:'android.hardware.camera'\n");
printf("uses-feature:'android.hardware.camera.autofocus'\n");
}
-
+
if (hasMainActivity) {
printf("main\n");
}
@@ -997,8 +997,15 @@
printf(" '%s'... (from gzip)\n", fileName);
result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
} else {
- printf(" '%s'...\n", fileName);
- result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
+ if (bundle->getJunkPath()) {
+ String8 storageName = String8(fileName).getPathLeaf();
+ printf(" '%s' as '%s'...\n", fileName, storageName.string());
+ result = zip->add(fileName, storageName.string(),
+ bundle->getCompressionMethod(), NULL);
+ } else {
+ printf(" '%s'...\n", fileName);
+ result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
+ }
}
if (result != NO_ERROR) {
fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index e61010c..98286c0 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -99,6 +99,7 @@
" -f force overwrite of existing files\n"
" -g specify a pixel tolerance to force images to grayscale, default 0\n"
" -j specify a jar or zip file containing classes to include\n"
+ " -k junk path of file(s) added\n"
" -m make package directories under location specified by -J\n"
#if 0
" -p pseudolocalize the default configuration\n"
@@ -236,6 +237,9 @@
bundle.setGrayscaleTolerance(tolerance);
printf("%s: Images with deviation <= %d will be forced to grayscale.\n", prog, tolerance);
break;
+ case 'k':
+ bundle.setJunkPath(true);
+ break;
case 'm':
bundle.setMakePackageDirs(true);
break;
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 2c0d0f1..b7d3a6e 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -90,6 +90,7 @@
*/
private static final int EVENT_DRIVER_STATE_CHANGED = 12;
private static final int EVENT_PASSWORD_KEY_MAY_BE_INCORRECT = 13;
+ private static final int EVENT_MAYBE_START_SCAN_POST_DISCONNECT = 14;
/**
* Interval in milliseconds between polling for connection
@@ -126,6 +127,14 @@
private static final int RECONNECT_DELAY_MSECS = 2000;
/**
+ * When the supplicant disconnects from an AP it sometimes forgets
+ * to restart scanning. Wait this delay before asking it to start
+ * scanning (in case it forgot). 15 sec is the standard delay between
+ * scans.
+ */
+ private static final int KICKSTART_SCANNING_DELAY_MSECS = 15000;
+
+ /**
* The maximum number of times we will retry a connection to an access point
* for which we have failed in acquiring an IP address from DHCP. A value of
* N means that we will make N+1 connection attempts in all.
@@ -149,6 +158,14 @@
private int mNumSupplicantLoopIterations = 0;
/**
+ * The current number of supplicant state changes. This is used to determine
+ * if we've received any new info since we found out it was DISCONNECTED or
+ * INACTIVE. If we haven't for X ms, we then request a scan - it should have
+ * done that automatically, but sometimes some firmware does not.
+ */
+ private int mNumSupplicantStateChanges = 0;
+
+ /**
* True if we received an event that that a password-key may be incorrect.
* If the next incoming supplicant state change event is DISCONNECT,
* broadcast a message that we have a possible password error and disable
@@ -831,7 +848,16 @@
}
break;
+ case EVENT_MAYBE_START_SCAN_POST_DISCONNECT:
+ // Only do this if we haven't gotten a new supplicant status since the timer
+ // started
+ if (mNumSupplicantStateChanges == msg.arg1) {
+ WifiNative.scanCommand(false); // do a passive scan
+ }
+ break;
+
case EVENT_SUPPLICANT_STATE_CHANGED:
+ mNumSupplicantStateChanges++;
SupplicantStateChangeResult supplicantStateResult =
(SupplicantStateChangeResult) msg.obj;
SupplicantState newState = supplicantStateResult.state;
@@ -850,6 +876,17 @@
int networkId = supplicantStateResult.networkId;
/*
+ * If we get disconnect or inactive we need to start our
+ * watchdog timer to start a scan
+ */
+ if (newState == SupplicantState.DISCONNECTED ||
+ newState == SupplicantState.INACTIVE) {
+ sendMessageDelayed(obtainMessage(EVENT_MAYBE_START_SCAN_POST_DISCONNECT,
+ mNumSupplicantStateChanges, 0), KICKSTART_SCANNING_DELAY_MSECS);
+ }
+
+
+ /*
* Did we get to DISCONNECTED state due to an
* authentication (password) failure?
*/