- IM app was separated as three separated parts before: IM.apk, com.android.im.plugin.jar and the plugin APK. With this structure, it's hard to maintain the dependency and impossible to update through Market.
- The plug-in structure is kept with some changes: instead of with aidl files in a shared library, they are changed as normal java interfaces and will be built in the apk now.
- The branding resources have to be put in IM/res now since there is only one APK after the refactoring.
- The landing page has been moved to IM APK from ImProvider since GTalk has been separated from IMPS client.
src/com/android/im/ISubscriptionListener.aidl \
src/com/android/im/IConnectionCreationListener.aidl \
-# Filter out the plugin and samples when build IM.apk
-LOCAL_SRC_FILES := $(filter-out \
- plugin/% samples/% \
- ,$(LOCAL_SRC_FILES))
-
LOCAL_PACKAGE_NAME := IM
-# TODO: Remove dependency of application on the test runner (android.test.runner)
-# library.
-LOCAL_JAVA_LIBRARIES := android.test.runner \
- com.android.im.plugin \
-# com.android.providers.im.plugin
-
-# LOCAL_REQUIRED_MODULES must go before BUILD_PACKAGE
-LOCAL_REQUIRED_MODULES := libwbxml libwbxml_jni ImProvider
+LOCAL_JNI_SHARED_LIBRARIES := libwbxml_jni
#Disable building the APK; we are checking in the pre-built version which
#contains the credential plug-in instead. Note the libwbxml_jni has to be
android:label="@string/im_label"
android:icon="@drawable/ic_launcher_im"
android:taskAffinity="android.task.im">
- <uses-library android:name="com.android.im.plugin" />
- <!-- TODO: Remove dependency of application on the test runner
- (android.test) library. -->
- <uses-library android:name="android.test.runner" />
-
- <service android:name=".app.FrontDoorPlugin">
- <intent-filter>
- <action android:name="android.im.plugin" />
- </intent-filter>
- </service>
<service android:name=".service.RemoteImService"
android:process="android.process.im"
</intent-filter>
</activity>
- <activity android:name=".app.AccountActivity">
+ <activity android:name=".app.LandingPage">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="vnd.android.cursor.dir/im-providers" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".app.AccountActivity"
+ android:configChanges="orientation|keyboardHidden"
+ android:windowSoftInputMode="stateUnchanged">
<intent-filter>
<action android:name="android.intent.action.EDIT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
- <activity android:name=".app.SigningInActivity">
+ <activity android:name=".app.SigningInActivity"
+ android:configChanges="orientation|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
+ <!--Plug-in Sevice started-->
+ <!--
+ <service android:name="com.android.im.plugin.demo.DemoImPlugin">
+ <intent-filter>
+ <action android:name="com.android.im.plugin" />
+ </intent-filter>
+ <meta-data android:name="com.android.im.provider_name"
+ android:value="Demo"/>
+ <meta-data android:name="com.android.im.provider_full_name"
+ android:value="Demo IM Provider"/>
+ <meta-data android:name="com.android.im.signup_url"
+ android:value="http://xxx.xxx.xxx"/>
+ </service>
+ -->
+ <!--Plug-in Service end-->
+
</application>
</manifest>
LOCAL_SRC_FILES := \
src/wbxml_parser.cpp \
+ src/wbxml_encoder.cpp \
src/imps_encoder.cpp \
src/csp13tags_hash.c \
src/csp13values_hash.c \
LOCAL_MODULE := libwbxml
-include $(BUILD_SHARED_LIBRARY)
+include $(BUILD_STATIC_LIBRARY)
# xml2wbxml library: libxml2wbxml.so
# ---------------------------------------
LOCAL_SHARED_LIBRARIES += \
libutils \
- libwbxml \
libexpat
+LOCAL_STATIC_LIBRARIES := libwbxml
+
LOCAL_MODULE_TAGS := $(wbxml_module_tags)
LOCAL_MODULE := libxml2wbxml
LOCAL_SRC_FILES += test/syncml_parser_test.cpp
LOCAL_SHARED_LIBRARIES += \
- libwbxml \
libxml2wbxml \
libembunit \
libutils \
libexpat
-LOCAL_MODULE_TAGS := optional
+LOCAL_STATIC_LIBRARIES := libwbxml
+
+LOCAL_MODULE_TAGS := tests
LOCAL_MODULE := wbxmltest
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include \
- external/expat/lib \
$(JNI_H_INCLUDE)
LOCAL_CFLAGS += -DPLATFORM_ANDROID
LOCAL_SHARED_LIBRARIES += \
- libwbxml \
libutils \
libcutils \
- libexpat
+
+LOCAL_STATIC_LIBRARIES := libwbxml
LOCAL_MODULE_TAGS := $(wbxml_module_tags)
{
public:
ImpsWbxmlEncoder(int publicid) :
- mPublicId(publicid)
+ WbxmlEncoder(publicid)
{
reset();
}
EncoderError endElement();
private:
- int mPublicId;
int mTagCodePage;
- string mResult;
string mCurrElement;
int mDepth;
- EncoderError encodeInteger(const char *chars, int len);
- EncoderError encodeDatetime(const char *chars, int len);
EncoderError encodeString(const char *chars, int len);
EncoderError encodeAttrib(const char *name, const char *value);
- void encodeInlinedStr(const char *s, int len);
- void encodeMbuint(uint32_t i);
-
- void appendResult(int ch)
- {
- mResult += (char)ch;
- }
-
- void appendResult(const char *s, int len)
- {
- mResult.append(s, len);
- }
};
#endif
#include <stdint.h>
#include "wbxml_const.h"
+#include "wbxml_stl.h"
+
class WbxmlHandler
{
class WbxmlEncoder
{
public:
+ WbxmlEncoder(int publicId):mPublicId(publicId) {}
+
virtual ~WbxmlEncoder() {}
void setWbxmlHandler(WbxmlHandler * handler)
*/
virtual void reset() = 0;
+ static bool isXmlWhitespace(int ch);
+ static bool parseUint(const char * s, int len, uint32_t *res);
+
protected:
WbxmlHandler * mHandler;
+ int mPublicId;
+
+ EncoderError encodeInteger(const char *chars, int len);
+ EncoderError encodeDatetime(const char *chars, int len);
+ void encodeInlinedStr(const char *s, int len);
+ void encodeMbuint(uint32_t i);
+
+ void clearResult()
+ {
+ mResult.clear();
+ }
+
+ void appendResult(int ch)
+ {
+ mResult += (char)ch;
+ }
+
+ void appendResult(const char *s, int len)
+ {
+ mResult.append(s, len);
+ }
+
+ void sendResult();
+
+ /**
+ * Append a string into the string table, return the index of the string in
+ * the string table.
+ */
+ int appendToStringTable(const char *s);
+
+private:
+ string mResult;
+ vector<string> mStringTable;
};
#endif
{ "http://www.openmobilealliance.org/DTD/IMPS-TRC", 0x0d },
};
-static bool isXmlWhitespace(int ch)
-{
- return ch == ' ' || ch == 9 || ch == 0xd || ch == 0xa;
-}
-
static bool isDatetimeElement(const char *name)
{
return (strcmp("DateTime", name) == 0 || strcmp("DeliveryTime", name) == 0);
}
-static bool parseUint(const char * s, int len, uint32_t *res)
-{
- string str(s, len);
- char *end;
- long long val = strtoll(str.c_str(), &end, 10);
- if (*end != 0 || val < 0 || val > 0xFFFFFFFFU) {
- return false;
- }
- *res = (uint32_t)val;
- return true;
-}
-
void ImpsWbxmlEncoder::reset()
{
- // WBXML 1.3, UTF-8, no string table
- char header[4] = {0x03, (char)mPublicId, 0x6A, 0x00};
- mResult.clear();
- mResult.append(header, sizeof(header));
+ clearResult();
mTagCodePage = 0;
mCurrElement.clear();
return ERROR_INVALID_DATA;
}
+ bool isUnknownTag = false;
int stag = csp13TagNameToKey(name);
if (stag == -1) {
- return ERROR_UNSUPPORTED_TAG;
+ stag = TOKEN_LITERAL;
+ isUnknownTag = true;
}
mDepth++;
mCurrElement = name;
stag |= 0x80; // has attribute
}
appendResult(stag);
+
+ if (isUnknownTag) {
+ int index = appendToStringTable(name);
+ encodeMbuint(index);
+ }
if (stag & 0x80) {
for (size_t i = 0; atts[i]; i += 2) {
EncoderError err = encodeAttrib(atts[i], atts[i + 1]);
}
appendResult(TOKEN_END);
mCurrElement.clear();
- if (mDepth == 0 && mHandler) {
- mHandler->wbxmlData(mResult.c_str(), mResult.size());
- }
- return NO_ERROR;
-}
-
-EncoderError ImpsWbxmlEncoder::encodeInteger(const char *chars, int len)
-{
- uint32_t val;
- if (!parseUint(chars, len, &val)) {
- return ERROR_INVALID_INTEGER_VALUE;
- }
-
- appendResult(TOKEN_OPAQUE);
- uint32_t mask = 0xff000000U;
- int numBytes = 4;
- while (!(val & mask) && mask) {
- numBytes--;
- mask >>= 8;
- }
- if (!numBytes) {
- // Zero value. We generate at least 1 byte OPAQUE data.
- // libwbxml2 generates 0 byte long OPAQUE data (0xC3 0x00) in this case.
- numBytes = 1;
- }
-
- appendResult(numBytes);
- while (numBytes) {
- numBytes--;
- appendResult((val >> (numBytes * 8)) & 0xff);
- }
-
- return NO_ERROR;
-}
-
-EncoderError ImpsWbxmlEncoder::encodeDatetime(const char *chars, int len)
-{
- // to make life easier we accept only yyyymmddThhmmssZ
- if (len != 16 || chars[8] != 'T' || chars[15] != 'Z') {
- return ERROR_INVALID_DATETIME_VALUE;
+ if (mDepth == 0) {
+ sendResult();
}
- appendResult(TOKEN_OPAQUE);
- appendResult(6);
-
- uint32_t year, month, day, hour, min, sec;
- if (!parseUint(chars, 4, &year)
- || !parseUint(chars + 4, 2, &month)
- || !parseUint(chars + 6, 2, &day)
- || !parseUint(chars + 9, 2, &hour)
- || !parseUint(chars + 11,2, &min)
- || !parseUint(chars + 13,2, &sec)) {
- return ERROR_INVALID_DATETIME_VALUE;
- }
- if (year > 4095 || month > 12 || day > 31 || hour > 23 || min > 59 || sec > 59) {
- return ERROR_INVALID_DATETIME_VALUE;
- }
-
- appendResult(year >> 6);
- appendResult(((year & 0x3f) << 2) | (month >> 2));
- appendResult(((month & 0x3) << 6) | (day << 1) | (hour >> 4));
- appendResult(((hour & 0xf) << 4) | (min >> 2));
- appendResult(((min & 0x2) << 6) | sec);
- appendResult('Z');
return NO_ERROR;
}
return ERROR_UNSUPPORTED_ATTR;
}
int valueLen = strlen(value);
- for (size_t i = 0; i < sizeof(csp13xmlns) / sizeof(csp13xmlns[0]); i++) {
+ size_t csp13xmlnsCount = sizeof(csp13xmlns) / sizeof(csp13xmlns[0]);
+ size_t i;
+ for (i = 0; i < csp13xmlnsCount; i++) {
const char * prefix = csp13xmlns[i].prefix;
int prefixLen = strlen(csp13xmlns[i].prefix);
if (strncmp(prefix, value, prefixLen) == 0) {
return NO_ERROR;
}
}
+ if (i == csp13xmlnsCount) {
+ // not predefined attribute
+ appendResult(TOKEN_LITERAL);
+ int index = appendToStringTable(name);
+ encodeMbuint(index);
+ }
encodeInlinedStr(value, valueLen);
return NO_ERROR;
}
-
-void ImpsWbxmlEncoder::encodeInlinedStr(const char *s, int len)
-{
- // TODO: move this to WbxmlEncoder
- // TODO: handle ENTITY
- appendResult(TOKEN_STR_I);
- appendResult(s, len);
- appendResult('\0');
-}
-
-void ImpsWbxmlEncoder::encodeMbuint(uint32_t val)
-{
- char buf[32 / 7 + 1]; // each byte holds up to 7 bits
- int i = sizeof(buf);
-
- buf[--i] = val & 0x7f;
- val >>= 7;
- while ((i > 0) && (val & 0x7f)) {
- buf[--i] = 0x80 | (val & 0x7f);
- val >>= 7;
- }
-
- appendResult(buf + i, sizeof(buf) - i);
-}
-
--- /dev/null
+/*
+ * Copyright (C) 2009 Esmertec AG.
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include "imps_encoder.h"
+
+bool WbxmlEncoder::isXmlWhitespace(int ch)
+{
+ return ch == ' ' || ch == 9 || ch == 0xd || ch == 0xa;
+}
+
+bool WbxmlEncoder::parseUint(const char * s, int len, uint32_t *res)
+{
+ string str(s, len);
+ char *end;
+ long long val = strtoll(str.c_str(), &end, 10);
+ if (*end != 0 || val < 0 || val > 0xFFFFFFFFU) {
+ return false;
+ }
+ *res = (uint32_t)val;
+ return true;
+}
+
+EncoderError WbxmlEncoder::encodeInteger(const char *chars, int len)
+{
+ uint32_t val;
+ if (!parseUint(chars, len, &val)) {
+ return ERROR_INVALID_INTEGER_VALUE;
+ }
+
+ appendResult(TOKEN_OPAQUE);
+ uint32_t mask = 0xff000000U;
+ int numBytes = 4;
+ while (!(val & mask) && mask) {
+ numBytes--;
+ mask >>= 8;
+ }
+ if (!numBytes) {
+ // Zero value. We generate at least 1 byte OPAQUE data.
+ // libwbxml2 generates 0 byte long OPAQUE data (0xC3 0x00) in this case.
+ numBytes = 1;
+ }
+
+ appendResult(numBytes);
+ while (numBytes) {
+ numBytes--;
+ appendResult((val >> (numBytes * 8)) & 0xff);
+ }
+
+ return NO_ERROR;
+}
+
+EncoderError WbxmlEncoder::encodeDatetime(const char *chars, int len)
+{
+ // to make life easier we accept only yyyymmddThhmmssZ
+ if (len != 16 || chars[8] != 'T' || chars[15] != 'Z') {
+ return ERROR_INVALID_DATETIME_VALUE;
+ }
+ appendResult(TOKEN_OPAQUE);
+ appendResult(6);
+
+ uint32_t year, month, day, hour, min, sec;
+ if (!parseUint(chars, 4, &year)
+ || !parseUint(chars + 4, 2, &month)
+ || !parseUint(chars + 6, 2, &day)
+ || !parseUint(chars + 9, 2, &hour)
+ || !parseUint(chars + 11,2, &min)
+ || !parseUint(chars + 13,2, &sec)) {
+ return ERROR_INVALID_DATETIME_VALUE;
+ }
+ if (year > 4095 || month > 12 || day > 31 || hour > 23 || min > 59 || sec > 59) {
+ return ERROR_INVALID_DATETIME_VALUE;
+ }
+
+ appendResult(year >> 6);
+ appendResult(((year & 0x3f) << 2) | (month >> 2));
+ appendResult(((month & 0x3) << 6) | (day << 1) | (hour >> 4));
+ appendResult(((hour & 0xf) << 4) | (min >> 2));
+ appendResult(((min & 0x2) << 6) | sec);
+ appendResult('Z');
+ return NO_ERROR;
+}
+
+void WbxmlEncoder::encodeInlinedStr(const char *s, int len)
+{
+ // TODO: handle ENTITY
+ appendResult(TOKEN_STR_I);
+ appendResult(s, len);
+ appendResult('\0');
+}
+
+void WbxmlEncoder::encodeMbuint(uint32_t val)
+{
+ char buf[32 / 7 + 1]; // each byte holds up to 7 bits
+ int i = sizeof(buf);
+
+ buf[--i] = val & 0x7f;
+ val >>= 7;
+ while ((i > 0) && (val & 0x7f)) {
+ buf[--i] = 0x80 | (val & 0x7f);
+ val >>= 7;
+ }
+
+ appendResult(buf + i, sizeof(buf) - i);
+}
+
+int WbxmlEncoder::appendToStringTable(const char *s)
+{
+ int stringTableSize = mStringTable.size();
+ int offset = 0;
+
+ // search the string table to find if the string already exist
+ int index = 0;
+ for (; index < stringTableSize; index++) {
+ if (mStringTable[index] == s) {
+ break;
+ }
+ offset += mStringTable[index].length();
+ ++offset; // '\0' for each string in the table
+ }
+ if (index == stringTableSize) {
+ // not found, insert a new one
+ mStringTable.push_back(s);
+ }
+ return offset;
+}
+
+void WbxmlEncoder::sendResult()
+{
+ if (mHandler) {
+ string data;
+ string tmp = mResult;
+ mResult = data;
+
+ // WBXML 1.3, UTF-8
+ char header[3] = { 0x03, (char) mPublicId, 0x6A };
+ appendResult(header, 3);
+
+ // calculate the length of string table
+ int len = 0;
+ for (int i = 0; i < mStringTable.size(); i++) {
+ len += mStringTable[i].length();
+ ++len;
+ }
+
+ encodeMbuint(len);
+
+ // encode each string in the table
+ for (int i = 0; i < mStringTable.size(); i++) {
+ mResult += mStringTable[i];
+ mResult += '\0';
+ }
+
+ mResult += tmp;
+
+ mHandler->wbxmlData(mResult.c_str(), mResult.size());
+ }
+}
+++ /dev/null
-#
-# Copyright (C) 2008 Esmertec AG.
-# 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.
-#
-LOCAL_PATH := $(call my-dir)
-
-# the library
-# ============================================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- $(call all-subdir-java-files) \
- com/android/im/plugin/IImPlugin.aidl \
- com/android/im/plugin/IPasswordDigest.aidl \
- com/android/im/plugin/IPresenceMapping.aidl \
-
-LOCAL_MODULE:= com.android.im.plugin
-
-include $(BUILD_JAVA_LIBRARY)
*/
package com.android.im.plugin;
-interface IImPlugin {
+import java.util.Map;
+
+public interface ImPlugin {
/**
* Gets a map of branding resources for the provider. The keys are defined
* in {@link com.android.im.plugin.BrandingResourceIDs}. The values are the
*
* @return The map of branding resources.
*/
- Map getResourceMap();
+ Map<Integer, Integer> getResourceMap();
/**
* Gets an array of IDs of the smiley icons. The sequence of the IDs must
*
* @return the configuration for the provider.
*/
- Map getProviderConfig();
+ Map<String, String> getProviderConfig();
}
package com.android.im.plugin;
+import com.android.im.engine.ImException;
+
/**
* The password digest method used in IMPS login transaction.
*/
-interface IPasswordDigest {
+public interface PasswordDigest {
/**
* Gets an array of supported digest schema.
*
* @param nonce The nonce string returned by the server.
* @param password The user password.
* @return The digest bytes of the password.
+ * @throws ImException
*/
- String digest(String schema, String nonce, String password);
+ String digest(String schema, String nonce, String password) throws ImException;
}
*/
package com.android.im.plugin;
+import java.util.Map;
+
/**
* The methods used to map presence value sent in protocol to predefined
* presence status.
*/
-interface IPresenceMapping {
+public interface PresenceMapping {
/**
* Tells if the mapping needs all presence values sent in protocol. If this
* method returns true, the framework will pass all the presence values
* @see #requireAllPresenceValues()
*/
int getPresenceStatus(boolean onlineStatus, String userAvailability,
- in Map allValues);
+ Map<String, Object> allValues);
/**
* Gets the value of <OnlineStatus> will be sent to the server when
* @param status the predefined status.
* @return The extra values that will be sent to the server.
*/
- Map getExtra(int status);
+ Map<String, Object> getExtra(int status);
/**
* Gets an array of the supported presence status. The client can only update
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true" android:color="#ff000000"/>
+ <item android:state_selected="true" android:color="#ff000000"/>
+ <item android:state_pressed="true" android:color="#ff000000"/>
+ <item android:color="#ffffffff"/>
+</selector>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true" android:color="#ff000000"/>
+ <item android:state_selected="true" android:color="#ff000000"/>
+ <item android:state_pressed="true" android:color="#ff000000"/>
+ <item android:color="#ffbebebe"/>
+</selector>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+
+<com.android.im.app.ProviderListItem
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight">
+
+ <ImageView
+ android:id="@+id/providerIcon"
+ android:layout_gravity="center_vertical"
+ android:scaleType="fitXY"
+ android:paddingLeft="5dip"
+ android:paddingRight="2dip"
+ android:layout_width="39dip"
+ android:layout_height="32dip"/>
+
+ <LinearLayout
+ android:id="@+id/underBubble"
+ android:orientation="horizontal"
+ android:layout_weight="1"
+ android:layout_width="0dip"
+ android:layout_height="fill_parent">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_weight="1"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView android:id="@+id/providerName"
+ android:textColor="@color/landing_page_text"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textStyle="bold"
+ android:singleLine="true"
+ android:layout_weight="1.0"
+ android:ellipsize="marquee"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+ <TextView android:id="@+id/conversations"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/landing_page_text_secondary"
+ android:singleLine="true"
+ android:paddingRight="3dip"
+ android:paddingLeft="5dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ <TextView android:id="@+id/loginName"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="@color/landing_page_text"
+ android:ellipsize="marquee"
+ android:singleLine="true"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+ <ImageView
+ android:id="@+id/statusIcon"
+ android:scaleType="fitXY"
+ android:paddingRight="6dip"
+ android:paddingLeft="6dip"
+ android:layout_weight="0"
+ android:layout_gravity="center_vertical"
+ android:layout_width="30dip"
+ android:layout_height="18dip"/>
+ </LinearLayout>
+</com.android.im.app.ProviderListItem>
\ No newline at end of file
<ExpandableListView android:id="@+id/contactsList"
android:layout_width="fill_parent"
- android:layout_height="fill_parent" />
+ android:layout_height="fill_parent"
+ android:nextFocusUp="@id/statusDropDownButton" />
</com.android.im.app.ContactListView>
android:layout_height="wrap_content">
<TextView android:id="@+id/line1"
- android:maxLines="1"
+ android:singleLine="true"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="wrap_content"
</LinearLayout>
<TextView android:id="@+id/line2"
- android:maxLines="1"
+ android:singleLine="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft">
<TextView android:id="@+id/text1"
- android:maxLines="1"
+ android:singleLine="true"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
+ android:ellipsize="marquee"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView android:id="@+id/text2"
- android:maxLines="1"
+ android:singleLine="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
Home screen. This is a noun. -->
<string name="im_label">IM</string>
+ <!-- These strings displayed on the landing page. -->
+ <!-- The title of the landing page.-->
+ <string name="landing_page_title">Chat - Select an account</string>
+
+ <!-- Landing page screen menu and context menu items. -->
+ <!-- Conext menu item: add a new account.-->
+ <string name="menu_add_account">Add account</string>
+ <!-- Conext menu item: edit an account.-->
+ <string name="menu_edit_account">Edit account</string>
+ <!-- Conext menu item: remove an account.-->
+ <string name="menu_remove_account">Remove account</string>
+ <!-- Screen menu item: sign out all service.-->
+ <string name="menu_sign_out_all">Sign out all</string>
+
+ <!-- These strings displayed on the landing page. -->
+ <!-- The title of the landing page.-->
+ <string name="choose_account_title">Chat - Select an account</string>
+ <!-- Displays the number of ongoing chats on the landing page.-->
+ <string name="conversations">(%1$d)</string>
+
<!-- Sign in progress screen menu items.-->
<!-- Screen menu item on the sign-in progress screen. It allows the user to cancel signing in.-->
<string name="menu_cancel_signin">Cancel signin</string>
<!-- Strings for the confirm dialogs-->
<!-- The title of the confirm dialog which asks the user to continue or cancel an operation such as block a contact, remove a contact.-->
<string name="confirm">Confirm</string>
+ <!-- This is the message displayed in the confirm dialog when the user choose to sign out all service -->
+ <string name="signout_all_confirm_message">Do you want to sign out all services?</string>
<!-- This is the message displayed in the confirm dialog that opens when user clicks to delete a contact in the contact list screen.-->
<string name="confirm_delete_contact">The contact \"<xliff:g id="user">%1$s</xliff:g>\" will be deleted.</string>
<!-- This is the message displayed in the confirm dialog that opens when user clicks to block a contact in the contact list screen.-->
+++ /dev/null
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := samples
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files) \
-
-LOCAL_PACKAGE_NAME := ImPluginDemo
-
-LOCAL_JAVA_LIBRARIES := com.android.im.plugin
-
-include $(BUILD_PACKAGE)
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright (C) 2008 Esmertec Inc.
- * 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.
- */
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.im.plugin.demo" android:sharedUserId="android.uid.im">
-
- <application android:taskAffinity="android.task.im">
-
- <uses-library android:name="com.android.im.plugin" />
-
- <service android:name=".DemoImPlugin">
- <intent-filter>
- <action android:name="com.android.im.plugin" />
- </intent-filter>
- <meta-data android:name="com.android.im.provider_name"
- android:value="Demo"/>
- <meta-data android:name="com.android.im.provider_full_name"
- android:value="Demo IM Provider"/>
- <meta-data android:name="com.android.im.signup_url"
- android:value="http://xxx.xxx.xxx"/>
- </service>
-
- </application>
-
-</manifest>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"Přidat kontakt"</string>
- <string name="menu_remove_contact">"Smazat kontakt"</string>
- <string name="menu_block_contact">"Blokovat"</string>
- <string name="menu_contact_list">"Seznam kontaktů"</string>
- <string name="menu_start_chat">"Odeslat zprávu"</string>
- <string name="menu_view_profile">"Info o kamarádovi"</string>
- <string name="menu_end_conversation">"Konec konverzace"</string>
- <string name="menu_switch_chats">"Přepnout chat"</string>
- <string name="menu_insert_smiley">"Vložit emotikony"</string>
- <string name="sign_up">"Zřízení nového účtu"</string>
- <string name="check_save_password">"Pokud je váš telefon ukraden nebo jej ztratíte, přejděte v zájmu vlastní bezpečnosti na počítači na web a změňte své heslo."</string>
- <string name="buddy_list_title">"Seznam kontaktů – <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"Uživatelské jméno:"</string>
- <string name="ongoing_conversation">"Konverzace (<xliff:g id="NUMBER">%1$d</xliff:g>)"</string>
- <string name="contact_profile_title">"Kontaktní informace"</string>
- <string name="presence_available">"Povoleno"</string>
- <string name="add_contact_title">"Přidat kontakt"</string>
- <string name="input_contact_label">"Jméno osoby, kterou chcete přidat:"</string>
- <string name="invite_label">"Přidat kamaráda"</string>
- <string-array name="smiley_names">
- <item>"Happy"</item>
- <item>"Smutný"</item>
- <item>"Mrkající"</item>
- <item>"Vypláznutý jazyk"</item>
- <item>"Překvapený"</item>
- <item>"Pusa"</item>
- <item>"Hej!"</item>
- <item>"Cool"</item>
- <item>"Cinkání zlaťáků"</item>
- <item>"Šlápota v úsměvu"</item>
- <item>"V rozpacích"</item>
- <item>"Andílek"</item>
- <item>"Nerozhodný"</item>
- <item>"Rozplakaný"</item>
- <item>"Ani muk"</item>
- <item>"Smějící se"</item>
- <item>"Zmatený"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"Tilføj kontaktperson"</string>
- <string name="menu_remove_contact">"Slet kontaktperson"</string>
- <string name="menu_block_contact">"Bloker"</string>
- <string name="menu_contact_list">"Liste over kontaktpersoner"</string>
- <string name="menu_start_chat">"Send IM"</string>
- <string name="menu_view_profile">"Venneoplysninger"</string>
- <string name="menu_end_conversation">"Afslut samtale"</string>
- <string name="menu_switch_chats">"Skift chats"</string>
- <string name="menu_insert_smiley">"Indsæt humørikoner"</string>
- <string name="sign_up">"Få en ny konto"</string>
- <string name="check_save_password">"For din egen sikkerheds skyld skal du gå til webstedet på din computer og ændre din adgangskode, hvis du mister din telefon, eller den bliver stjålet."</string>
- <string name="buddy_list_title">"Liste over kontaktpersoner – <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"Brugernavn:"</string>
- <string name="ongoing_conversation">"Samtaler (<xliff:g id="NUMBER">%1$d</xliff:g>)"</string>
- <string name="contact_profile_title">"Kontaktoplysninger"</string>
- <string name="presence_available">"Ledig"</string>
- <string name="add_contact_title">"Tilføj kontaktperson"</string>
- <string name="input_contact_label">"Skærmnavn for den person du ønsker at tilføje:"</string>
- <string name="invite_label">"Tilføj ven"</string>
- <string-array name="smiley_names">
- <item>"Glad"</item>
- <item>"Trist"</item>
- <item>"Blinker"</item>
- <item>"Rækker tunge"</item>
- <item>"Overrasket"</item>
- <item>"Kysser"</item>
- <item>"Råber"</item>
- <item>"Sej"</item>
- <item>"Pengeglad"</item>
- <item>"Forlegen"</item>
- <item>"Flov"</item>
- <item>"Engel"</item>
- <item>"Uafklaret"</item>
- <item>"Græder"</item>
- <item>"Lukket med syv segl"</item>
- <item>"Griner"</item>
- <item>"Forvirret"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"Kontakt hinzufügen"</string>
- <string name="menu_remove_contact">"Kontakt löschen"</string>
- <string name="menu_block_contact">"Blockieren"</string>
- <string name="menu_contact_list">"Kontaktliste"</string>
- <string name="menu_start_chat">"IM senden"</string>
- <string name="menu_view_profile">"Buddy Info"</string>
- <string name="menu_end_conversation">"Gespräch beenden"</string>
- <string name="menu_switch_chats">"Chats wechseln"</string>
- <string name="menu_insert_smiley">"Emoticons einfügen"</string>
- <string name="sign_up">"Ein neues Konto erhalten"</string>
- <string name="check_save_password">"Zu Ihrer Sicherheit: Wenn Sie Ihr Telefon verloren haben oder es gestohlen wurde, rufen Sie die Website auf Ihrem Computer auf und ändern Sie Ihr Passwort."</string>
- <string name="buddy_list_title">"Kontaktliste - <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"Benutzername:"</string>
- <string name="ongoing_conversation">"Gespräche (<xliff:g id="NUMBER">%1$d</xliff:g>)"</string>
- <string name="contact_profile_title">"Kontaktinformationen"</string>
- <string name="presence_available">"Verfügbar"</string>
- <string name="add_contact_title">"Kontakt hinzufügen"</string>
- <string name="input_contact_label">"Displayname der Person, die Sie hinzufügen möchten:"</string>
- <string name="invite_label">"Buddy hinzufügen"</string>
- <string-array name="smiley_names">
- <item>"Glücklich"</item>
- <item>"Traurig"</item>
- <item>"Zwinkern"</item>
- <item>"Zunge rausstrecken"</item>
- <item>"Überrascht"</item>
- <item>"Kuss"</item>
- <item>"Schreien"</item>
- <item>"Cool"</item>
- <item>"Lass Taten sprechen"</item>
- <item>"Fettnäpfchen"</item>
- <item>"Peinlich berührt"</item>
- <item>"Engel"</item>
- <item>"Unentschlossen"</item>
- <item>"Weinen"</item>
- <item>"Versiegelte Lippen"</item>
- <item>"Lachen"</item>
- <item>"Verwirrt"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"Προσθήκη επαφής"</string>
- <string name="menu_remove_contact">"Διαγραφή επαφής"</string>
- <string name="menu_block_contact">"Αποκλεισμός"</string>
- <string name="menu_contact_list">"Λίστα επαφών"</string>
- <string name="menu_start_chat">"Αποστολή άμεσων μηνυμάτων (IM)"</string>
- <string name="menu_view_profile">"Πληροφορίες φίλου"</string>
- <string name="menu_end_conversation">"Τερματισμός συνομιλίας"</string>
- <string name="menu_switch_chats">"Αλλαγή συζητήσεων"</string>
- <string name="menu_insert_smiley">"Εισαγωγή εικονιδίων emoticons"</string>
- <string name="sign_up">"Αποκτήστε νέο λογαριασμό"</string>
- <string name="check_save_password">"Για την ασφάλειά σας, εάν το τηλέφωνό σας χαθεί ή κλαπεί, μεταβείτε στον ιστότοπο στον υπολογιστή σας και αλλάξτε τον κωδικό πρόσβασης."</string>
- <string name="buddy_list_title">"Λίστα επαφών - <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"Όνομα χρήστη:"</string>
- <string name="ongoing_conversation">"Συνομιλίες (<xliff:g id="NUMBER">%1$d</xliff:g>)"</string>
- <string name="contact_profile_title">"Πληροφορίας επικοινωνίας"</string>
- <string name="presence_available">"Διαθέσιμος/η"</string>
- <string name="add_contact_title">"Προσθήκη επαφής"</string>
- <string name="input_contact_label">"Το ψευδώνυμο του ατόμου που θέλετε να προσθέσετε:"</string>
- <string name="invite_label">"Προσθήκη φίλου"</string>
- <string-array name="smiley_names">
- <item>"Είμαι χαρούμενος"</item>
- <item>"Είμαι λυπημένος"</item>
- <item>"Κλείνω το μάτι"</item>
- <item>"Κοροϊδεύω"</item>
- <item>"Είμαι έκπληκτος"</item>
- <item>"Φιλάω"</item>
- <item>"Φωνάζω"</item>
- <item>"Cool"</item>
- <item>"Κάνω τα λόγια μου πράξη"</item>
- <item>"Είπα ανοησία"</item>
- <item>"Ντρέπομαι"</item>
- <item>"Αγγελούδι"</item>
- <item>"Δεν έχω αποφασίσει"</item>
- <item>"Κλαίω"</item>
- <item>"Δεν αποκαλύπτω τίποτα"</item>
- <item>"Γελάω"</item>
- <item>"Είμαι μπερδεμένος"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"Agregar contacto"</string>
- <string name="menu_remove_contact">"Eliminar contacto"</string>
- <string name="menu_block_contact">"Bloquear"</string>
- <string name="menu_contact_list">"Lista de contactos"</string>
- <string name="menu_start_chat">"Enviar mensajería instantánea"</string>
- <string name="menu_view_profile">"Información de Buddy"</string>
- <string name="menu_end_conversation">"Finalizar conversación"</string>
- <string name="menu_switch_chats">"Modificar chats"</string>
- <string name="menu_insert_smiley">"Insertar emoticones"</string>
- <string name="sign_up">"Obtén una cuenta nueva"</string>
- <string name="check_save_password">"Para tu seguridad, si pierdes o te roban el teléfono, ingresa al sitio web desde tu computadora y cambia la contraseña."</string>
- <string name="buddy_list_title">"Lista de contactos - <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"Nombre de usuario:"</string>
- <string name="ongoing_conversation">"Conversaciones (<xliff:g id="NUMBER">%1$d</xliff:g>)"</string>
- <string name="contact_profile_title">"Información de contacto"</string>
- <string name="presence_available">"Disponible"</string>
- <string name="add_contact_title">"Agregar contacto"</string>
- <string name="input_contact_label">"Nombre de pantalla de la persona que deseas agregar:"</string>
- <string name="invite_label">"Agregar Buddy"</string>
- <string-array name="smiley_names">
- <item>"Feliz"</item>
- <item>"Triste"</item>
- <item>"Guiñando un ojo"</item>
- <item>"Con la lengua afuera"</item>
- <item>"Sorprendido"</item>
- <item>"Besando"</item>
- <item>"Gritando"</item>
- <item>"En la onda"</item>
- <item>"Dinero en boca"</item>
- <item>"Meter la pata"</item>
- <item>"Avergonzado"</item>
- <item>"Ángel"</item>
- <item>"Indeciso"</item>
- <item>"Llorando"</item>
- <item>"Los labios están cerrados"</item>
- <item>"Riendo"</item>
- <item>"Confundido"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"Añadir contacto"</string>
- <string name="menu_remove_contact">"Eliminar contacto"</string>
- <string name="menu_block_contact">"Bloquear"</string>
- <string name="menu_contact_list">"Lista de contactos"</string>
- <string name="menu_start_chat">"Enviar MI"</string>
- <string name="menu_view_profile">"Información de amigo"</string>
- <string name="menu_end_conversation">"Finalizar conversación"</string>
- <string name="menu_switch_chats">"Cambiar de chat"</string>
- <string name="menu_insert_smiley">"Insertar emoticonos"</string>
- <string name="sign_up">"Obtener una cuenta nueva"</string>
- <string name="check_save_password">"Si pierdes el teléfono o te lo roban, te recomendamos por seguridad que accedas al sitio web desde tu equipo y cambies la contraseña."</string>
- <string name="buddy_list_title">"Lista de contactos - <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"Nombre de usuario:"</string>
- <string name="ongoing_conversation">"Conversaciones (<xliff:g id="NUMBER">%1$d</xliff:g>)"</string>
- <string name="contact_profile_title">"Información de contacto"</string>
- <string name="presence_available">"Disponible"</string>
- <string name="add_contact_title">"Añadir contacto"</string>
- <string name="input_contact_label">"Nombre de pantalla de la persona que quieres añadir:"</string>
- <string name="invite_label">"Añadir amigo"</string>
- <string-array name="smiley_names">
- <item>"Contento"</item>
- <item>"Triste"</item>
- <item>"Guiño"</item>
- <item>"Sacando la lengua"</item>
- <item>"Sorprendido"</item>
- <item>"Besando"</item>
- <item>"Gritando"</item>
- <item>"Atractivo"</item>
- <item>"Dinero en la boca"</item>
- <item>"Metedura de pata"</item>
- <item>"Avergonzado"</item>
- <item>"Ángel"</item>
- <item>"Indeciso"</item>
- <item>"Llorando"</item>
- <item>"Labios sellados"</item>
- <item>"Riendo"</item>
- <item>"Confuso"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"Ajouter un contact"</string>
- <string name="menu_remove_contact">"Supprimer un contact"</string>
- <string name="menu_block_contact">"Bloquer"</string>
- <string name="menu_contact_list">"Liste de contacts"</string>
- <string name="menu_start_chat">"Envoyer un message instantané"</string>
- <string name="menu_view_profile">"Infos sur le contact"</string>
- <string name="menu_end_conversation">"Terminer la conversation"</string>
- <string name="menu_switch_chats">"Changer de chat"</string>
- <string name="menu_insert_smiley">"Insérer une émoticône"</string>
- <string name="sign_up">"Créer un nouveau compte"</string>
- <string name="check_save_password">"Pour votre sécurité, en cas de vol ou de perte de votre téléphone, rendez-vous sur le site Web depuis votre ordinateur et modifiez votre mot de passe."</string>
- <string name="buddy_list_title">"Liste de contacts :<xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"Nom d\'utilisateur :"</string>
- <string name="ongoing_conversation">"Conversations (<xliff:g id="NUMBER">%1$d</xliff:g>)"</string>
- <string name="contact_profile_title">"Infos sur le contact"</string>
- <string name="presence_available">"Disponible"</string>
- <string name="add_contact_title">"Ajouter le contact"</string>
- <string name="input_contact_label">"Identifiant du contact que vous souhaitez ajouter :"</string>
- <string name="invite_label">"Ajouter un contact"</string>
- <string-array name="smiley_names">
- <item>"Content"</item>
- <item>"Triste"</item>
- <item>"Clin d\'œil"</item>
- <item>"Tire la langue"</item>
- <item>"Surpris"</item>
- <item>"Bisou"</item>
- <item>"Hurle"</item>
- <item>"Cool"</item>
- <item>"Argent"</item>
- <item>"Embarrassé"</item>
- <item>"Gêné"</item>
- <item>"Ange"</item>
- <item>"Indécis"</item>
- <item>"Pleure"</item>
- <item>"Motus et bouche cousue"</item>
- <item>"Rigole"</item>
- <item>"Confus"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"Aggiungi contatto"</string>
- <string name="menu_remove_contact">"Elimina contatto"</string>
- <string name="menu_block_contact">"Blocca"</string>
- <string name="menu_contact_list">"Elenco contatti"</string>
- <string name="menu_start_chat">"Invia msg chat"</string>
- <string name="menu_view_profile">"Info amico"</string>
- <string name="menu_end_conversation">"Termina conversazione"</string>
- <string name="menu_switch_chats">"Cambia conversazione"</string>
- <string name="menu_insert_smiley">"Inserisci emoticon"</string>
- <string name="sign_up">"Ottieni un nuovo account"</string>
- <string name="check_save_password">"Per la tua sicurezza, in caso di perdita o furto del telefono visita il sito web da un computer e cambia la password."</string>
- <string name="buddy_list_title">"Elenco contatti - <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"Nome utente:"</string>
- <string name="ongoing_conversation">"Conversazioni (<xliff:g id="NUMBER">%1$d</xliff:g>)"</string>
- <string name="contact_profile_title">"Info contatto"</string>
- <string name="presence_available">"Disponibile"</string>
- <string name="add_contact_title">"Aggiungi contatto"</string>
- <string name="input_contact_label">"Nome visualizzato della persona da aggiungere:"</string>
- <string name="invite_label">"Aggiungi amico"</string>
- <string-array name="smiley_names">
- <item>"Felice"</item>
- <item>"Triste"</item>
- <item>"Occhiolino"</item>
- <item>"Linguaccia"</item>
- <item>"Sorpreso"</item>
- <item>"Bacio"</item>
- <item>"Urlo"</item>
- <item>"Fico"</item>
- <item>"Fatti, non parole"</item>
- <item>"Gaffe"</item>
- <item>"Imbarazzato"</item>
- <item>"Angelo"</item>
- <item>"Indeciso"</item>
- <item>"Piango"</item>
- <item>"Labbra cucite"</item>
- <item>"Risata"</item>
- <item>"Confuso"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"連絡先を追加"</string>
- <string name="menu_remove_contact">"連絡先を削除"</string>
- <string name="menu_block_contact">"ブロック"</string>
- <string name="menu_contact_list">"連絡先リスト"</string>
- <string name="menu_start_chat">"チャットを送信"</string>
- <string name="menu_view_profile">"友だち情報"</string>
- <string name="menu_end_conversation">"チャット終了"</string>
- <string name="menu_switch_chats">"チャットを切り替え"</string>
- <string name="menu_insert_smiley">"絵文字を挿入"</string>
- <string name="sign_up">"新しいアカウントを取得"</string>
- <string name="check_save_password">"セキュリティ保護のため、携帯電話を紛失したり盗まれたりした場合は、パソコンからウェブサイトにアクセスしてパスワードを変更してください。"</string>
- <string name="buddy_list_title">"連絡先リスト - <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"ユーザー名:"</string>
- <string name="ongoing_conversation">"チャット (<xliff:g id="NUMBER">%1$d</xliff:g>件)"</string>
- <string name="contact_profile_title">"連絡先情報"</string>
- <string name="presence_available">"オンライン"</string>
- <string name="add_contact_title">"連絡先を追加"</string>
- <string name="input_contact_label">"追加する人のスクリーンネーム:"</string>
- <string name="invite_label">"友だちを追加"</string>
- <string-array name="smiley_names">
- <item>"ハッピー"</item>
- <item>"悲しい"</item>
- <item>"ウィンク"</item>
- <item>"アッカンベー"</item>
- <item>"びっくり"</item>
- <item>"キス"</item>
- <item>"激怒"</item>
- <item>"クール"</item>
- <item>"気持ち悪い"</item>
- <item>"しまった"</item>
- <item>"恥ずかしい"</item>
- <item>"天使"</item>
- <item>"迷う"</item>
- <item>"泣く"</item>
- <item>"お口にチャック"</item>
- <item>"笑顔"</item>
- <item>"混乱"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"연락처 추가"</string>
- <string name="menu_remove_contact">"연락처 삭제"</string>
- <string name="menu_block_contact">"차단"</string>
- <string name="menu_contact_list">"연락처 목록"</string>
- <string name="menu_start_chat">"채팅하기"</string>
- <string name="menu_view_profile">"친구 정보"</string>
- <string name="menu_end_conversation">"대화 종료"</string>
- <string name="menu_switch_chats">"채팅 전환"</string>
- <string name="menu_insert_smiley">"이모티콘 삽입"</string>
- <string name="sign_up">"새 계정 만들기"</string>
- <string name="check_save_password">"휴대전화를 잃어버렸거나 도난당한 경우에는 보안을 위해 컴퓨터에서 웹사이트를 방문하여 비밀번호를 변경하세요."</string>
- <string name="buddy_list_title">"연락처 목록 - <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"사용자 이름:"</string>
- <string name="ongoing_conversation">"대화(<xliff:g id="NUMBER">%1$d</xliff:g>개)"</string>
- <string name="contact_profile_title">"연락처 정보"</string>
- <string name="presence_available">"온라인"</string>
- <string name="add_contact_title">"연락처 추가"</string>
- <string name="input_contact_label">"추가할 사람의 대화명:"</string>
- <string name="invite_label">"친구 추가"</string>
- <string-array name="smiley_names">
- <item>"행복해"</item>
- <item>"슬퍼요"</item>
- <item>"윙크"</item>
- <item>"메롱"</item>
- <item>"헉!"</item>
- <item>"키스"</item>
- <item>"아악"</item>
- <item>"멋지네"</item>
- <item>"으이구"</item>
- <item>"실수했네"</item>
- <item>"당황"</item>
- <item>"천사"</item>
- <item>"결정하기 어렵군"</item>
- <item>"엉엉엉"</item>
- <item>"비밀로 할게"</item>
- <item>"하하하"</item>
- <item>"어리둥절"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"Legg til kontakt"</string>
- <string name="menu_remove_contact">"Fjern kontakt"</string>
- <string name="menu_block_contact">"Blokker kontakt"</string>
- <string name="menu_contact_list">"Kontaktliste"</string>
- <string name="menu_start_chat">"Start samtale"</string>
- <string name="menu_view_profile">"Vis profil"</string>
- <string name="menu_end_conversation">"Avslutt samtale"</string>
- <string name="menu_switch_chats">"Bytt mellom samtaler"</string>
- <string name="menu_insert_smiley">"Sett inn smilefjes"</string>
- <string name="sign_up">"Mangler du konto?"</string>
- <string name="check_save_password">"Av sikkerhetsgrunner, gå til nettstedet og endre passordet om telefonen blir tapt eller stjåler."</string>
- <string name="buddy_list_title">"Kontaktliste - <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"Brukernavn:"</string>
- <string name="ongoing_conversation">"Pågående samtaler (<xliff:g id="NUMBER">%1$d</xliff:g>)"</string>
- <string name="contact_profile_title">"Kontaktprofil"</string>
- <string name="presence_available">"Tilgjengelig"</string>
- <string name="add_contact_title">"Legg til kontakt"</string>
- <string name="input_contact_label">"E-postadresse til den du ønsker å invitere:"</string>
- <string name="invite_label">"Send invitasjon"</string>
- <string-array name="smiley_names">
- <item>"Glad"</item>
- <item>"Trist"</item>
- <item>"Blunker"</item>
- <item>"Rekker tunge"</item>
- <item>"Overrasket"</item>
- <item>"Kyss"</item>
- <item>"Roper"</item>
- <item>"Kul"</item>
- <item>"Pengemunn"</item>
- <item>"Fot i munnen"</item>
- <item>"Flau"</item>
- <item>"Engel"</item>
- <item>"Usikker"</item>
- <item>"Gråter"</item>
- <item>"Stille som graven"</item>
- <item>"Ler"</item>
- <item>"Forvirret"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"Contact toevoegen"</string>
- <string name="menu_remove_contact">"Contact verwijderen"</string>
- <string name="menu_block_contact">"Blokkeren"</string>
- <string name="menu_contact_list">"Lijst met contacten"</string>
- <string name="menu_start_chat">"Chat verzenden"</string>
- <string name="menu_view_profile">"Buddygegevens"</string>
- <string name="menu_end_conversation">"Conversatie beëindigen"</string>
- <string name="menu_switch_chats">"Schakelen tussen chats"</string>
- <string name="menu_insert_smiley">"Emoticons invoegen"</string>
- <string name="sign_up">"Een nieuw account verkrijgen"</string>
- <string name="check_save_password">"Als u uw telefoon verliest of als deze wordt gestolen, moet u voor uw veiligheid naar de website gaan op uw computer en uw wachtwoord wijzigen."</string>
- <string name="buddy_list_title">"Lijst met contacten - <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"Gebruikersnaam:"</string>
- <string name="ongoing_conversation">"Conversaties (<xliff:g id="NUMBER">%1$d</xliff:g>)"</string>
- <string name="contact_profile_title">"Contactgegevens"</string>
- <string name="presence_available">"Beschikbaar"</string>
- <string name="add_contact_title">"Contact toevoegen"</string>
- <string name="input_contact_label">"Schermnaam van de persoon die u wilt toevoegen:"</string>
- <string name="invite_label">"Buddy toevoegen"</string>
- <string-array name="smiley_names">
- <item>"Blij"</item>
- <item>"Bedroefd"</item>
- <item>"Knipoog"</item>
- <item>"Tong uitsteken"</item>
- <item>"Verrast"</item>
- <item>"Kussend"</item>
- <item>"Schreeuwend"</item>
- <item>"Cool"</item>
- <item>"Geldzoeker"</item>
- <item>"Mond vol tanden"</item>
- <item>"Beschaamd"</item>
- <item>"Engel"</item>
- <item>"Twijfelend"</item>
- <item>"Huilend"</item>
- <item>"Lippen op elkaar"</item>
- <item>"Lachend"</item>
- <item>"Verward"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"Err:502"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"Dodaj kontakt"</string>
- <string name="menu_remove_contact">"Usuń kontakt"</string>
- <string name="menu_block_contact">"Zablokuj"</string>
- <string name="menu_contact_list">"Lista kontaktów"</string>
- <string name="menu_start_chat">"Wyślij wiadomość przez czat"</string>
- <string name="menu_view_profile">"Informacje o znajomym"</string>
- <string name="menu_end_conversation">"Zakończ rozmowę"</string>
- <string name="menu_switch_chats">"Przełącz czaty"</string>
- <string name="menu_insert_smiley">"Wstaw buźkę"</string>
- <string name="sign_up">"Utwórz nowe konto"</string>
- <string name="check_save_password">"Ze względów bezpieczeństwa w przypadku zgubienia lub kradzieży telefonu odwiedź witrynę internetową ze swojego komputera i zmień hasło."</string>
- <string name="buddy_list_title">"Lista kontaktów – <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"Nazwa użytkownika:"</string>
- <string name="ongoing_conversation">"Rozmowy: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="contact_profile_title">"Informacje kontaktowe"</string>
- <string name="presence_available">"Dostępny"</string>
- <string name="add_contact_title">"Dodaj kontakt"</string>
- <string name="input_contact_label">"Widoczna nazwa dodawanej osoby:"</string>
- <string name="invite_label">"Dodaj znajomego"</string>
- <string-array name="smiley_names">
- <item>"Wesoły"</item>
- <item>"Smutny"</item>
- <item>"Mruga"</item>
- <item>"Pokazuje język"</item>
- <item>"Zdziwienie"</item>
- <item>"Całuje"</item>
- <item>"Krzyczy"</item>
- <item>"Na luzie"</item>
- <item>"Pazerny"</item>
- <item>"Nietaktowny"</item>
- <item>"Zakłopotanie"</item>
- <item>"Anioł"</item>
- <item>"Niezdecydowany"</item>
- <item>"Płacze"</item>
- <item>"Milczy jak grób"</item>
- <item>"Śmieje się"</item>
- <item>"Zmieszany"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"Adicionar contacto"</string>
- <string name="menu_remove_contact">"Eliminar contacto"</string>
- <string name="menu_block_contact">"Bloquear"</string>
- <string name="menu_contact_list">"Lista de contactos"</string>
- <string name="menu_start_chat">"Enviar MI"</string>
- <string name="menu_view_profile">"Informações do amigo"</string>
- <string name="menu_end_conversation">"Terminar conversa"</string>
- <string name="menu_switch_chats">"Trocar chats"</string>
- <string name="menu_insert_smiley">"Inserir ícones expressivos"</string>
- <string name="sign_up">"Obter uma nova conta"</string>
- <string name="check_save_password">"Para sua segurança, caso perca o telefone ou este seja roubado, aceda ao Web site no computador e altere a sua palavra-passe."</string>
- <string name="buddy_list_title">"Lista de contactos - <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"Nome de utilizador:"</string>
- <string name="ongoing_conversation">"Conversas (<xliff:g id="NUMBER">%1$d</xliff:g>)"</string>
- <string name="contact_profile_title">"Informações de contacto"</string>
- <string name="presence_available">"Disponível"</string>
- <string name="add_contact_title">"Adicionar contacto"</string>
- <string name="input_contact_label">"Pseudónimo da pessoa que pretende adicionar:"</string>
- <string name="invite_label">"Adicionar amigo"</string>
- <string-array name="smiley_names">
- <item>"Feliz"</item>
- <item>"Triste"</item>
- <item>"A piscar o olho"</item>
- <item>"Língua de fora"</item>
- <item>"Surpreendido"</item>
- <item>"Beijoqueiro"</item>
- <item>"A gritar"</item>
- <item>"Fixe"</item>
- <item>"Interesseiro"</item>
- <item>"Arrependido"</item>
- <item>"Envergonhado"</item>
- <item>"Anjo"</item>
- <item>"Indeciso"</item>
- <item>"Chorão"</item>
- <item>"Boca fechada"</item>
- <item>"Risonho"</item>
- <item>"Confuso"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"Adicionar contato"</string>
- <string name="menu_remove_contact">"Excluir contato"</string>
- <string name="menu_block_contact">"Bloquear"</string>
- <string name="menu_contact_list">"Lista de contatos"</string>
- <string name="menu_start_chat">"Enviar mensagem instantânea"</string>
- <string name="menu_view_profile">"Informações do amigo"</string>
- <string name="menu_end_conversation">"Encerrar conversa"</string>
- <string name="menu_switch_chats">"Alternar bate-papos"</string>
- <string name="menu_insert_smiley">"Inserir emoticons"</string>
- <string name="sign_up">"Obter uma nova conta"</string>
- <string name="check_save_password">"Para a sua segurança, se o seu telefone for perdido ou roubado, acesse o site no seu computador e altere a sua senha."</string>
- <string name="buddy_list_title">"Lista de contatos - <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"Nome de usuário:"</string>
- <string name="ongoing_conversation">"Conversas (<xliff:g id="NUMBER">%1$d</xliff:g>)"</string>
- <string name="contact_profile_title">"Informações de contato"</string>
- <string name="presence_available">"Disponível"</string>
- <string name="add_contact_title">"Adicionar contato"</string>
- <string name="input_contact_label">"Apelido da pessoa que você deseja adicionar:"</string>
- <string name="invite_label">"Adicionar amigo"</string>
- <string-array name="smiley_names">
- <item>"Feliz"</item>
- <item>"Triste"</item>
- <item>"Piscando"</item>
- <item>"Mostrando a língua"</item>
- <item>"Surpreso"</item>
- <item>"Beijando"</item>
- <item>"Gritando"</item>
- <item>"Tranquilo"</item>
- <item>"Louco por dinheiro"</item>
- <item>"Falei besteira"</item>
- <item>"Envergonhado"</item>
- <item>"Anjo"</item>
- <item>"Indeciso"</item>
- <item>"Chorando"</item>
- <item>"Boca fechada"</item>
- <item>"Rindo"</item>
- <item>"Confuso"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"Добавить контакт"</string>
- <string name="menu_remove_contact">"Удалить контакт"</string>
- <string name="menu_block_contact">"Заблокировать"</string>
- <string name="menu_contact_list">"Список контактов"</string>
- <string name="menu_start_chat">"Отправить сообщение чата"</string>
- <string name="menu_view_profile">"О приятеле"</string>
- <string name="menu_end_conversation">"Закончить разговор"</string>
- <string name="menu_switch_chats">"Сменить чат"</string>
- <string name="menu_insert_smiley">"Вставить значки настроений"</string>
- <string name="sign_up">"Получить новый аккаунт"</string>
- <string name="check_save_password">"В случае потери или кражи телефона для обеспечения вашей безопасности перейдите со своего компьютера на веб-сайт и поменяйте пароль."</string>
- <string name="buddy_list_title">"Список контактов – <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"Имя пользователя:"</string>
- <string name="ongoing_conversation">"Разговоров: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="contact_profile_title">"Сведения о контакте"</string>
- <string name="presence_available">"На месте"</string>
- <string name="add_contact_title">"Добавить контакт"</string>
- <string name="input_contact_label">"Псевдоним человека, которого необходимо добавить:"</string>
- <string name="invite_label">"Добавить приятеля"</string>
- <string-array name="smiley_names">
- <item>"Веселый"</item>
- <item>"Грустный"</item>
- <item>"Подмигивает"</item>
- <item>"Показывает язык"</item>
- <item>"Удивление"</item>
- <item>"Поцелуй"</item>
- <item>"Кричит"</item>
- <item>"Крутой"</item>
- <item>"Молчание – золото"</item>
- <item>"В замешательстве"</item>
- <item>"Смущение"</item>
- <item>"Ангел"</item>
- <item>"Нерешительный"</item>
- <item>"Плачет"</item>
- <item>"Не скажу"</item>
- <item>"Смех"</item>
- <item>"Озадаченный"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"Lägg till kontakt"</string>
- <string name="menu_remove_contact">"Ta bort kontakt"</string>
- <string name="menu_block_contact">"Blockera"</string>
- <string name="menu_contact_list">"Kontaktlista"</string>
- <string name="menu_start_chat">"Skicka chattmeddelande"</string>
- <string name="menu_view_profile">"Kompisinfo"</string>
- <string name="menu_end_conversation">"Avsluta konversation"</string>
- <string name="menu_switch_chats">"Byt chatt"</string>
- <string name="menu_insert_smiley">"Infoga uttryckssymboler"</string>
- <string name="sign_up">"Öppna ett nytt konto"</string>
- <string name="check_save_password">"Om din telefon blir stulen besöker du webbplatsen via datorn och ändrar lösenordet. Detta är för din egen säkerhet."</string>
- <string name="buddy_list_title">"Kontaktlista – <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"Användarnamn:"</string>
- <string name="ongoing_conversation">"Konversationer (<xliff:g id="NUMBER">%1$d</xliff:g>)"</string>
- <string name="contact_profile_title">"Kontaktinfo"</string>
- <string name="presence_available">"Tillgänglig"</string>
- <string name="add_contact_title">"Lägg till kontakt"</string>
- <string name="input_contact_label">"Signatur för den du vill lägga till:"</string>
- <string name="invite_label">"Lägg till kompis"</string>
- <string-array name="smiley_names">
- <item>"Glad"</item>
- <item>"Ledsen"</item>
- <item>"Blinkar"</item>
- <item>"Lipar"</item>
- <item>"Förvånad"</item>
- <item>"Pussar"</item>
- <item>"Skriker"</item>
- <item>"Cool"</item>
- <item>"Dollarmun"</item>
- <item>"Bortgjord"</item>
- <item>"Generad"</item>
- <item>"Ängel"</item>
- <item>"Tveksam"</item>
- <item>"Gråter"</item>
- <item>"Hemlis"</item>
- <item>"Skrattar"</item>
- <item>"Förvirrad"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"Kişi Ekle"</string>
- <string name="menu_remove_contact">"Kişiyi Sil"</string>
- <string name="menu_block_contact">"Engelle"</string>
- <string name="menu_contact_list">"Kişi Listesi"</string>
- <string name="menu_start_chat">"IM Gönder"</string>
- <string name="menu_view_profile">"Arkadaş Bilgileri"</string>
- <string name="menu_end_conversation">"İleti Dizisini Sonlandır"</string>
- <string name="menu_switch_chats">"Sohbetler Arasında Geçiş Yap"</string>
- <string name="menu_insert_smiley">"İfade Ekle"</string>
- <string name="sign_up">"Yeni hesap al"</string>
- <string name="check_save_password">"Telefonunuz kaybolur veya çalınırsa, güvenliğiniz için bilgisayarınızdan Web sitesine gidin ve şifrenizi değiştirin."</string>
- <string name="buddy_list_title">"Kişi Listesi - <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"Kullanıcı adı:"</string>
- <string name="ongoing_conversation">"Konuşmalar (<xliff:g id="NUMBER">%1$d</xliff:g>)"</string>
- <string name="contact_profile_title">"Kişi Bilgileri"</string>
- <string name="presence_available">"Müsait"</string>
- <string name="add_contact_title">"Kişi Ekle"</string>
- <string name="input_contact_label">"Eklemek istediğiniz kişinin ekran adı:"</string>
- <string name="invite_label">"Arkadaş Ekle"</string>
- <string-array name="smiley_names">
- <item>"Mutlu"</item>
- <item>"Üzgün"</item>
- <item>"Göz Kırpma"</item>
- <item>"Dil çıkarmış"</item>
- <item>"Şaşırmış"</item>
- <item>"Öpücük"</item>
- <item>"Bağırma"</item>
- <item>"Sakin"</item>
- <item>"Para ağızlı"</item>
- <item>"Ayak ağızda"</item>
- <item>"Utanmış"</item>
- <item>"Melek"</item>
- <item>"Kararsız"</item>
- <item>"Ağlama"</item>
- <item>"Dudakları mühürlü"</item>
- <item>"Gülme"</item>
- <item>"Kafası karışmış"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"添加联系人"</string>
- <string name="menu_remove_contact">"删除联系人"</string>
- <string name="menu_block_contact">"阻止"</string>
- <string name="menu_contact_list">"联系人列表"</string>
- <string name="menu_start_chat">"发送即时消息"</string>
- <string name="menu_view_profile">"好友信息"</string>
- <string name="menu_end_conversation">"结束会话"</string>
- <string name="menu_switch_chats">"切换聊天"</string>
- <string name="menu_insert_smiley">"插入表情符"</string>
- <string name="sign_up">"获取新帐户"</string>
- <string name="check_save_password">"为了您的安全,如果您的手机遗失或被窃,请通过您的计算机访问网站并更改您的密码。"</string>
- <string name="buddy_list_title">"联系人列表 - <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"用户名:"</string>
- <string name="ongoing_conversation">"会话 (<xliff:g id="NUMBER">%1$d</xliff:g>)"</string>
- <string name="contact_profile_title">"联系人信息"</string>
- <string name="presence_available">"有空"</string>
- <string name="add_contact_title">"添加联系人"</string>
- <string name="input_contact_label">"您希望添加的联系人的用户名:"</string>
- <string name="invite_label">"添加好友"</string>
- <string-array name="smiley_names">
- <item>"幸福"</item>
- <item>"悲伤"</item>
- <item>"眨眼"</item>
- <item>"吐舌头"</item>
- <item>"惊讶"</item>
- <item>"亲吻"</item>
- <item>"大喊"</item>
- <item>"酷"</item>
- <item>"财迷"</item>
- <item>"说错了话"</item>
- <item>"尴尬"</item>
- <item>"天使"</item>
- <item>"犹豫"</item>
- <item>"哭泣"</item>
- <item>"保密"</item>
- <item>"大笑"</item>
- <item>"困惑"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">"新增聯絡人"</string>
- <string name="menu_remove_contact">"刪除聯絡人"</string>
- <string name="menu_block_contact">"封鎖"</string>
- <string name="menu_contact_list">"聯絡人清單"</string>
- <string name="menu_start_chat">"傳送即時訊息"</string>
- <string name="menu_view_profile">"好友資訊"</string>
- <string name="menu_end_conversation">"結束會話"</string>
- <string name="menu_switch_chats">"切換即時通訊"</string>
- <string name="menu_insert_smiley">"插入表情符號"</string>
- <string name="sign_up">"取得新帳戶"</string>
- <string name="check_save_password">"為了安全起見,如果電話遺失或遭竊,請使用電腦前往該網站並變更密碼。"</string>
- <string name="buddy_list_title">"聯絡人清單 - <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="label_username">"使用者名稱:"</string>
- <string name="ongoing_conversation">"會話 (<xliff:g id="NUMBER">%1$d</xliff:g>)"</string>
- <string name="contact_profile_title">"聯絡人資訊"</string>
- <string name="presence_available">"可用"</string>
- <string name="add_contact_title">"新增聯絡人"</string>
- <string name="input_contact_label">"要新增之聯絡人在畫面上的名稱:"</string>
- <string name="invite_label">"新增好友"</string>
- <string-array name="smiley_names">
- <item>"開心"</item>
- <item>"傷心"</item>
- <item>"眨眼"</item>
- <item>"吐舌頭"</item>
- <item>"驚訝"</item>
- <item>"紅唇"</item>
- <item>"大喊"</item>
- <item>"酷"</item>
- <item>"滿嘴錢"</item>
- <item>"說錯話"</item>
- <item>"害羞"</item>
- <item>"天使"</item>
- <item>"還沒決定"</item>
- <item>"嚎啕大哭"</item>
- <item>"不要告訴別人"</item>
- <item>"開懷大笑"</item>
- <item>"疑惑"</item>
- </string-array>
- <string-array name="smiley_texts">
- <item>":-)"</item>
- <item>":-("</item>
- <item>";-)"</item>
- <item>":-P"</item>
- <item>"=-O"</item>
- <item>":-*"</item>
- <item>":O"</item>
- <item>"B-)"</item>
- <item>":-$"</item>
- <item>":-!"</item>
- <item>":-["</item>
- <item>"O:-)"</item>
- <item>":-\\"</item>
- <item>":\'("</item>
- <item>":-X"</item>
- <item>":-D"</item>
- <item>"o_O"</item>
- </string-array>
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright (C) 2008 Esmertec Inc.
- * 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.
- */
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="menu_add_contact">Add Contact</string>
- <string name="menu_remove_contact">Delete Contact</string>
- <string name="menu_block_contact">Block</string>
- <string name="menu_contact_list">Contact List</string>
- <string name="menu_start_chat">Send IM</string>
- <string name="menu_view_profile">Buddy Info</string>
- <string name="menu_end_conversation">End Conversation</string>
- <string name="menu_switch_chats">Switch Chats</string>
- <string name="menu_insert_smiley">Insert Emoticons</string>
- <string name="sign_up">Get a new account</string>
- <string name="check_save_password">For your security, if your phone is lost or stolen, go to the Web site on your computer and change your password.</string>
- <string name="buddy_list_title">Contact List - <xliff:g id="username">%1$s</xliff:g></string>
- <string name="label_username">Username:</string>
- <string name="ongoing_conversation">Conversations (<xliff:g id="number">%1$d</xliff:g>)</string>
- <string name="contact_profile_title">Contact Info</string>
- <string name="presence_available">Available</string>
- <string name="add_contact_title">Add Contact</string>
- <string name="input_contact_label">Screen name of person you wish to add:</string>
- <string name="invite_label">Add Buddy</string>
-
- <string-array name="smiley_names">
- <item>Happy</item>
- <item>Sad</item>
- <item>Winking</item>
- <item>Tongue sticking out</item>
- <item>Surprised</item>
- <item>Kissing</item>
- <item>Yelling</item>
- <item>Cool</item>
- <item>Money mouth</item>
- <item>Foot in mouth</item>
- <item>Embarrassed</item>
- <item>Angel</item>
- <item>Undecided</item>
- <item>Crying</item>
- <item>Lips are sealed</item>
- <item>Laughing</item>
- <item>Confused</item>
- </string-array>
-
- <string-array name="smiley_texts">
- <item>:-)</item>
- <item>:-(</item>
- <item>;-)</item>
- <item>:-P</item>
- <item>=-O</item>
- <item>:-*</item>
- <item>:O</item>
- <item>B-)</item>
- <item>:-$</item>
- <item>:-!</item>
- <item>:-[</item>
- <item>O:-)</item>
- <item>:-\\</item>
- <item>:\'(</item>
- <item>:-X</item>
- <item>:-D</item>
- <item>o_O</item>
- </string-array>
-</resources>
package com.android.im.plugin.demo;
import com.android.im.plugin.BrandingResourceIDs;
-import com.android.im.plugin.IImPlugin;
+import com.android.im.plugin.ImPlugin;
import com.android.im.plugin.ImConfigNames;
import com.android.im.plugin.ImpsConfigNames;
* Simple example of writing a plug-in for the IM application.
*
*/
-public class DemoImPlugin extends Service {
+public class DemoImPlugin extends Service implements ImPlugin {
@Override
public IBinder onBind(Intent intent) {
- return mBinder;
+ return null;
}
- /**
- * The implementation of IImPlugin defined through AIDL.
- */
- private IBinder mBinder = new IImPlugin.Stub() {
- public Map getProviderConfig() {
- HashMap<String, String> config = new HashMap<String, String>();
- // The protocol name MUST be IMPS now.
- config.put(ImConfigNames.PROTOCOL_NAME, "IMPS");
- config.put(ImpsConfigNames.HOST, "http://xxx.xxxx.xxx");
- config.put(ImpsConfigNames.CLIENT_ID, "Jimmy");
- config.put(ImpsConfigNames.DATA_CHANNEL, "HTTP");
- config.put(ImpsConfigNames.DATA_ENCODING, "WBXML");
- config.put(ImpsConfigNames.CIR_CHANNEL, "STCP");
- config.put(ImpsConfigNames.CUSTOM_PRESENCE_MAPPING,
- "com.android.im.plugin.demo.DemoPresenceMapping");
- return config;
- }
+ public Map getProviderConfig() {
+ HashMap<String, String> config = new HashMap<String, String>();
+ // The protocol name MUST be IMPS now.
+ config.put(ImConfigNames.PROTOCOL_NAME, "IMPS");
+ config.put(ImpsConfigNames.HOST, "http://xxx.xxxx.xxx");
+ config.put(ImpsConfigNames.CLIENT_ID, "Jimmy");
+ config.put(ImpsConfigNames.DATA_CHANNEL, "HTTP");
+ config.put(ImpsConfigNames.DATA_ENCODING, "WBXML");
+ config.put(ImpsConfigNames.CIR_CHANNEL, "STCP");
+ config.put(ImpsConfigNames.CUSTOM_PRESENCE_MAPPING,
+ "com.android.im.plugin.demo.DemoPresenceMapping");
+ return config;
+ }
- public Map getResourceMap() throws RemoteException {
- HashMap<Integer, Integer> resMapping = new HashMap<Integer, Integer>();
- resMapping.put(BrandingResourceIDs.DRAWABLE_LOGO, R.drawable.im_logo);
- resMapping.put(BrandingResourceIDs.DRAWABLE_PRESENCE_ONLINE,
- android.R.drawable.presence_online);
- resMapping.put(BrandingResourceIDs.DRAWABLE_PRESENCE_AWAY,
- android.R.drawable.presence_away);
- resMapping.put(BrandingResourceIDs.DRAWABLE_PRESENCE_BUSY,
- android.R.drawable.presence_busy);
- resMapping.put(BrandingResourceIDs.DRAWABLE_PRESENCE_INVISIBLE,
- android.R.drawable.presence_invisible);
- resMapping.put(BrandingResourceIDs.DRAWABLE_PRESENCE_OFFLINE,
- android.R.drawable.presence_offline);
- resMapping.put(BrandingResourceIDs.DRAWABLE_SPLASH_SCREEN,
- R.drawable.im_logo_splashscr);
- resMapping.put(BrandingResourceIDs.DRAWABLE_READ_CHAT,
- R.drawable.chat);
- resMapping.put(BrandingResourceIDs.DRAWABLE_UNREAD_CHAT,
- R.drawable.chat_new);
+ public Map getResourceMap() {
+ HashMap<Integer, Integer> resMapping = new HashMap<Integer, Integer>();
+/* resMapping.put(BrandingResourceIDs.DRAWABLE_LOGO, R.drawable.im_logo);
+ resMapping.put(BrandingResourceIDs.DRAWABLE_PRESENCE_ONLINE,
+ android.R.drawable.presence_online);
+ resMapping.put(BrandingResourceIDs.DRAWABLE_PRESENCE_AWAY,
+ android.R.drawable.presence_away);
+ resMapping.put(BrandingResourceIDs.DRAWABLE_PRESENCE_BUSY,
+ android.R.drawable.presence_busy);
+ resMapping.put(BrandingResourceIDs.DRAWABLE_PRESENCE_INVISIBLE,
+ android.R.drawable.presence_invisible);
+ resMapping.put(BrandingResourceIDs.DRAWABLE_PRESENCE_OFFLINE,
+ android.R.drawable.presence_offline);
+ resMapping.put(BrandingResourceIDs.DRAWABLE_SPLASH_SCREEN,
+ R.drawable.im_logo_splashscr);
+ resMapping.put(BrandingResourceIDs.DRAWABLE_READ_CHAT,
+ R.drawable.chat);
+ resMapping.put(BrandingResourceIDs.DRAWABLE_UNREAD_CHAT,
+ R.drawable.chat_new);
- resMapping.put(BrandingResourceIDs.STRING_BUDDY_LIST_TITLE,
- R.string.buddy_list_title);
- resMapping.put(BrandingResourceIDs.STRING_ARRAY_SMILEY_NAMES,
- R.array.smiley_names);
- resMapping.put(BrandingResourceIDs.STRING_ARRAY_SMILEY_TEXTS,
- R.array.smiley_texts);
- resMapping.put(BrandingResourceIDs.STRING_PRESENCE_AVAILABLE,
- R.string.presence_available);
+ resMapping.put(BrandingResourceIDs.STRING_BUDDY_LIST_TITLE,
+ R.string.buddy_list_title);
+ resMapping.put(BrandingResourceIDs.STRING_ARRAY_SMILEY_NAMES,
+ R.array.smiley_names);
+ resMapping.put(BrandingResourceIDs.STRING_ARRAY_SMILEY_TEXTS,
+ R.array.smiley_texts);
+ resMapping.put(BrandingResourceIDs.STRING_PRESENCE_AVAILABLE,
+ R.string.presence_available);
- resMapping.put(BrandingResourceIDs.STRING_LABEL_USERNAME,
- R.string.label_username);
- resMapping.put(BrandingResourceIDs.STRING_ONGOING_CONVERSATION,
- R.string.ongoing_conversation);
- resMapping.put(BrandingResourceIDs.STRING_ADD_CONTACT_TITLE,
- R.string.add_contact_title);
- resMapping.put(BrandingResourceIDs.STRING_LABEL_INPUT_CONTACT,
- R.string.input_contact_label);
- resMapping.put(BrandingResourceIDs.STRING_BUTTON_ADD_CONTACT,
- R.string.invite_label);
- resMapping.put(BrandingResourceIDs.STRING_CONTACT_INFO_TITLE,
- R.string.contact_profile_title);
+ resMapping.put(BrandingResourceIDs.STRING_LABEL_USERNAME,
+ R.string.label_username);
+ resMapping.put(BrandingResourceIDs.STRING_ONGOING_CONVERSATION,
+ R.string.ongoing_conversation);
+ resMapping.put(BrandingResourceIDs.STRING_ADD_CONTACT_TITLE,
+ R.string.add_contact_title);
+ resMapping.put(BrandingResourceIDs.STRING_LABEL_INPUT_CONTACT,
+ R.string.input_contact_label);
+ resMapping.put(BrandingResourceIDs.STRING_BUTTON_ADD_CONTACT,
+ R.string.invite_label);
+ resMapping.put(BrandingResourceIDs.STRING_CONTACT_INFO_TITLE,
+ R.string.contact_profile_title);
- resMapping.put(BrandingResourceIDs.STRING_MENU_ADD_CONTACT,
- R.string.menu_add_contact);
- resMapping.put(BrandingResourceIDs.STRING_MENU_BLOCK_CONTACT,
- R.string.menu_block_contact);
- resMapping.put(BrandingResourceIDs.STRING_MENU_CONTACT_LIST,
- R.string.menu_contact_list);
- resMapping.put(BrandingResourceIDs.STRING_MENU_DELETE_CONTACT,
- R.string.menu_remove_contact);
- resMapping.put(BrandingResourceIDs.STRING_MENU_END_CHAT,
- R.string.menu_end_conversation);
- resMapping.put(BrandingResourceIDs.STRING_MENU_INSERT_SMILEY,
- R.string.menu_insert_smiley);
- resMapping.put(BrandingResourceIDs.STRING_MENU_START_CHAT,
- R.string.menu_start_chat);
- resMapping.put(BrandingResourceIDs.STRING_MENU_VIEW_PROFILE,
- R.string.menu_view_profile);
- resMapping.put(BrandingResourceIDs.STRING_MENU_SWITCH_CHATS,
- R.string.menu_switch_chats);
+ resMapping.put(BrandingResourceIDs.STRING_MENU_ADD_CONTACT,
+ R.string.menu_add_contact);
+ resMapping.put(BrandingResourceIDs.STRING_MENU_BLOCK_CONTACT,
+ R.string.menu_block_contact);
+ resMapping.put(BrandingResourceIDs.STRING_MENU_CONTACT_LIST,
+ R.string.menu_contact_list);
+ resMapping.put(BrandingResourceIDs.STRING_MENU_DELETE_CONTACT,
+ R.string.menu_remove_contact);
+ resMapping.put(BrandingResourceIDs.STRING_MENU_END_CHAT,
+ R.string.menu_end_conversation);
+ resMapping.put(BrandingResourceIDs.STRING_MENU_INSERT_SMILEY,
+ R.string.menu_insert_smiley);
+ resMapping.put(BrandingResourceIDs.STRING_MENU_START_CHAT,
+ R.string.menu_start_chat);
+ resMapping.put(BrandingResourceIDs.STRING_MENU_VIEW_PROFILE,
+ R.string.menu_view_profile);
+ resMapping.put(BrandingResourceIDs.STRING_MENU_SWITCH_CHATS,
+ R.string.menu_switch_chats);
- resMapping.put(BrandingResourceIDs.STRING_TOAST_CHECK_SAVE_PASSWORD,
- R.string.check_save_password);
- resMapping.put(BrandingResourceIDs.STRING_LABEL_SIGN_UP,
- R.string.sign_up);
- return resMapping;
- }
+ resMapping.put(BrandingResourceIDs.STRING_TOAST_CHECK_SAVE_PASSWORD,
+ R.string.check_save_password);
+ resMapping.put(BrandingResourceIDs.STRING_LABEL_SIGN_UP,
+ R.string.sign_up);*/
+ return resMapping;
+ }
- public int[] getSmileyIconIds() throws RemoteException {
- return SMILEY_RES_IDS;
- }
- };
+ public int[] getSmileyIconIds() {
+ return SMILEY_RES_IDS;
+ }
/**
* An array of the smiley icon IDs. Note that the sequence of the array MUST
* match the smiley texts and smiley names defined in strings.xml.
*/
static final int[] SMILEY_RES_IDS = {
- R.drawable.emo_im_happy,
+/* R.drawable.emo_im_happy,
R.drawable.emo_im_sad,
R.drawable.emo_im_winking,
R.drawable.emo_im_tongue_sticking_out,
R.drawable.emo_im_crying,
R.drawable.emo_im_lips_are_sealed,
R.drawable.emo_im_laughing,
- R.drawable.emo_im_wtf
+ R.drawable.emo_im_wtf */
};
}
package com.android.im.plugin.demo;
import com.android.im.plugin.ImPluginConstants;
-import com.android.im.plugin.IPresenceMapping;
+import com.android.im.plugin.PresenceMapping;
import java.util.Map;
* A simple implementation of PresenceMaping for the provider.
*
*/
-public class DemoPresenceMapping extends IPresenceMapping.Stub {
+public class DemoPresenceMapping implements PresenceMapping {
public int[] getSupportedPresenceStatus() {
return new int[] {
package com.android.im.app;
-import com.android.im.plugin.IImPlugin;
+import com.android.im.plugin.ImPlugin;
import com.android.im.plugin.ImPluginInfo;
import dalvik.system.PathClassLoader;
// Load the plug-in directly from the apk instead of binding the service
// and calling through the IPC binder API. It's more effective in this way
// and we can avoid the async behaviors of binding service.
- PathClassLoader classLoader = new PathClassLoader(pluginInfo.mSrcPath,
- context.getClassLoader());
+ ClassLoader classLoader = context.getClassLoader();
try {
Class cls = classLoader.loadClass(pluginInfo.mClassName);
Method m = cls.getMethod("onBind", Intent.class);
- IImPlugin plugin = (IImPlugin)m.invoke(cls.newInstance(), new Object[]{null});
+ ImPlugin plugin = (ImPlugin)m.invoke(cls.newInstance(), new Object[]{null});
mResMapping = plugin.getResourceMap();
mSmileyIcons = plugin.getSmileyIconIds();
} catch (ClassNotFoundException e) {
Log.e(TAG, "Failed load the plugin resource map", e);
} catch (InvocationTargetException e) {
Log.e(TAG, "Failed load the plugin resource map", e);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed load the plugin resource map", e);
}
}
*/
public BrandingResources(Context context, Map<Integer, Integer> resMapping,
BrandingResources defaultRes) {
- mPackageRes = context.getResources();
+ this(context.getResources(), resMapping, null, defaultRes);
+ }
+
+ public BrandingResources(Resources packageRes, Map<Integer, Integer> resMapping,
+ int[] smileyIcons, BrandingResources defaultRes) {
+ mPackageRes = packageRes;
mResMapping = resMapping;
+ mSmileyIcons = smileyIcons;
mDefaultRes = defaultRes;
}
+++ /dev/null
-/*
- * Copyright (C) 2008 Esmertec AG.
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.im.app;
-
-import com.android.im.plugin.ImConfigNames;
-import com.android.im.plugin.ImPluginConstants;
-
-import android.app.Service;
-import android.content.Intent;
-import android.content.ContentUris;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.im.IImPlugin;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.Bundle;
-import android.provider.Im;
-import android.util.Log;
-import android.text.TextUtils;
-import android.database.Cursor;
-import android.net.Uri;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.List;
-import java.util.ArrayList;
-import java.lang.reflect.Method;
-import java.lang.reflect.InvocationTargetException;
-
-import dalvik.system.PathClassLoader;
-
-
-public class FrontDoorPlugin extends Service {
- private final static String TAG = ImApp.LOG_TAG;
- private final static boolean LOCAL_DEBUG = false;
-
- // database access constants for branding resource map cache table
- private final static String[] BRANDING_RESOURCE_MAP_CACHE_PROJECTION = {
- Im.BrandingResourceMapCache.PROVIDER_ID,
- Im.BrandingResourceMapCache.APP_RES_ID,
- Im.BrandingResourceMapCache.PLUGIN_RES_ID
- };
- private final static int BRANDING_RESOURCE_MAP_CACHE_PROVIDER_ID_COLUMN = 0;
- private final static int BRANDING_RESOURCE_MAP_CACHE_APP_RES_ID_COLUMN = 1;
- private final static int BRANDING_RESOURCE_MAP_CACHE_PLUGIN_RES_ID_COLUMN = 2;
-
- private ArrayList<String> mProviderNames;
- private HashMap<String, String> mPackageNames;
- private HashMap<String, Map<Integer, Integer>> mBrandingResources;
-
- @Override
- public IBinder onBind(Intent intent) {
- // temporary mappings
- HashMap<String, Long> providerNameToId = new HashMap<String, Long>();
- HashMap<Long, String> providerIdToName = new HashMap<Long, String>();
- HashMap<String, Class> classes = new HashMap<String, Class>();
-
- loadThirdPartyPlugins(providerNameToId, providerIdToName, classes);
- loadBrandingResources(providerNameToId, providerIdToName, classes);
-
- return mBinder;
- }
-
- private void loadThirdPartyPlugins(
- HashMap<String, Long> providerNameToId, HashMap<Long, String> providerIdToName,
- HashMap<String, Class> classes) {
- mProviderNames = new ArrayList<String>();
- mPackageNames = new HashMap<String, String>();
-
- PackageManager pm = getPackageManager();
- List<ResolveInfo> plugins = pm.queryIntentServices(
- new Intent(ImPluginConstants.PLUGIN_ACTION_NAME), PackageManager.GET_META_DATA);
- int numPlugins = plugins.size();
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- log("loadThirdPartyPlugins: # plugins found: " + numPlugins);
- }
-
- if (numPlugins == 0) {
- Log.e(TAG, "[IM.FrontDoorPlugin] no plugins found! bail...");
- return;
- }
-
- for (ResolveInfo info : plugins) {
- if (Log.isLoggable(TAG, Log.DEBUG)) log("loadThirdPartyPlugins: found plugin " + info);
-
- ServiceInfo serviceInfo = info.serviceInfo;
- if (serviceInfo == null) {
- Log.e(TAG, "[FrontDoorPlugin] loadThirdPartyPlugins: ignore bad plugin: " + info);
- continue;
- }
-
- String providerName = null;
- String providerFullName = null;
- String signUpUrl = null;
- Bundle metaData = serviceInfo.metaData;
- if (metaData != null) {
- providerName = metaData.getString(ImPluginConstants.METADATA_PROVIDER_NAME);
- providerFullName =
- metaData.getString(ImPluginConstants.METADATA_PROVIDER_FULL_NAME);
- signUpUrl = metaData.getString(ImPluginConstants.METADATA_SIGN_UP_URL);
- }
- if (TextUtils.isEmpty(providerName) || TextUtils.isEmpty(providerFullName)) {
- Log.e(TAG, "[FrontDoorPlugin] Ignore bad IM plugin: " + info +
- ". Lack of required meta data");
- continue;
- }
-
- mProviderNames.add(providerName);
- mPackageNames.put(providerName, serviceInfo.packageName);
-
- String className = serviceInfo.name;
- String srcPath = serviceInfo.applicationInfo.sourceDir;
- Class pluginClass = loadClass(className, srcPath);
- if (pluginClass == null) {
- Log.e(TAG, "[FrontDoorPlugin] Can not load package for plugin " + providerName);
- continue;
- }
- classes.put(providerName, pluginClass);
-
- Map<String, String> config = loadProviderConfigFromPlugin(pluginClass);
- if (config == null) {
- Log.e(TAG, "[FrontDoorPlugin] Can not load config for plugin " + providerName);
- continue;
- }
- config.put(ImConfigNames.PLUGIN_PATH, srcPath);
- config.put(ImConfigNames.PLUGIN_CLASS, className);
-
- long providerId = DatabaseUtils.updateProviderDb(getContentResolver(),
- providerName, providerFullName, signUpUrl, config);
- providerNameToId.put(providerName, providerId);
- providerIdToName.put(providerId, providerName);
- }
- }
-
- private void loadBrandingResources(
- HashMap<String, Long> providerNameToId, HashMap<Long, String> providerIdToName,
- HashMap<String, Class> classes) {
- mBrandingResources = new HashMap<String, Map<Integer, Integer>>();
-
- // first try load from cache
- loadBrandingResourcesFromCache(providerIdToName);
-
- // check and load any un-cached resources
- final ArrayList<ContentValues> valuesList = new ArrayList<ContentValues>();
- for (String provider : mProviderNames) {
- long providerId = providerNameToId.get(provider);
- if (!mBrandingResources.containsKey(provider)) {
- Map<Integer, Integer> resMap = loadBrandingResource(classes.get(provider));
- if (resMap != null) {
- mBrandingResources.put(provider, resMap);
- for (int appResId : resMap.keySet()) {
- int pluginResId = resMap.get(appResId);
-
- ContentValues values = new ContentValues();
- values.put(Im.BrandingResourceMapCache.PROVIDER_ID, providerId);
- values.put(Im.BrandingResourceMapCache.APP_RES_ID, appResId);
- values.put(Im.BrandingResourceMapCache.PLUGIN_RES_ID, pluginResId);
-
- valuesList.add(values);
- }
- Log.d(TAG, "Plugin " + provider + " not in cache, loaded and saved");
- }
- }
- }
-
- // save the changes to cache
- if (valuesList.size() > 0) {
- new Thread(new Runnable() {
- public void run() {
- getContentResolver().bulkInsert(
- Im.BrandingResourceMapCache.CONTENT_URI,
- valuesList.toArray(new ContentValues[]{}));
- }
- }).start();
- }
- }
-
- /**
- * Try loading the branding resources from the database.
- * @param providerIdToName a map between provider ID and name.
- */
- private void loadBrandingResourcesFromCache(HashMap<Long, String> providerIdToName) {
- ContentResolver cr = getContentResolver();
- Cursor c = cr.query(
- Im.BrandingResourceMapCache.CONTENT_URI, /* URI */
- BRANDING_RESOURCE_MAP_CACHE_PROJECTION, /* projection */
- null, /* where */
- null, /* where args */
- null /* sort */);
-
- if (c != null) {
- try {
- while (c.moveToNext()) {
- long providerId = c.getLong(BRANDING_RESOURCE_MAP_CACHE_PROVIDER_ID_COLUMN);
- String provider = providerIdToName.get(providerId);
- if (TextUtils.isEmpty(provider)) {
- Log.e(TAG, "Empty provider name in branding resource map cache table.");
- continue;
- }
- int appResId = c.getInt(BRANDING_RESOURCE_MAP_CACHE_APP_RES_ID_COLUMN);
- int pluginResId = c.getInt(BRANDING_RESOURCE_MAP_CACHE_PLUGIN_RES_ID_COLUMN);
-
- Map<Integer, Integer> resMap = mBrandingResources.get(provider);
- if (resMap == null) {
- resMap = new HashMap<Integer, Integer>();
- mBrandingResources.put(provider, resMap);
- }
-
- resMap.put(appResId, pluginResId);
- }
- } finally {
- c.close();
- }
- } else {
- Log.e(TAG, "Query of branding resource map cache table returns empty cursor");
- }
- }
-
- /**
- * Load branding resources from one plugin.
- */
- private Map<Integer, Integer> loadBrandingResource(Class cls) {
- try {
- Method m = cls.getMethod("getResourceMap");
- // TODO: this would still cause a VM verifier exception to be thrown if.
- // the landing page Android.mk and AndroidManifest.xml don't include use-library for
- // "com.android.im.plugin". This is even with getCustomClassLoader() as the parent
- // class loader.
- return (Map)m.invoke(cls.newInstance(), new Object[]{});
-
- } catch (IllegalAccessException e) {
- Log.e(TAG, "Failed load the plugin resource map", e);
- } catch (InstantiationException e) {
- Log.e(TAG, "Failed load the plugin resource map", e);
- } catch (SecurityException e) {
- Log.e(TAG, "Failed load the plugin resource map", e);
- } catch (NoSuchMethodException e) {
- Log.e(TAG, "Failed load the plugin resource map", e);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Failed load the plugin resource map", e);
- } catch (InvocationTargetException e) {
- Log.e(TAG, "Failed load the plugin resource map", e);
- }
- return null;
- }
-
- /**
- * Load plugin config.
- */
- private Map<String, String> loadProviderConfigFromPlugin(Class cls) {
- try {
- Method m = cls.getMethod("onBind", Intent.class);
- com.android.im.plugin.IImPlugin plugin =
- (com.android.im.plugin.IImPlugin)m.invoke(cls.newInstance(), new Object[]{null});
- return plugin.getProviderConfig();
- } catch (IllegalAccessException e) {
- Log.e(TAG, "Could not create plugin instance", e);
- } catch (InstantiationException e) {
- Log.e(TAG, "Could not create plugin instance", e);
- } catch (SecurityException e) {
- Log.e(TAG, "Could not load config from the plugin", e);
- } catch (NoSuchMethodException e) {
- Log.e(TAG, "Could not load config from the plugin", e);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Could not load config from the plugin", e);
- } catch (InvocationTargetException e) {
- Log.e(TAG, "Could not load config from the plugin", e);
- } catch (RemoteException e) {
- Log.e(TAG, "Could not load config from the plugin", e);
- }
- return null;
- }
-
- private Class loadClass(String className, String srcPath) {
- PathClassLoader loader = new PathClassLoader(srcPath, getClassLoader());
- try {
- return loader.loadClass(className);
- } catch (ClassNotFoundException e) {
- Log.e(TAG, "Could not find plugin class", e);
- }
- return null;
- }
-
- private void log(String msg) {
- Log.d(TAG, "[ImFrontDoor] " + msg);
- }
-
-
- /**
- * The implementation of IImFrontDoorPlugin defined through AIDL.
- */
- private final IImPlugin.Stub mBinder = new IImPlugin.Stub() {
-
- /**
- * Notify the plugin the front door activity is created. This gives the plugin a chance to
- * start its own servics, etc.
- */
- public void onStart() {
- }
-
- /**
- * Notify the plugin the front door activity is stopping.
- */
- public void onStop() {
- }
-
- /**
- * Sign in to the service for the account passed in.
- */
- public void signIn(long account) {
- if (LOCAL_DEBUG) log("signIn for account " + account);
-
- Intent intent = new Intent();
- intent.setData(ContentUris.withAppendedId(Im.Account.CONTENT_URI, account));
- intent.setClassName("com.android.im", "com.android.im.app.SigningInActivity");
-
- startActivity(intent);
- }
-
- /**
- * Sign out of the service for the account passed in.
- */
- public void signOut(long account) {
- if (LOCAL_DEBUG) log("signOut for account " + account);
- Intent intent = new Intent();
- intent.setData(ContentUris.withAppendedId(Im.Account.CONTENT_URI, account));
- intent.setClassName("com.android.im", "com.android.im.app.SignoutActivity");
-
- startActivity(intent);
- }
-
- public String getResourcePackageNameForProvider(String providerName) {
- return mPackageNames.get(providerName);
- }
-
- public Map getResourceMapForProvider(String providerName) throws RemoteException {
- return mBrandingResources.get(providerName);
- }
-
- public List getSupportedProviders() {
- return mProviderNames;
- }
- };
-
-}
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.ConnectivityManager;
import com.android.im.engine.ImConnection;
import com.android.im.engine.ImErrorInfo;
import com.android.im.plugin.BrandingResourceIDs;
+import com.android.im.plugin.ImPlugin;
import com.android.im.plugin.ImPluginConstants;
import com.android.im.plugin.ImPluginInfo;
import com.android.im.service.ImServiceConstants;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
public class ImApp extends Application {
public static final String LOG_TAG = "ImApp";
super.onCreate();
mBroadcaster = new Broadcaster();
loadDefaultBrandingRes();
- mBrandingResources = new HashMap<String, BrandingResources>();
- loadThirdPartyResources();
}
@Override
if (mProviders != null) {
return;
}
-
+
mProviders = new HashMap<Long, ProviderDef>();
ContentResolver cr = getContentResolver();
}
private void loadThirdPartyResources() {
+ ImPluginHelper helper = ImPluginHelper.getInstance(this);
+ helper.loadAvaiablePlugins();
+ ArrayList<ImPlugin> pluginList = helper.getPluginObjects();
+ ArrayList<ImPluginInfo> infoList = helper.getPluginsInfo();
+ int N = pluginList.size();
PackageManager pm = getPackageManager();
- List<ResolveInfo> plugins = pm.queryIntentServices(
- new Intent(ImPluginConstants.PLUGIN_ACTION_NAME), PackageManager.GET_META_DATA);
- for (ResolveInfo info : plugins) {
- Log.d(LOG_TAG, "Found plugin " + info);
-
- ServiceInfo serviceInfo = info.serviceInfo;
- if (serviceInfo == null) {
- Log.e(LOG_TAG, "Ignore bad IM plugin: " + info);
- continue;
- }
- String providerName = null;
- String providerFullName = null;
- Bundle metaData = serviceInfo.metaData;
- if (metaData != null) {
- providerName = metaData.getString(
- ImPluginConstants.METADATA_PROVIDER_NAME);
- providerFullName = metaData.getString(
- ImPluginConstants.METADATA_PROVIDER_FULL_NAME);
- }
- if (TextUtils.isEmpty(providerName)
- || TextUtils.isEmpty(providerFullName)) {
- Log.e(LOG_TAG, "Ignore bad IM plugin: " + info
- + ". Lack of required meta data");
- continue;
- }
+ for (int i = 0; i < N; i++) {
+ ImPlugin plugin = pluginList.get(i);
+ ImPluginInfo pluginInfo = infoList.get(i);
+
+ try {
+ Resources packageRes = pm.getResourcesForApplication(pluginInfo.mPackageName);
- ImPluginInfo pluginInfo = new ImPluginInfo(providerName,
- serviceInfo.packageName, serviceInfo.name,
- serviceInfo.applicationInfo.sourceDir);
+ Map<Integer, Integer> resMap = plugin.getResourceMap();
+ int[] smileyIcons = plugin.getSmileyIconIds();
- BrandingResources res = new BrandingResources(this, pluginInfo,
- mDefaultBrandingResources);
- mBrandingResources.put(providerName, res);
+ BrandingResources res = new BrandingResources(packageRes, resMap,
+ smileyIcons, mDefaultBrandingResources);
+ mBrandingResources.put(pluginInfo.mProviderName, res);
+ } catch (NameNotFoundException e) {
+ Log.e(LOG_TAG, "Failed to load third party resources.", e);
+ }
}
}
if (provider == null) {
return mDefaultBrandingResources;
}
+ if (mBrandingResources == null) {
+ mBrandingResources = new HashMap<String, BrandingResources>();
+ loadThirdPartyResources();
+ }
BrandingResources res = mBrandingResources.get(provider.mName);
return res == null ? mDefaultBrandingResources : res;
}
case ImConnection.LOGGING_OUT:
what = EVENT_CONNECTION_LOGGING_OUT;
+ synchronized (mConnections) {
+ mConnections.remove(providerId);
+ }
break;
case ImConnection.DISCONNECTED:
--- /dev/null
+/*
+ * Copyright (C) 2009 Myriad Group AG.
+ * 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.im.app;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteFullException;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Im;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.im.plugin.ImConfigNames;
+import com.android.im.plugin.ImPlugin;
+import com.android.im.plugin.ImPluginConstants;
+import com.android.im.plugin.ImPluginInfo;
+
+public class ImPluginHelper {
+
+ private static final String TAG = "ImPluginUtils";
+
+ private Context mContext;
+ private ArrayList<ImPluginInfo> mPluginsInfo;
+ private ArrayList<ImPlugin> mPluginObjects;
+ private boolean mLoaded;
+
+ private static ImPluginHelper sInstance;
+ public static ImPluginHelper getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new ImPluginHelper(context);
+ }
+ return sInstance;
+ }
+
+ private ImPluginHelper(Context context) {
+ mContext = context;
+ mPluginsInfo = new ArrayList<ImPluginInfo>();
+ mPluginObjects = new ArrayList<ImPlugin>();
+ }
+
+ public ArrayList<ImPluginInfo> getPluginsInfo() {
+ if (!mLoaded) {
+ loadAvaiablePlugins();
+ }
+ return mPluginsInfo;
+ }
+
+ public ArrayList<ImPlugin> getPluginObjects() {
+ if (!mLoaded) {
+ loadAvaiablePlugins();
+ }
+ return mPluginObjects;
+ }
+
+ public void loadAvaiablePlugins() {
+ if (mLoaded) {
+ return;
+ }
+
+ PackageManager pm = mContext.getPackageManager();
+ List<ResolveInfo> plugins = pm.queryIntentServices(
+ new Intent(ImPluginConstants.PLUGIN_ACTION_NAME), PackageManager.GET_META_DATA);
+ for (ResolveInfo info : plugins) {
+ Log.d(TAG, "Found plugin " + info);
+
+ ServiceInfo serviceInfo = info.serviceInfo;
+ if (serviceInfo == null) {
+ Log.e(TAG, "Ignore bad IM plugin: " + info);
+ continue;
+ }
+ String providerName = null;
+ String providerFullName = null;
+ String signUpUrl = null;
+ Bundle metaData = serviceInfo.metaData;
+ if (metaData != null) {
+ providerName = metaData.getString(ImPluginConstants.METADATA_PROVIDER_NAME);
+ providerFullName = metaData.getString(ImPluginConstants.METADATA_PROVIDER_FULL_NAME);
+ signUpUrl = metaData.getString(ImPluginConstants.METADATA_SIGN_UP_URL);
+ }
+ if (TextUtils.isEmpty(providerName) || TextUtils.isEmpty(providerFullName)) {
+ Log.e(TAG, "Ignore bad IM plugin: " + info + ". Lack of required meta data");
+ continue;
+ }
+
+ if (isPluginDuplicated(providerName)) {
+ Log.e(TAG, "Ignore duplicated IM plugin: " + info);
+ continue;
+ }
+
+ if (!serviceInfo.packageName.equals(mContext.getPackageName())) {
+ Log.e(TAG, "Ignore plugin in package: " + serviceInfo.packageName);
+ continue;
+ }
+ ImPluginInfo pluginInfo = new ImPluginInfo(providerName, serviceInfo.packageName,
+ serviceInfo.name, serviceInfo.applicationInfo.sourceDir);
+
+ ImPlugin plugin = loadPlugin(pluginInfo);
+ if (plugin == null) {
+ Log.e(TAG, "Ignore bad IM plugin");
+ continue;
+ }
+
+ try {
+ updateProviderDb(plugin, pluginInfo,providerFullName, signUpUrl);
+ } catch (SQLiteFullException e) {
+ Log.e(TAG, "Storage full", e);
+ return;
+ }
+ mPluginsInfo.add(pluginInfo);
+ mPluginObjects.add(plugin);
+ }
+ mLoaded = true;
+ }
+
+ private boolean isPluginDuplicated(String providerName) {
+ for (ImPluginInfo plugin : mPluginsInfo) {
+ if (plugin.mProviderName.equals(providerName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private ImPlugin loadPlugin(ImPluginInfo pluginInfo) {
+ // XXX Load the plug-in implementation directly from the apk rather than
+ // binding to the service and call through IPC Binder API. This is much
+ // more effective since we don't need to start the service in other
+ // process. We can not run the plug-in service in the same process as a
+ // local service because that the interface is defined in a shared
+ // library in order to compile the plug-in separately. In this case, the
+ // interface will be loaded by two class loader separately and a
+ // ClassCastException will be thrown if we cast the binder to the
+ // interface.
+ ClassLoader loader = mContext.getClassLoader();
+ try {
+ Class<?> cls = loader.loadClass(pluginInfo.mClassName);
+ return (ImPlugin) cls.newInstance();
+ } catch (ClassNotFoundException e) {
+ Log.e(TAG, "Could not find plugin class", e);
+ } catch (IllegalAccessException e) {
+ Log.e(TAG, "Could not create plugin instance", e);
+ } catch (InstantiationException e) {
+ Log.e(TAG, "Could not create plugin instance", e);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Could not load plugin", e);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Could not load plugin", e);
+ }
+ return null;
+ }
+
+ private long updateProviderDb(ImPlugin plugin, ImPluginInfo info,
+ String providerFullName, String signUpUrl) {
+ Map<String, String> config = loadConfiguration(plugin, info);
+ if (config == null) {
+ return 0;
+ }
+
+ long providerId = 0;
+ ContentResolver cr = mContext.getContentResolver();
+ String where = Im.Provider.NAME + "=?";
+ String[] selectionArgs = new String[]{info.mProviderName};
+ Cursor c = cr.query(Im.Provider.CONTENT_URI,
+ null /* projection */,
+ where,
+ selectionArgs,
+ null /* sort order */);
+
+ boolean pluginChanged;
+ try {
+ if (c.moveToFirst()) {
+ providerId = c.getLong(c.getColumnIndexOrThrow(Im.Provider._ID));
+ pluginChanged = isPluginChanged(cr, providerId, config);
+ if (pluginChanged) {
+ // Update the full name, signup url and category each time when the plugin change
+ // instead of specific version change because this is called only once.
+ // It's ok to update them even the values are not changed.
+ // Note that we don't update the provider name because it's used as
+ // identifier at some place and the plugin should never change it.
+ ContentValues values = new ContentValues(3);
+ values.put(Im.Provider.FULLNAME, providerFullName);
+ values.put(Im.Provider.SIGNUP_URL, signUpUrl);
+ values.put(Im.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
+ Uri uri = ContentUris.withAppendedId(Im.Provider.CONTENT_URI, providerId);
+ cr.update(uri, values, null, null);
+ }
+ } else {
+ ContentValues values = new ContentValues(3);
+ values.put(Im.Provider.NAME, info.mProviderName);
+ values.put(Im.Provider.FULLNAME, providerFullName);
+ values.put(Im.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
+ values.put(Im.Provider.SIGNUP_URL, signUpUrl);
+
+ Uri result = cr.insert(Im.Provider.CONTENT_URI, values);
+ providerId = ContentUris.parseId(result);
+ pluginChanged = true;
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ if (pluginChanged) {
+ // Remove all the old settings
+ cr.delete(ContentUris.withAppendedId(
+ Im.ProviderSettings.CONTENT_URI, providerId),
+ null, /*where*/
+ null /*selectionArgs*/);
+
+ ContentValues[] settingValues = new ContentValues[config.size()];
+
+ int index = 0;
+ for (Map.Entry<String, String> entry : config.entrySet()) {
+ ContentValues settingValue = new ContentValues();
+ settingValue.put(Im.ProviderSettings.PROVIDER, providerId);
+ settingValue.put(Im.ProviderSettings.NAME, entry.getKey());
+ settingValue.put(Im.ProviderSettings.VALUE, entry.getValue());
+ settingValues[index++] = settingValue;
+ }
+ cr.bulkInsert(Im.ProviderSettings.CONTENT_URI, settingValues);
+ }
+
+ return providerId;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Map<String, String> loadConfiguration(ImPlugin plugin,
+ ImPluginInfo info) {
+ Map<String, String> config = null;
+
+ config = plugin.getProviderConfig();
+
+ if (config != null) {
+ config.put(ImConfigNames.PLUGIN_PATH, info.mSrcPath);
+ config.put(ImConfigNames.PLUGIN_CLASS, info.mClassName);
+ }
+ return config;
+ }
+
+ private boolean isPluginChanged(ContentResolver cr, long providerId,
+ Map<String, String> config) {
+ String origVersion = Im.ProviderSettings.getStringValue(cr, providerId,
+ ImConfigNames.PLUGIN_VERSION);
+
+ if (origVersion == null) {
+ return true;
+ }
+ String newVersion = config.get(ImConfigNames.PLUGIN_VERSION);
+ return !origVersion.equals(newVersion);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.im.app;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ListActivity;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.RemoteException;
+import android.provider.Im;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.AdapterView;
+import android.widget.CursorAdapter;
+import android.widget.ListView;
+
+import com.android.im.IImConnection;
+import com.android.im.R;
+import com.android.im.plugin.BrandingResourceIDs;
+
+public class LandingPage extends ListActivity implements View.OnCreateContextMenuListener {
+ private static final String TAG = ImApp.LOG_TAG;
+
+ private static final int ID_SIGN_IN = Menu.FIRST + 1;
+ private static final int ID_SIGN_OUT = Menu.FIRST + 2;
+ private static final int ID_EDIT_ACCOUNT = Menu.FIRST + 3;
+ private static final int ID_REMOVE_ACCOUNT = Menu.FIRST + 4;
+ private static final int ID_SIGN_OUT_ALL = Menu.FIRST + 5;
+ private static final int ID_ADD_ACCOUNT = Menu.FIRST + 6;
+ private static final int ID_VIEW_CONTACT_LIST = Menu.FIRST + 7;
+ private static final int ID_SETTINGS = Menu.FIRST + 8;
+
+ private ProviderAdapter mAdapter;
+ private Cursor mProviderCursor;
+ private ImApp mApp;
+ private SimpleAlertHandler mHandler;
+
+ private static final String[] PROVIDER_PROJECTION = {
+ Im.Provider._ID,
+ Im.Provider.NAME,
+ Im.Provider.FULLNAME,
+ Im.Provider.CATEGORY,
+ Im.Provider.ACTIVE_ACCOUNT_ID,
+ Im.Provider.ACTIVE_ACCOUNT_USERNAME,
+ Im.Provider.ACTIVE_ACCOUNT_PW,
+ Im.Provider.ACTIVE_ACCOUNT_LOCKED,
+ Im.Provider.ACTIVE_ACCOUNT_KEEP_SIGNED_IN,
+ Im.Provider.ACCOUNT_PRESENCE_STATUS,
+ Im.Provider.ACCOUNT_CONNECTION_STATUS,
+ };
+
+ static final int PROVIDER_ID_COLUMN = 0;
+ static final int PROVIDER_NAME_COLUMN = 1;
+ static final int PROVIDER_FULLNAME_COLUMN = 2;
+ static final int PROVIDER_CATEGORY_COLUMN = 3;
+ static final int ACTIVE_ACCOUNT_ID_COLUMN = 4;
+ static final int ACTIVE_ACCOUNT_USERNAME_COLUMN = 5;
+ static final int ACTIVE_ACCOUNT_PW_COLUMN = 6;
+ static final int ACTIVE_ACCOUNT_LOCKED = 7;
+ static final int ACTIVE_ACCOUNT_KEEP_SIGNED_IN = 8;
+ static final int ACCOUNT_PRESENCE_STATUS = 9;
+ static final int ACCOUNT_CONNECTION_STATUS = 10;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ setTitle(R.string.landing_page_title);
+
+ mApp = ImApp.getApplication(this);
+ mHandler = new MyHandler(this);
+
+ ImPluginHelper.getInstance(this).loadAvaiablePlugins();
+
+ mProviderCursor = managedQuery(Im.Provider.CONTENT_URI_WITH_ACCOUNT,
+ PROVIDER_PROJECTION,
+ null /* selection */,
+ null /* selection args */,
+ Im.Provider.DEFAULT_SORT_ORDER);
+ mAdapter = new ProviderAdapter(this, mProviderCursor);
+ setListAdapter(mAdapter);
+
+ registerForContextMenu(getListView());
+ }
+
+
+ @Override
+ protected void onPause() {
+ mHandler.unregisterForBroadcastEvents();
+
+ super.onPause();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ mHandler.registerForBroadcastEvents();
+ }
+
+ private void signIn(long accountId) {
+ if (accountId == 0) {
+ Log.w(TAG, "signIn: account id is 0, bail");
+ return;
+ }
+
+ boolean isAccountEditible = mProviderCursor.getInt(ACTIVE_ACCOUNT_LOCKED) == 0;
+ if (isAccountEditible && mProviderCursor.isNull(ACTIVE_ACCOUNT_PW_COLUMN)) {
+ // no password, edit the account
+ if (Log.isLoggable(TAG, Log.DEBUG)) log("no pw for account " + accountId);
+ Intent intent = getEditAccountIntent();
+ startActivity(intent);
+ return;
+ }
+
+ Intent intent = new Intent(this, SigningInActivity.class);
+ intent.setData(ContentUris.withAppendedId(Im.Account.CONTENT_URI, accountId));
+ startActivity(intent);
+ }
+
+ boolean isSigningIn(Cursor cursor) {
+ int connectionStatus = cursor.getInt(ACCOUNT_CONNECTION_STATUS);
+ return connectionStatus == Im.ConnectionStatus.CONNECTING;
+ }
+
+ private boolean isSignedIn(Cursor cursor) {
+ int connectionStatus = cursor.getInt(ACCOUNT_CONNECTION_STATUS);
+ return connectionStatus == Im.ConnectionStatus.ONLINE;
+ }
+
+ private boolean allAccountsSignedOut() {
+ if(!mProviderCursor.moveToFirst()) {
+ return false;
+ }
+ do {
+ if (isSignedIn(mProviderCursor)) {
+ return false;
+ }
+ } while (mProviderCursor.moveToNext()) ;
+
+ return true;
+ }
+
+ private void signoutAll() {
+ DialogInterface.OnClickListener confirmListener
+ = new DialogInterface.OnClickListener(){
+ public void onClick(DialogInterface dialog, int whichButton) {
+ do {
+ long accountId = mProviderCursor.getLong(ACTIVE_ACCOUNT_ID_COLUMN);
+ signOut(accountId);
+ } while (mProviderCursor.moveToNext()) ;
+ }
+ };
+
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.confirm)
+ .setMessage(R.string.signout_all_confirm_message)
+ .setPositiveButton(R.string.yes, confirmListener) // default button
+ .setNegativeButton(R.string.no, null)
+ .setCancelable(true)
+ .show();
+ }
+
+ private void signOut(long accountId) {
+ if (accountId == 0) {
+ Log.w(TAG, "signOut: account id is 0, bail");
+ return;
+ }
+
+ try {
+ IImConnection conn = mApp.getConnectionByAccount(accountId);
+ if (conn != null) {
+ conn.logout();
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, "signOut failed", ex);
+ }
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+ menu.findItem(ID_SIGN_OUT_ALL).setVisible(!allAccountsSignedOut());
+ return true;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, ID_SIGN_OUT_ALL, 0, R.string.menu_sign_out_all)
+ .setIcon(android.R.drawable.ic_menu_close_clear_cancel);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case ID_SIGN_OUT_ALL:
+ signoutAll();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ AdapterView.AdapterContextMenuInfo info;
+ try {
+ info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+ } catch (ClassCastException e) {
+ Log.e(TAG, "bad menuInfo", e);
+ return;
+ }
+
+ Cursor providerCursor = (Cursor) getListAdapter().getItem(info.position);
+ menu.setHeaderTitle(providerCursor.getString(PROVIDER_FULLNAME_COLUMN));
+
+ if (providerCursor.isNull(ACTIVE_ACCOUNT_ID_COLUMN)) {
+ menu.add(0, ID_ADD_ACCOUNT, 0, R.string.menu_add_account);
+ return;
+ }
+
+ long providerId = providerCursor.getLong(PROVIDER_ID_COLUMN);
+ boolean isLoggingIn = isSigningIn(providerCursor);
+ boolean isLoggedIn = isSignedIn(providerCursor);
+
+ BrandingResources brandingRes = mApp.getBrandingResource(providerId);
+ if (!isLoggedIn) {
+ menu.add(0, ID_SIGN_IN, 0, R.string.sign_in).setIcon(com.android.internal.R.drawable.ic_menu_login);
+ } else {
+ menu.add(0, ID_VIEW_CONTACT_LIST, 0,
+ brandingRes.getString(BrandingResourceIDs.STRING_MENU_CONTACT_LIST));
+ menu.add(0, ID_SIGN_OUT, 0, R.string.menu_sign_out)
+ .setIcon(android.R.drawable.ic_menu_close_clear_cancel);
+ }
+
+ boolean isAccountEditible = providerCursor.getInt(ACTIVE_ACCOUNT_LOCKED) == 0;
+ if (isAccountEditible && !isLoggingIn && !isLoggedIn) {
+ menu.add(0, ID_EDIT_ACCOUNT, 0, R.string.menu_edit_account)
+ .setIcon(android.R.drawable.ic_menu_edit);
+ menu.add(0, ID_REMOVE_ACCOUNT, 0, R.string.menu_remove_account)
+ .setIcon(android.R.drawable.ic_menu_delete);
+ }
+
+ // always add a settings menu item
+ menu.add(0, ID_SETTINGS, 0, R.string.menu_settings);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ AdapterView.AdapterContextMenuInfo info;
+ try {
+ info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
+ } catch (ClassCastException e) {
+ Log.e(TAG, "bad menuInfo", e);
+ return false;
+ }
+ long providerId = info.id;
+ Cursor providerCursor = (Cursor) getListAdapter().getItem(info.position);
+ long accountId = providerCursor.getLong(ACTIVE_ACCOUNT_ID_COLUMN);
+
+ switch (item.getItemId()) {
+ case ID_EDIT_ACCOUNT:
+ {
+ startActivity(getEditAccountIntent());
+ return true;
+ }
+
+ case ID_REMOVE_ACCOUNT:
+ {
+ Uri accountUri = ContentUris.withAppendedId(Im.Account.CONTENT_URI, accountId);
+ getContentResolver().delete(accountUri, null, null);
+ // Requery the cursor to force refreshing screen
+ providerCursor.requery();
+ return true;
+ }
+
+ case ID_VIEW_CONTACT_LIST:
+ {
+ Intent intent = getViewContactsIntent();
+ startActivity(intent);
+ return true;
+ }
+ case ID_ADD_ACCOUNT:
+ {
+ startActivity(getCreateAccountIntent());
+ return true;
+ }
+
+ case ID_SIGN_IN:
+ {
+ signIn(accountId);
+ return true;
+ }
+
+ case ID_SIGN_OUT:
+ {
+ // TODO: progress bar
+ signOut(accountId);
+ return true;
+ }
+
+ case ID_SETTINGS:
+ {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Im.ProviderSettings.CONTENT_URI);
+ intent.addCategory(getProviderCategory(providerCursor));
+ intent.putExtra("providerId", providerId);
+ startActivity(intent);
+ return true;
+ }
+
+ }
+
+ return false;
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ Intent intent = null;
+ mProviderCursor.moveToPosition(position);
+
+ if (mProviderCursor.isNull(ACTIVE_ACCOUNT_ID_COLUMN)) {
+ // add account
+ intent = getCreateAccountIntent();
+ } else {
+ int state = mProviderCursor.getInt(ACCOUNT_CONNECTION_STATUS);
+ long accountId = mProviderCursor.getLong(ACTIVE_ACCOUNT_ID_COLUMN);
+
+ if (state == Im.ConnectionStatus.OFFLINE) {
+ boolean isKeepSignedIn = mProviderCursor.getInt(ACTIVE_ACCOUNT_KEEP_SIGNED_IN) != 0;
+ boolean isAccountEditible = mProviderCursor.getInt(ACTIVE_ACCOUNT_LOCKED) == 0;
+ if (isKeepSignedIn) {
+ signIn(accountId);
+ } else if(isAccountEditible) {
+ intent = getEditAccountIntent();
+ }
+ } else if (state == Im.ConnectionStatus.CONNECTING) {
+ signIn(accountId);
+ } else {
+ intent = getViewContactsIntent();
+ }
+ }
+
+ if (intent != null) {
+ startActivity(intent);
+ }
+ }
+
+ Intent getCreateAccountIntent() {
+ Intent intent = new Intent();
+ intent.setAction(Intent.ACTION_INSERT);
+
+ long providerId = mProviderCursor.getLong(PROVIDER_ID_COLUMN);
+ intent.setData(ContentUris.withAppendedId(Im.Provider.CONTENT_URI, providerId));
+ intent.addCategory(getProviderCategory(mProviderCursor));
+ return intent;
+ }
+
+ Intent getEditAccountIntent() {
+ Intent intent = new Intent(Intent.ACTION_EDIT,
+ ContentUris.withAppendedId(Im.Account.CONTENT_URI,
+ mProviderCursor.getLong(ACTIVE_ACCOUNT_ID_COLUMN)));
+ intent.addCategory(getProviderCategory(mProviderCursor));
+ return intent;
+ }
+
+ Intent getViewContactsIntent() {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Im.Contacts.CONTENT_URI);
+ intent.addCategory(getProviderCategory(mProviderCursor));
+ intent.putExtra("accountId", mProviderCursor.getLong(ACTIVE_ACCOUNT_ID_COLUMN));
+ return intent;
+ }
+
+ private String getProviderCategory(Cursor cursor) {
+ return cursor.getString(PROVIDER_CATEGORY_COLUMN);
+ }
+
+ static void log(String msg) {
+ Log.d(TAG, "[LandingPage]" + msg);
+ }
+
+ private class ProviderListItemFactory implements LayoutInflater.Factory {
+ public View onCreateView(String name, Context context, AttributeSet attrs) {
+ if (name != null && name.equals(ProviderListItem.class.getName())) {
+ return new ProviderListItem(context, LandingPage.this);
+ }
+ return null;
+ }
+ }
+
+ private final class ProviderAdapter extends CursorAdapter {
+ private LayoutInflater mInflater;
+
+ public ProviderAdapter(Context context, Cursor c) {
+ super(context, c);
+ mInflater = LayoutInflater.from(context).cloneInContext(context);
+ mInflater.setFactory(new ProviderListItemFactory());
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ // create a custom view, so we can manage it ourselves. Mainly, we want to
+ // initialize the widget views (by calling getViewById()) in newView() instead of in
+ // bindView(), which can be called more often.
+ ProviderListItem view = (ProviderListItem) mInflater.inflate(
+ R.layout.account_view, parent, false);
+ view.init(cursor);
+ return view;
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ ((ProviderListItem) view).bindView(cursor);
+ }
+ }
+
+ private final static class MyHandler extends SimpleAlertHandler {
+
+ public MyHandler(Activity activity) {
+ super(activity);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == ImApp.EVENT_CONNECTION_DISCONNECTED) {
+ promptDisconnectedEvent(msg);
+ }
+ super.handleMessage(msg);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2009 Myriad Group AG
+ * 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.im.app;
+
+import com.android.im.R;
+
+import android.graphics.drawable.Drawable;
+import android.widget.LinearLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.content.Context;
+import android.content.ContentResolver;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.im.BrandingResourceIDs;
+import android.content.res.ColorStateList;
+import android.view.View;
+import android.provider.Im;
+import android.util.Log;
+
+public class ProviderListItem extends LinearLayout {
+ private static final String TAG = "IM";
+ private static final boolean LOCAL_DEBUG = false;
+
+ private LandingPage mActivity;
+ private ImageView mProviderIcon;
+ private ImageView mStatusIcon;
+ private TextView mProviderName;
+ private TextView mLoginName;
+ private TextView mChatView;
+ private View mUnderBubble;
+ private Drawable mBubbleDrawable, mDefaultBackground;
+
+ private int mProviderIdColumn;
+ private int mProviderFullnameColumn;
+ private int mActiveAccountIdColumn;
+ private int mActiveAccountUserNameColumn;
+ private int mAccountPresenceStatusColumn;
+ private int mAccountConnectionStatusColumn;
+
+ private ColorStateList mProviderNameColors;
+ private ColorStateList mLoginNameColors;
+ private ColorStateList mChatViewColors;
+
+ public ProviderListItem(Context context, LandingPage activity) {
+ super(context);
+ mActivity = activity;
+ }
+
+ public void init(Cursor c) {
+ mProviderIcon = (ImageView) findViewById(R.id.providerIcon);
+ mStatusIcon = (ImageView) findViewById(R.id.statusIcon);
+ mProviderName = (TextView) findViewById(R.id.providerName);
+ mLoginName = (TextView) findViewById(R.id.loginName);
+ mChatView = (TextView) findViewById(R.id.conversations);
+ mUnderBubble = findViewById(R.id.underBubble);
+ mBubbleDrawable = getResources().getDrawable(R.drawable.bubble);
+ mDefaultBackground = getResources().getDrawable(R.drawable.default_background);
+
+ mProviderIdColumn = c.getColumnIndexOrThrow(Im.Provider._ID);
+ mProviderFullnameColumn = c.getColumnIndexOrThrow(Im.Provider.FULLNAME);
+ mActiveAccountIdColumn = c.getColumnIndexOrThrow(
+ Im.Provider.ACTIVE_ACCOUNT_ID);
+ mActiveAccountUserNameColumn = c.getColumnIndexOrThrow(
+ Im.Provider.ACTIVE_ACCOUNT_USERNAME);
+ mAccountPresenceStatusColumn = c.getColumnIndexOrThrow(
+ Im.Provider.ACCOUNT_PRESENCE_STATUS);
+ mAccountConnectionStatusColumn = c.getColumnIndexOrThrow(
+ Im.Provider.ACCOUNT_CONNECTION_STATUS);
+
+ mProviderNameColors = mProviderName.getTextColors();
+ mLoginNameColors = mLoginName.getTextColors();
+ mChatViewColors = mChatView.getTextColors();
+ }
+
+ public void bindView(Cursor cursor) {
+ Resources r = getResources();
+ ImageView providerIcon = mProviderIcon;
+ ImageView statusIcon = mStatusIcon;
+ TextView providerName = mProviderName;
+ TextView loginName = mLoginName;
+ TextView chatView = mChatView;
+
+ int providerId = cursor.getInt(mProviderIdColumn);
+ String providerDisplayName = cursor.getString(mProviderFullnameColumn);
+
+ ImApp app = ImApp.getApplication(mActivity);
+ BrandingResources brandingRes = app.getBrandingResource(providerId);
+ providerIcon.setImageDrawable(
+ brandingRes.getDrawable(BrandingResourceIDs.DRAWABLE_LOGO));
+
+ mUnderBubble.setBackgroundDrawable(mDefaultBackground);
+ statusIcon.setVisibility(View.GONE);
+
+ providerName.setTextColor(mProviderNameColors);
+ loginName.setTextColor(mLoginNameColors);
+ chatView.setTextColor(mChatViewColors);
+
+ if (!cursor.isNull(mActiveAccountIdColumn)) {
+ mLoginName.setVisibility(View.VISIBLE);
+ providerName.setVisibility(View.VISIBLE);
+ providerName.setText(providerDisplayName);
+
+ long accountId = cursor.getLong(mActiveAccountIdColumn);
+ int connectionStatus = cursor.getInt(mAccountConnectionStatusColumn);
+
+ String secondRowText;
+
+ chatView.setVisibility(View.GONE);
+
+ switch (connectionStatus) {
+ case Im.ConnectionStatus.CONNECTING:
+ secondRowText = r.getString(R.string.signing_in_wait);
+ break;
+
+ case Im.ConnectionStatus.ONLINE:
+ int presenceIconId = getPresenceIconId(cursor);
+ statusIcon.setImageDrawable(
+ brandingRes.getDrawable(presenceIconId));
+ statusIcon.setVisibility(View.VISIBLE);
+ ContentResolver cr = mActivity.getContentResolver();
+
+ int count = getConversationCount(cr, accountId);
+ if (count > 0) {
+ mUnderBubble.setBackgroundDrawable(mBubbleDrawable);
+ chatView.setVisibility(View.VISIBLE);
+ chatView.setText(r.getString(R.string.conversations, count));
+
+ providerName.setTextColor(0xff000000);
+ loginName.setTextColor(0xff000000);
+ chatView.setTextColor(0xff000000);
+ } else {
+ chatView.setVisibility(View.GONE);
+ }
+
+ secondRowText = cursor.getString(mActiveAccountUserNameColumn);
+ break;
+
+ default:
+ secondRowText = cursor.getString(mActiveAccountUserNameColumn);
+ break;
+ }
+
+ loginName.setText(secondRowText);
+
+ } else {
+ // No active account, show add account
+ mLoginName.setVisibility(View.GONE);
+ mChatView.setVisibility(View.GONE);
+ mProviderName.setText(providerDisplayName);
+ }
+ }
+
+ private int getConversationCount(ContentResolver cr, long accountId) {
+ // TODO: this is code used to get Google Talk's chat count. Not sure if this will work
+ // for IMPS chat count.
+ StringBuilder where = new StringBuilder();
+ where.append(Im.Chats.CONTACT_ID);
+ where.append(" in (select _id from contacts where ");
+ where.append(Im.Contacts.ACCOUNT);
+ where.append("=");
+ where.append(accountId);
+ where.append(")");
+
+ Cursor cursor = cr.query(Im.Chats.CONTENT_URI, null, where.toString(), null, null);
+
+ try {
+ return cursor.getCount();
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private int getPresenceIconId(Cursor cursor) {
+ int presenceStatus = cursor.getInt(mAccountPresenceStatusColumn);
+
+ if (LOCAL_DEBUG) log("getPresenceIconId: presenceStatus=" + presenceStatus);
+
+ switch (presenceStatus) {
+ case Im.Presence.AVAILABLE:
+ return BrandingResourceIDs.DRAWABLE_PRESENCE_ONLINE;
+
+ case Im.Presence.IDLE:
+ case Im.Presence.AWAY:
+ return BrandingResourceIDs.DRAWABLE_PRESENCE_AWAY;
+
+ case Im.Presence.DO_NOT_DISTURB:
+ return BrandingResourceIDs.DRAWABLE_PRESENCE_BUSY;
+
+ case Im.Presence.INVISIBLE:
+ return BrandingResourceIDs.DRAWABLE_PRESENCE_INVISIBLE;
+
+ default:
+ return BrandingResourceIDs.DRAWABLE_PRESENCE_OFFLINE;
+ }
+ }
+
+ private void log(String msg) {
+ Log.d(TAG, msg);
+ }
+}
* An abstract interface to access system SMS service.
*/
public interface SmsService {
+ public static final String ANY_ADDRESS = "*";
+
/**
* The listener which will be notified when an incoming SMS is received.
*
*/
public abstract void connect() throws ImException;
+ /**
+ * Re-establish the connection and drop the old one.
+ */
public void reconnect(){
}
/**
+ * Tells if the CIR has been shutdown or not.
+ */
+ public abstract boolean isShutdown();
+
+ /**
* Shutdown the CIR channel, stops to listen to CIR requests from the server.
*
*/
package com.android.im.imps;
import com.android.im.engine.ImException;
-import com.android.im.plugin.IPasswordDigest;
-
-import android.os.RemoteException;
+import com.android.im.plugin.PasswordDigest;
import dalvik.system.PathClassLoader;
public class CustomPasswordDigest implements PasswordDigest {
- private IPasswordDigest mPasswordDigest;
+ private PasswordDigest mPasswordDigest;
public CustomPasswordDigest(String pluginPath, String implClass) throws ImException {
PathClassLoader classLoader = new PathClassLoader(pluginPath,
getClass().getClassLoader());
try {
- Class cls = classLoader.loadClass(implClass);
- mPasswordDigest = (IPasswordDigest)cls.newInstance();
+ Class<?> cls = classLoader.loadClass(implClass);
+ mPasswordDigest = (PasswordDigest)cls.newInstance();
} catch (ClassNotFoundException e) {
throw new ImException(e);
} catch (IllegalAccessException e) {
}
}
public String digest(String schema, String nonce, String password) throws ImException {
- try {
- return mPasswordDigest.digest(schema, nonce, password);
- } catch (RemoteException e) {
- throw new ImException(e);
- }
+ return mPasswordDigest.digest(schema, nonce, password);
}
public String[] getSupportedDigestSchema() {
- try {
- return mPasswordDigest.getSupportedDigestSchema();
- } catch (RemoteException e) {
- return new String[0];
- }
+ return mPasswordDigest.getSupportedDigestSchema();
}
}
*/
package com.android.im.imps;
+import java.util.Map;
+
+import android.os.RemoteException;
+
import com.android.im.engine.ImException;
-import com.android.im.plugin.IPresenceMapping;
import com.android.im.plugin.ImPluginConstants;
+import com.android.im.plugin.PresenceMapping;
import dalvik.system.PathClassLoader;
-import android.os.RemoteException;
-
-import java.util.Map;
-
public class CustomPresenceMapping implements PresenceMapping {
- private IPresenceMapping mPresenceMapping;
+ private PresenceMapping mPresenceMapping;
public CustomPresenceMapping(String pluginPath, String implClass) throws ImException {
PathClassLoader classLoader = new PathClassLoader(pluginPath,
getClass().getClassLoader());
try {
- Class cls = classLoader.loadClass(implClass);
- mPresenceMapping = (IPresenceMapping)cls.newInstance();
+ Class<?> cls = classLoader.loadClass(implClass);
+ mPresenceMapping = (PresenceMapping)cls.newInstance();
} catch (ClassNotFoundException e) {
throw new ImException(e);
} catch (IllegalAccessException e) {
}
public Map<String, Object> getExtra(int status) {
- try {
- return mPresenceMapping.getExtra(status);
- } catch (RemoteException e) {
- return null;
- }
+ return mPresenceMapping.getExtra(status);
}
public boolean getOnlineStatus(int status) {
- try {
- return mPresenceMapping.getOnlineStatus(status);
- } catch (RemoteException e) {
- return false;
- }
+ return mPresenceMapping.getOnlineStatus(status);
}
public int getPresenceStatus(boolean onlineStatus, String userAvailability,
Map<String, Object> allValues) {
- try {
- return mPresenceMapping.getPresenceStatus(onlineStatus, userAvailability, allValues);
- } catch (RemoteException e) {
- return ImPluginConstants.PRESENCE_OFFLINE;
- }
+ return mPresenceMapping.getPresenceStatus(onlineStatus, userAvailability, allValues);
}
public int[] getSupportedPresenceStatus() {
- try {
- return mPresenceMapping.getSupportedPresenceStatus();
- } catch (RemoteException e) {
- return new int[0];
- }
+ return mPresenceMapping.getSupportedPresenceStatus();
}
public String getUserAvaibility(int status) {
- try {
- return mPresenceMapping.getUserAvaibility(status);
- } catch (RemoteException e) {
- return ImPluginConstants.PA_NOT_AVAILABLE;
- }
+ return mPresenceMapping.getUserAvaibility(status);
}
public boolean requireAllPresenceValues() {
- try {
- return mPresenceMapping.requireAllPresenceValues();
- } catch (RemoteException e) {
- return false;
- }
+ return mPresenceMapping.requireAllPresenceValues();
}
}
package com.android.im.imps;
import com.android.im.plugin.ImPluginConstants;
+import com.android.im.plugin.PresenceMapping;
import java.util.Map;
}
@Override
- public void connect() {
+ public synchronized void connect() {
ImpsSession session = mConnection.getSession();
try {
if (session.getCirHttpAddress() != null) {
}
mServerPollMin = session.getServerPollMin() * 1000;
+ mStopped = false;
mPollingTask = new Thread(this, "HTTPCIRChannel");
mPollingTask.setDaemon(true);
mPollingTask.start();
}
+ public synchronized boolean isShutdown() {
+ return mStopped;
+ }
+
@Override
- public void shutdown() {
+ public synchronized void shutdown() {
mStopped = true;
}
import com.android.im.engine.ImException;
import com.android.im.imps.ImpsConstants.ImpsVersion;
import com.android.im.plugin.ImpsConfigNames;
+import com.android.im.plugin.PasswordDigest;
+import com.android.im.plugin.PresenceMapping;
/**
* The configuration for IMPS connection.
import com.android.im.engine.Presence;
import com.android.im.engine.SubscriptionRequestListener;
import com.android.im.imps.ImpsConstants.ImpsVersion;
+import com.android.im.plugin.PresenceMapping;
/**
* An implementation of ContactListManager of Wireless Village IMPS protocol.
package com.android.im.imps;
import com.android.im.engine.Presence;
+import com.android.im.plugin.PresenceMapping;
+
import org.apache.commons.codec.binary.Base64;
import android.os.Base64Utils;
if (value instanceof String) {
elem.setContents((String)value);
} else if (value instanceof Map) {
- mapToPrimitives((Map)value, elem.getChildren());
+ mapToPrimitives((Map<String, Object>)value, elem.getChildren());
}
elems.add(elem);
}
+++ /dev/null
-/*
- * Copyright (C) 2008 Esmertec AG.
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.im.imps;
-
-import com.android.im.engine.ImException;
-
-public interface PasswordDigest {
- /**
- * Gets an array of supported digest schema.
- *
- * @return an array of digest schema
- */
- String[] getSupportedDigestSchema();
-
- /**
- * Generates digest bytes.
- *
- */
- String digest(String schema, String nonce, String password) throws ImException;
-}
+++ /dev/null
-/*
- * Copyright (C) 2008 Esmertec AG.
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.im.imps;
-
-import java.util.Map;
-
-public interface PresenceMapping {
- /**
- * Tells if the mapping needs all presence values sent in protocol. If this
- * method returns true, the framework will pass all the presence values
- * received from the server when map to the predefined status.
- *
- * @return true if needs; false otherwise.
- */
- boolean requireAllPresenceValues();
-
- /**
- * Map the presence values sent in protocol to the predefined presence
- * status.
- *
- * @param onlineStatus The value of presence <OnlineStatus> received
- * from the server.
- * @param userAvailability The value of presence <UserAvailibility>
- * received from the server.
- * @param allValues The whole presence values received from the server.
- * @return a predefined status.
- * @see #requireAllPresenceValues()
- */
- int getPresenceStatus(boolean onlineStatus, String userAvailability,
- Map<String, Object> allValues);
-
- /**
- * Gets the value of <OnlineStatus> will be sent to the server when
- * update presence to the predefined status.
- *
- * @param status the predefined status.
- * @return The value of <OnlineStatus> will be sent to the server
- */
- boolean getOnlineStatus(int status);
-
- /**
- * Gets the value of <UserAvaibility> will be sent to the server when
- * update presence to the predefined status.
- *
- * @param status the predefined status.
- * @return The value of <UserAvaibility> will be sent to the server
- */
- String getUserAvaibility(int status);
-
- /**
- * Gets the extra presence values other than <OnlineStatus> and
- * <UserAvaibility> will be sent to the server when update presence to
- * the predefined status.
- *
- * @param status the predefined status.
- * @return The extra values that will be sent to the server.
- */
- Map<String, Object> getExtra(int status);
-
- /**
- * Gets an array of the supported presence status. The client can only update
- * presence to the values in the array.
- *
- * @return an array of the supported presence status.
- */
- int[] getSupportedPresenceStatus();
-}
// poll. Fetch the presence of all contacts in list.
pollingAddress = getContactLists();
}
- mManager.fetchPresence(pollingAddress);
+ if (pollingAddress != null) {
+ mManager.fetchPresence(pollingAddress);
+ }
}
try {
*/
package com.android.im.imps;
+import android.util.Log;
+
import com.android.im.engine.ImErrorInfo;
import com.android.im.engine.ImException;
import com.android.im.engine.SmsService;
@Override
public void connect() throws ImException {
- if (mAddr == null || mAddr.length() == 0) {
- throw new ImException(ImpsErrorInfo.UNKNOWN_SERVER,
- "Invalid sms addr");
- }
mSmsService = SystemService.getDefault().getSmsService();
- mSmsService.addSmsListener(mAddr, mPort, this);
- sendHelo();
+ if (mAddr != null) {
+ mSmsService.addSmsListener(mAddr, mPort, this);
+ sendHelo();
+ } else {
+ mSmsService.addSmsListener(SmsService.ANY_ADDRESS, mPort, this);
+ }
+ }
+
+ public boolean isShutdown() {
+ return false;
}
@Override
// It's safe to assume that each character is encoded into 7-bit since
// all characters in CIR are in gsm 7-bit alphabet.
int lengthSeptets = data.length * 8 / 7;
- int numPaddingBits = data.length * 8 % 7;
String s = GsmAlphabet.gsm7BitPackedToString(data, 0,
- lengthSeptets, numPaddingBits);
+ lengthSeptets, 0);
// CIR format: WVCI <version> <session cookie>
if (!s.startsWith("WVCI")) {
// not a valid CIR, ignore.
+ Log.w("SmsCir", "Received a non-CIR SMS, ignore!");
return;
}
+
+ String sessionCookie = mConnection.getSession().getCookie();
String[] fields = s.split(" ");
- if (fields.length != 3) {
+ if (fields.length != 3 || !sessionCookie.equalsIgnoreCase(fields[2])) {
// Not a valid CIR, ignore
- return;
- }
- String sessionCookie = mConnection.getSession().getCookie();
- if (sessionCookie.equalsIgnoreCase(fields[2])) {
- mConnection.sendPollingRequest();
+ Log.w("SmsCir", "The CIR format is not correct or session cookie" +
+ " does not match");
}
+ mConnection.sendPollingRequest();
}
public void onFailure(int errorCode) {
package com.android.im.imps;
import com.android.im.engine.ImException;
+import com.android.im.plugin.PasswordDigest;
import org.apache.commons.codec.binary.Base64;
@Override
public synchronized void connect() throws ImException {
try {
+ mDone = false;
connectServer();
mCirThread = new Thread(this, "TcpCirChannel");
mCirThread.setDaemon(true);
ImpsLog.log(mUser + " Shutting down CIR channel");
}
mDone = true;
- synchronized (mReconnectLock) {
- if (mReconnecting) {
+ if (mReconnecting) {
+ synchronized (mReconnectLock) {
mReconnecting = false;
mReconnectLock.notify();
}
}
}
+ public boolean isShutdown() {
+ return mDone;
+ }
+
public void run() {
while (!mDone) {
try {
package com.android.im.service;
-import com.android.im.engine.HeartbeatService;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.os.SystemClock;
import android.util.SparseArray;
+import com.android.im.engine.HeartbeatService;
+
public class AndroidHeartBeatService extends BroadcastReceiver
implements HeartbeatService {
private static final String HEARTBEAT_CONTENT_TYPE
= "vnd.android.im/heartbeat";
- private Context mContext;
- private AlarmManager mAlarmManager;
- private PowerManager.WakeLock mWakeLock;
+ private static final ExecutorService sExecutor = Executors.newSingleThreadExecutor();
+
+ private final Context mContext;
+ private final AlarmManager mAlarmManager;
+ /*package*/ PowerManager.WakeLock mWakeLock;
static class Alarm {
public PendingIntent mAlaramSender;
public Callback mCallback;
}
- private SparseArray<Alarm> mAlarms;
+ private final SparseArray<Alarm> mAlarms;
public AndroidHeartBeatService(Context context) {
mContext = context;
int id = nextId();
alarm.mCallback = callback;
Uri data = ContentUris.withAppendedId(HEARTBEAT_CONTENT_URI, id);
- Intent i = new Intent().setDataAndType(data, HEARTBEAT_CONTENT_TYPE);
+ Intent i = new Intent(HEARTBEAT_INTENT_ACTION)
+ .setDataAndType(data, HEARTBEAT_CONTENT_TYPE);
alarm.mAlaramSender = PendingIntent.getBroadcast(mContext, 0, i, 0);
if (mAlarms.size() == 0) {
mContext.registerReceiver(this, IntentFilter.create(
@Override
public void onReceive(Context context, Intent intent) {
- mWakeLock.acquire();
- try {
- int id = (int)ContentUris.parseId(intent.getData());
- Alarm alarm = mAlarms.get(id);
- if (alarm == null) {
- return;
- }
- Callback callback = alarm.mCallback;
- long nextSchedule = callback.sendHeartbeat();
- if (nextSchedule <= 0) {
- cancelAlarm(alarm);
- } else {
- setAlarm(alarm, nextSchedule);
+ int id = (int)ContentUris.parseId(intent.getData());
+ Alarm alarm = mAlarms.get(id);
+ if (alarm == null) {
+ return;
+ }
+ sExecutor.execute(new Worker(alarm));
+ }
+
+ private class Worker implements Runnable {
+ private final Alarm mAlarm;
+
+ public Worker(Alarm alarm) {
+ mAlarm = alarm;
+ }
+
+ public void run() {
+ mWakeLock.acquire();
+ try {
+ Callback callback = mAlarm.mCallback;
+ long nextSchedule = callback.sendHeartbeat();
+ if (nextSchedule <= 0) {
+ cancelAlarm(mAlarm);
+ } else {
+ setAlarm(mAlarm, nextSchedule);
+ }
+ } finally {
+ mWakeLock.release();
}
- } finally {
- mWakeLock.release();
}
}
return null;
}
- private void setAlarm(Alarm alarm, long offset) {
+ /*package*/ synchronized void setAlarm(Alarm alarm, long offset) {
long triggerAtTime = SystemClock.elapsedRealtime() + offset;
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime,
alarm.mAlaramSender);
}
- private void cancelAlarm(Alarm alarm) {
+ /*package*/ synchronized void cancelAlarm(Alarm alarm) {
mAlarmManager.cancel(alarm.mAlaramSender);
int index = mAlarms.indexOfValue(alarm);
if (index >= 0) {
private Context mContext;
private SmsReceiver mSmsReceiver;
private IntentFilter mIntentFilter;
+ private boolean mStarted;
/*package*/HashMap<Integer, ListenerList> mListeners;
/*package*/HashMap<Long, SmsSendFailureCallback> mFailureCallbacks;
if (l == null) {
l = new ListenerList(port);
mListeners.put(port, l);
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ log("Register SMS receiver on port " + port);
+ }
// We didn't listen on the port yet, register the receiver with the
// additional port.
mIntentFilter.addDataAuthority("*", String.valueOf(port));
mContext.registerReceiver(mSmsReceiver, mIntentFilter);
+ synchronized (this) {
+ mStarted = true;
+ }
}
l.addListener(from, listener);
}
}
}
- public void stop() {
- mContext.unregisterReceiver(mSmsReceiver);
+ public synchronized void stop() {
+ if (mStarted) {
+ mContext.unregisterReceiver(mSmsReceiver);
+ mStarted = false;
+ }
}
private static long sNextMsgId = 0;
} else if (DATA_SMS_RECEIVED_ACTION.equals(intent.getAction())){
Uri uri = intent.getData();
int port = uri.getPort();
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ log("Received sms on port:" + port);
+ }
+
ListenerList listeners = mListeners.get(port);
if (listeners == null) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
public void notifySms(String addr, byte[] data) {
int N = mListenerList.size();
for (int i = 0; i < N; i++) {
- if (PhoneNumberUtils.compare(addr, mAddrList.get(i))) {
+ String listenAddr = mAddrList.get(i);
+ if (ANY_ADDRESS.equals(listenAddr)
+ || addr.equals(listenAddr)
+ || PhoneNumberUtils.compare(addr, listenAddr)) {
mListenerList.get(i).onIncomingSms(data);
}
}
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.NetworkConnectivityListener;
import android.util.Log;
import android.widget.Toast;
-import com.android.im.app.DatabaseUtils;
import com.android.im.IConnectionCreationListener;
import com.android.im.IImConnection;
import com.android.im.IRemoteImService;
+import com.android.im.app.ImPluginHelper;
import com.android.im.engine.ConnectionFactory;
import com.android.im.engine.ImConnection;
import com.android.im.engine.ImException;
import com.android.im.imps.ImpsConnectionConfig;
-import com.android.im.plugin.IImPlugin;
import com.android.im.plugin.ImConfigNames;
-import com.android.im.plugin.ImPluginConstants;
import com.android.im.plugin.ImPluginInfo;
import com.android.im.plugin.ImpsConfigNames;
-import dalvik.system.PathClassLoader;
+
public class RemoteImService extends Service {
private SettingsMonitor mSettingsMonitor;
+ private ImPluginHelper mPluginHelper;
Vector<ImConnectionAdapter> mConnections;
final RemoteCallbackList<IConnectionCreationListener> mRemoteListeners
= new RemoteCallbackList<IConnectionCreationListener>();
- private HashMap<Long, ImPluginInfo> mPlugins;
public RemoteImService() {
mConnections = new Vector<ImConnectionAdapter>();
- mPlugins = new HashMap<Long, ImPluginInfo>();
}
@Override
= (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
setBackgroundData(manager.getBackgroundDataSetting());
- findAvaiablePlugins();
+ mPluginHelper = ImPluginHelper.getInstance(this);
+ mPluginHelper.loadAvaiablePlugins();
AndroidSystemService.getInstance().initialize(this);
}
- private void findAvaiablePlugins() {
- PackageManager pm = getPackageManager();
- List<ResolveInfo> plugins = pm.queryIntentServices(
- new Intent(ImPluginConstants.PLUGIN_ACTION_NAME), PackageManager.GET_META_DATA);
- for (ResolveInfo info : plugins) {
- Log.d(TAG, "Found plugin " + info);
-
- ServiceInfo serviceInfo = info.serviceInfo;
- if (serviceInfo == null) {
- Log.e(TAG, "Ignore bad IM plugin: " + info);
- continue;
- }
- String providerName = null;
- String providerFullName = null;
- String signUpUrl = null;
- Bundle metaData = serviceInfo.metaData;
- if (metaData != null) {
- providerName = metaData.getString(ImPluginConstants.METADATA_PROVIDER_NAME);
- providerFullName = metaData.getString(ImPluginConstants.METADATA_PROVIDER_FULL_NAME);
- signUpUrl = metaData.getString(ImPluginConstants.METADATA_SIGN_UP_URL);
- }
- if (TextUtils.isEmpty(providerName) || TextUtils.isEmpty(providerFullName)) {
- Log.e(TAG, "Ignore bad IM plugin: " + info + ". Lack of required meta data");
- continue;
- }
-
- ImPluginInfo pluginInfo = new ImPluginInfo(providerName, serviceInfo.packageName,
- serviceInfo.name, serviceInfo.applicationInfo.sourceDir);
-
- Map<String, String> config = loadProviderConfigFromPlugin(pluginInfo);
- if (config == null) {
- Log.e(TAG, "Ignore bad IM plugin");
- break;
- }
-
- config.put(ImConfigNames.PLUGIN_PATH, pluginInfo.mSrcPath);
- config.put(ImConfigNames.PLUGIN_CLASS, pluginInfo.mClassName);
- long providerId = DatabaseUtils.updateProviderDb(getContentResolver(),
- providerName, providerFullName, signUpUrl, config);
- mPlugins.put(providerId, pluginInfo);
- }
- }
-
- private Map<String, String> loadProviderConfigFromPlugin(ImPluginInfo pluginInfo) {
- // XXX Load the plug-in implementation directly from the apk rather than
- // binding to the service and call through IPC Binder API. This is much
- // more effective since we don't need to start the service in other
- // process. We can not run the plug-in service in the same process as a
- // local service because that the interface is defined in a shared
- // library in order to compile the plug-in separately. In this case, the
- // interface will be loaded by two class loader separately and a
- // ClassCastException will be thrown if we cast the binder to the
- // interface.
- PathClassLoader loader = new PathClassLoader(pluginInfo.mSrcPath, getClassLoader());
- try {
- Class cls = loader.loadClass(pluginInfo.mClassName);
- Method m = cls.getMethod("onBind", Intent.class);
- IImPlugin plugin = (IImPlugin)m.invoke(cls.newInstance(), new Object[]{null});
- return plugin.getProviderConfig();
- } catch (ClassNotFoundException e) {
- Log.e(TAG, "Could not find plugin class", e);
- } catch (IllegalAccessException e) {
- Log.e(TAG, "Could not create plugin instance", e);
- } catch (InstantiationException e) {
- Log.e(TAG, "Could not create plugin instance", e);
- } catch (SecurityException e) {
- Log.e(TAG, "Could not load config from the plugin", e);
- } catch (NoSuchMethodException e) {
- Log.e(TAG, "Could not load config from the plugin", e);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Could not load config from the plugin", e);
- } catch (InvocationTargetException e) {
- Log.e(TAG, "Could not load config from the plugin", e);
- } catch (RemoteException e) {
- Log.e(TAG, "Could not load config from the plugin", e);
- }
- return null;
- }
-
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
private final IRemoteImService.Stub mBinder = new IRemoteImService.Stub() {
- public List getAllPlugins() {
- return new ArrayList(mPlugins.values());
+ public List<ImPluginInfo> getAllPlugins() {
+ return new ArrayList<ImPluginInfo>(mPluginHelper.getPluginsInfo());
}
public void addConnectionCreatedListener(IConnectionCreationListener listener) {