am aa1be08c: Merge change 22791 into donut
Merge commit 'aa1be08c90507e20c1ee78d0c7cbd4fa5c40a799' into eclair
* commit 'aa1be08c90507e20c1ee78d0c7cbd4fa5c40a799':
Add test definition for cts-gesture.
diff --git a/apps/Development/AndroidManifest.xml b/apps/Development/AndroidManifest.xml
index 4f8df3e..0bdd26e 100644
--- a/apps/Development/AndroidManifest.xml
+++ b/apps/Development/AndroidManifest.xml
@@ -31,6 +31,9 @@
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.ALL_SERVICES" />
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.YouTubeUser" />
<uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" />
+ <uses-permission android:name="android.permission.GET_ACCOUNTS" />
+ <uses-permission android:name="android.permission.USE_CREDENTIALS" />
+ <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<application android:label="Dev Tools"
android:icon="@drawable/ic_launcher_devtools">
@@ -73,6 +76,14 @@
</intent-filter>
</activity>
+ <activity android:name="AccountsTester" android:label="AccountsTester"
+ android:theme="@android:style/Theme.Light">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.TEST" />
+ </intent-filter>
+ </activity>
+
<activity android:name="DataList">
</activity>
<activity android:name="Details">
diff --git a/apps/Development/res/layout/account_list_context_menu.xml b/apps/Development/res/layout/account_list_context_menu.xml
new file mode 100644
index 0000000..50e7ca1
--- /dev/null
+++ b/apps/Development/res/layout/account_list_context_menu.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:id="@+id/accounts_tester_remove_account"
+ android:title="@string/accounts_tester_remove_account" />
+
+ <item android:id="@+id/accounts_tester_get_auth_token"
+ android:title="@string/accounts_tester_get_auth_token" />
+
+ <item android:id="@+id/accounts_tester_invalidate_auth_token"
+ android:title="@string/accounts_tester_invalidate_auth_token" />
+
+ <item android:id="@+id/accounts_tester_update_credentials"
+ android:title="@string/accounts_tester_update_credentials" />
+
+ <item android:id="@+id/accounts_tester_confirm_credentials"
+ android:title="@string/accounts_tester_confirm_credentials" />
+
+</menu>
diff --git a/apps/Development/res/layout/accounts_tester.xml b/apps/Development/res/layout/accounts_tester.xml
new file mode 100644
index 0000000..e69f505
--- /dev/null
+++ b/apps/Development/res/layout/accounts_tester.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <ListView android:id="@+id/accounts_tester_authenticators_list"
+ android:layout_width="fill_parent" android:layout_height="fill_parent"/>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView android:id="@+id/accounts_tester_account_types_spinner_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/accounts_tester_select_account_type"/>
+
+ <Spinner android:id="@+id/accounts_tester_account_types_spinner"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+ <Button
+ android:id="@+id/accounts_tester_get_accounts_by_type"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/accounts_tester_get_accounts_by_type"/>
+
+ <Button
+ android:id="@+id/accounts_tester_get_all_accounts"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/accounts_tester_get_all_accounts"/>
+ <Button android:id="@+id/accounts_tester_add_account"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/accounts_tester_add_account"/>
+
+ <Button android:id="@+id/accounts_tester_edit_properties"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/accounts_tester_edit_properties"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+ <TextView android:id="@+id/accounts_tester_desiredFeatures"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/accounts_tester_desired_features_label"/>
+
+ <EditText android:id="@+id/accounts_tester_desired_features"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minEms="15"/>
+ </LinearLayout>
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+ <TextView android:id="@+id/accounts_tester_desiredFeatures"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/accounts_tester_desired_authtokentype_label"/>
+
+ <EditText android:id="@+id/accounts_tester_desired_authtokentype"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minEms="15"/>
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <ListView android:id="@+id/accounts_tester_accounts_list"
+ android:layout_width="fill_parent" android:layout_height="fill_parent"/>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/apps/Development/res/layout/authenticators_list_item.xml b/apps/Development/res/layout/authenticators_list_item.xml
new file mode 100644
index 0000000..309a818
--- /dev/null
+++ b/apps/Development/res/layout/authenticators_list_item.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingTop="1dip"
+ android:paddingBottom="1dip"
+ android:paddingLeft="9dip"
+ android:paddingRight="9dip">
+
+ <ImageView android:id="@+id/accounts_tester_authenticator_icon"
+ android:paddingRight="9dip"
+ android:layout_gravity="center_vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView android:id="@+id/accounts_tester_authenticator_label"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:layout_gravity="center_horizontal|center_vertical" />
+</LinearLayout>
+
diff --git a/apps/Development/res/layout/get_auth_token_view.xml b/apps/Development/res/layout/get_auth_token_view.xml
new file mode 100644
index 0000000..f371523
--- /dev/null
+++ b/apps/Development/res/layout/get_auth_token_view.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <TextView android:id="@+id/accounts_tester_get_auth_token_dialog_message"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:text="@string/accounts_tester_enter_auth_token_type" />
+
+ <EditText android:id="@+id/accounts_tester_auth_token_type"
+ android:singleLine="true"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:minWidth="250dip"
+ android:scrollHorizontally="true"
+ android:capitalize="none"
+ android:autoText="false"/>
+
+</LinearLayout>
diff --git a/apps/Development/res/values/strings.xml b/apps/Development/res/values/strings.xml
index 4c84548..21f15f6 100644
--- a/apps/Development/res/values/strings.xml
+++ b/apps/Development/res/values/strings.xml
@@ -133,4 +133,23 @@
<string name="perm_list_header_text">Apps using permission </string>
<string name="source_uid_text">Source uid : </string>
<string name="shared_pkgs_text">Packages accessing via shared uid : </string>
+
+ <!-- AccountsTester -->
+ <string name="accounts_tester_app_name">Accounts Tester</string>
+ <string name="accounts_tester_get_accounts_by_type">Get By Type</string>
+ <string name="accounts_tester_get_all_accounts">Get All</string>
+ <string name="accounts_tester_add_account">Add</string>
+ <string name="accounts_tester_select_account_type">Select Account Type</string>
+ <string name="accounts_tester_process_name_header">Process Name:</string>
+ <string name="accounts_tester_remove_account">remove</string>
+ <string name="accounts_tester_get_auth_token">authenticate</string>
+ <string name="accounts_tester_invalidate_auth_token">invalidate token</string>
+ <string name="accounts_tester_account_context_menu_title">account operations</string>
+ <string name="accounts_tester_do_get_auth_token">Ok</string>
+ <string name="accounts_tester_enter_auth_token_type">Enter the authtoken type:</string>
+ <string name="accounts_tester_update_credentials">Update Credentials</string>
+ <string name="accounts_tester_confirm_credentials">Confirm Credentials</string>
+ <string name="accounts_tester_edit_properties">Properties</string>
+ <string name="accounts_tester_desired_authtokentype_label">authtoken type:</string>
+ <string name="accounts_tester_desired_features_label">features:</string>
</resources>
diff --git a/apps/Development/src/com/android/development/AccountsTester.java b/apps/Development/src/com/android/development/AccountsTester.java
new file mode 100644
index 0000000..2525f4a
--- /dev/null
+++ b/apps/Development/src/com/android/development/AccountsTester.java
@@ -0,0 +1,431 @@
+/*
+ * 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.development;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.AlertDialog;
+import android.content.*;
+import android.content.pm.PackageManager;
+import android.accounts.*;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.Handler;
+import android.view.*;
+import android.widget.*;
+import android.widget.ArrayAdapter;
+import android.util.Log;
+import android.text.TextUtils;
+
+import java.io.IOException;
+
+public class AccountsTester extends Activity implements OnAccountsUpdatedListener {
+ private static final String TAG = "AccountsTester";
+ private Spinner mAccountTypesSpinner;
+ private ListView mAccountsListView;
+ private ListView mAuthenticatorsListView;
+ private AccountManager mAccountManager;
+ private String mLongPressedAccount = null;
+ private static final String COM_GOOGLE_GAIA = "com.google.GAIA";
+ private AuthenticatorDescription[] mAuthenticatorDescs;
+
+ private static final int GET_AUTH_TOKEN_DIALOG_ID = 1;
+ private static final int UPDATE_CREDENTIALS_DIALOG_ID = 2;
+ private static final int INVALIDATE_AUTH_TOKEN_DIALOG_ID = 3;
+ private EditText mDesiredAuthTokenTypeEditText;
+ private EditText mDesiredFeaturesEditText;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mAccountManager = AccountManager.get(this);
+ setContentView(R.layout.accounts_tester);
+ ButtonClickListener buttonClickListener = new ButtonClickListener();
+
+ mAccountTypesSpinner = (Spinner) findViewById(R.id.accounts_tester_account_types_spinner);
+ mAccountsListView = (ListView) findViewById(R.id.accounts_tester_accounts_list);
+ mAuthenticatorsListView = (ListView) findViewById(R.id.accounts_tester_authenticators_list);
+ registerForContextMenu(mAccountsListView);
+ getAuthenticatorTypes();
+ findViewById(R.id.accounts_tester_get_all_accounts).setOnClickListener(buttonClickListener);
+ findViewById(R.id.accounts_tester_get_accounts_by_type).setOnClickListener(
+ buttonClickListener);
+ findViewById(R.id.accounts_tester_add_account).setOnClickListener(buttonClickListener);
+ findViewById(R.id.accounts_tester_edit_properties).setOnClickListener(buttonClickListener);
+ mDesiredAuthTokenTypeEditText =
+ (EditText) findViewById(R.id.accounts_tester_desired_authtokentype);
+ mDesiredFeaturesEditText = (EditText) findViewById(R.id.accounts_tester_desired_features);
+ }
+
+ private static class AuthenticatorsArrayAdapter extends ArrayAdapter<AuthenticatorDescription> {
+ protected LayoutInflater mInflater;
+ private static final int mResource = R.layout.authenticators_list_item;
+
+ public AuthenticatorsArrayAdapter(Context context, AuthenticatorDescription[] items) {
+ super(context, mResource, items);
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ static class ViewHolder {
+ TextView label;
+ ImageView icon;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // A ViewHolder keeps references to children views to avoid unneccessary calls
+ // to findViewById() on each row.
+ ViewHolder holder;
+
+ // When convertView is not null, we can reuse it directly, there is no need
+ // to reinflate it. We only inflate a new View when the convertView supplied
+ // by ListView is null.
+ if (convertView == null) {
+ convertView = mInflater.inflate(R.layout.authenticators_list_item, null);
+
+ // Creates a ViewHolder and store references to the two children views
+ // we want to bind data to.
+ holder = new ViewHolder();
+ holder.label = (TextView) convertView.findViewById(
+ R.id.accounts_tester_authenticator_label);
+ holder.icon = (ImageView) convertView.findViewById(
+ R.id.accounts_tester_authenticator_icon);
+
+ convertView.setTag(holder);
+ } else {
+ // Get the ViewHolder back to get fast access to the TextView
+ // and the ImageView.
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ final AuthenticatorDescription desc = getItem(position);
+ final String packageName = desc.packageName;
+ try {
+ final Context authContext = getContext().createPackageContext(packageName, 0);
+
+ // Set text field
+ holder.label.setText(authContext.getString(desc.labelId));
+
+ // Set resource icon
+ holder.icon.setImageDrawable(authContext.getResources().getDrawable(desc.iconId));
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.d(TAG, "error getting the Package Context for " + packageName, e);
+ }
+
+ return convertView;
+ }
+ }
+
+ private void getAuthenticatorTypes() {
+ mAuthenticatorDescs = mAccountManager.getAuthenticatorTypes();
+ String[] names = new String[mAuthenticatorDescs.length];
+ for (int i = 0; i < mAuthenticatorDescs.length; i++) {
+ Context authContext;
+ try {
+ authContext = createPackageContext(mAuthenticatorDescs[i].packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ continue;
+ }
+ names[i] = authContext.getString(mAuthenticatorDescs[i].labelId);
+ }
+
+ ArrayAdapter<String> adapter =
+ new ArrayAdapter<String>(AccountsTester.this,
+ android.R.layout.simple_spinner_item, names);
+ mAccountTypesSpinner.setAdapter(adapter);
+
+ mAuthenticatorsListView.setAdapter(new AuthenticatorsArrayAdapter(
+ AccountsTester.this, mAuthenticatorDescs));
+ }
+
+ public void onAccountsUpdated(Account[] accounts) {
+ Log.d(TAG, "onAccountsUpdated: \n " + TextUtils.join("\n ", accounts));
+ String[] accountNames = new String[accounts.length];
+ for (int i = 0; i < accounts.length; i++) {
+ accountNames[i] = accounts[i].name;
+ }
+ ArrayAdapter<String> adapter =
+ new ArrayAdapter<String>(AccountsTester.this,
+ android.R.layout.simple_list_item_1, accountNames);
+ mAccountsListView.setAdapter(adapter);
+ }
+
+ protected void onStart() {
+ super.onStart();
+ final Handler mainHandler = new Handler(getMainLooper());
+ mAccountManager.addOnAccountsUpdatedListener(this, mainHandler,
+ true /* updateImmediately */);
+ }
+
+ protected void onStop() {
+ super.onStop();
+ mAccountManager.removeOnAccountsUpdatedListener(this);
+ }
+
+ class ButtonClickListener implements View.OnClickListener {
+ public void onClick(View v) {
+ if (R.id.accounts_tester_get_all_accounts == v.getId()) {
+ onAccountsUpdated(mAccountManager.getAccounts());
+ } else if (R.id.accounts_tester_get_accounts_by_type == v.getId()) {
+ String type = getSelectedAuthenticator().type;
+ onAccountsUpdated(mAccountManager.getAccountsByType(type));
+ } else if (R.id.accounts_tester_add_account == v.getId()) {
+ AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
+ public void run(AccountManagerFuture<Bundle> future) {
+ try {
+ Bundle bundle = future.getResult();
+ bundle.keySet();
+ Log.d(TAG, "account added: " + bundle);
+ } catch (OperationCanceledException e) {
+ Log.d(TAG, "addAccount was canceled");
+ } catch (IOException e) {
+ Log.d(TAG, "addAccount failed: " + e);
+ } catch (AuthenticatorException e) {
+ Log.d(TAG, "addAccount failed: " + e);
+ }
+ }
+ };
+ String authTokenType = mDesiredAuthTokenTypeEditText.getText().toString();
+ if (TextUtils.isEmpty(authTokenType)) {
+ authTokenType = null;
+ }
+ String featureString = mDesiredFeaturesEditText.getText().toString();
+ String[] requiredFeatures = TextUtils.split(featureString, " ");
+ if (requiredFeatures.length == 0) {
+ requiredFeatures = null;
+ }
+ mAccountManager.addAccount(getSelectedAuthenticator().type,
+ authTokenType, requiredFeatures, null /* options */,
+ AccountsTester.this, callback, null /* handler */);
+ } else if (R.id.accounts_tester_edit_properties == v.getId()) {
+ mAccountManager.editProperties(getSelectedAuthenticator().type,
+ AccountsTester.this, new EditPropertiesCallback(), null /* handler */);
+ } else {
+ // unknown button
+ }
+ }
+
+ private class EditPropertiesCallback implements AccountManagerCallback<Bundle> {
+ public void run(AccountManagerFuture<Bundle> future) {
+ try {
+ Bundle bundle = future.getResult();
+ bundle.keySet();
+ Log.d(TAG, "editProperties succeeded: " + bundle);
+ } catch (OperationCanceledException e) {
+ Log.d(TAG, "editProperties was canceled");
+ } catch (IOException e) {
+ Log.d(TAG, "editProperties failed: ", e);
+ } catch (AuthenticatorException e) {
+ Log.d(TAG, "editProperties failed: ", e);
+ }
+ }
+ }
+ }
+
+ private AuthenticatorDescription getSelectedAuthenticator() {
+ return mAuthenticatorDescs[mAccountTypesSpinner.getSelectedItemPosition()];
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenu.ContextMenuInfo menuInfo) {
+ menu.setHeaderTitle(R.string.accounts_tester_account_context_menu_title);
+
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)menuInfo;
+
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.layout.account_list_context_menu, menu);
+ mLongPressedAccount = ((TextView)info.targetView).getText().toString();
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.accounts_tester_remove_account) {
+ final Account account = new Account(mLongPressedAccount, COM_GOOGLE_GAIA);
+ mAccountManager.removeAccount(account, new AccountManagerCallback<Boolean>() {
+ public void run(AccountManagerFuture<Boolean> future) {
+ try {
+ Log.d(TAG, "removeAccount(" + account + ") = " + future.getResult());
+ } catch (OperationCanceledException e) {
+ } catch (IOException e) {
+ } catch (AuthenticatorException e) {
+ }
+ }
+ }, null /* handler */);
+ } else if (item.getItemId() == R.id.accounts_tester_get_auth_token) {
+ showDialog(GET_AUTH_TOKEN_DIALOG_ID);
+ } else if (item.getItemId() == R.id.accounts_tester_invalidate_auth_token) {
+ showDialog(INVALIDATE_AUTH_TOKEN_DIALOG_ID);
+ } else if (item.getItemId() == R.id.accounts_tester_update_credentials) {
+ showDialog(UPDATE_CREDENTIALS_DIALOG_ID);
+ } else if (item.getItemId() == R.id.accounts_tester_confirm_credentials) {
+ mAccountManager.confirmCredentials(new Account(mLongPressedAccount, COM_GOOGLE_GAIA),
+ AccountsTester.this, new ConfirmCredentialsCallback(), null /* handler */);
+ }
+ return true;
+ }
+
+ @Override
+ protected Dialog onCreateDialog(final int id) {
+ if (id == GET_AUTH_TOKEN_DIALOG_ID || id == INVALIDATE_AUTH_TOKEN_DIALOG_ID
+ || id == UPDATE_CREDENTIALS_DIALOG_ID) {
+ final View view = LayoutInflater.from(this).inflate(R.layout.get_auth_token_view, null);
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setPositiveButton(R.string.accounts_tester_do_get_auth_token,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ EditText value = (EditText) view.findViewById(
+ R.id.accounts_tester_auth_token_type);
+
+ String authTokenType = value.getText().toString();
+ AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
+ public void run(AccountManagerFuture<Bundle> future) {
+ try {
+ Bundle bundle = future.getResult();
+ bundle.keySet();
+ Log.d(TAG, "dialog " + id + " success: " + bundle);
+ } catch (OperationCanceledException e) {
+ Log.d(TAG, "dialog " + id + " canceled");
+ } catch (IOException e) {
+ Log.d(TAG, "dialog " + id + " failed: " + e);
+ } catch (AuthenticatorException e) {
+ Log.d(TAG, "dialog " + id + " failed: " + e);
+ }
+ }
+ };
+ final Account account = new Account(mLongPressedAccount,
+ COM_GOOGLE_GAIA);
+ if (id == GET_AUTH_TOKEN_DIALOG_ID) {
+ mAccountManager.getAuthToken(account, authTokenType,
+ null /* loginOptions */, AccountsTester.this,
+ callback, null /* handler */);
+ } else if (id == INVALIDATE_AUTH_TOKEN_DIALOG_ID) {
+ mAccountManager.getAuthToken(account, authTokenType, false,
+ new GetAndInvalidateAuthTokenCallback(), null);
+ } else {
+ mAccountManager.updateCredentials(
+ account,
+ authTokenType, null /* loginOptions */,
+ AccountsTester.this, callback, null /* handler */);
+ }
+ }
+ });
+ builder.setView(view);
+ return builder.create();
+ }
+ return super.onCreateDialog(id);
+ }
+
+ AccountManagerCallback<Bundle> newAccountsCallback(String type, String[] features) {
+ return new GetAccountsCallback(type, features);
+ }
+
+ class GetAccountsCallback implements AccountManagerCallback<Bundle> {
+ final String[] mFeatures;
+ final String mAccountType;
+
+ public GetAccountsCallback(String type, String[] features) {
+ mFeatures = features;
+ mAccountType = type;
+ }
+
+ public void run(AccountManagerFuture<Bundle> future) {
+ Log.d(TAG, "GetAccountsCallback: type " + mAccountType
+ + ", features "
+ + (mFeatures == null ? "none" : TextUtils.join(",", mFeatures)));
+ try {
+ Bundle result = future.getResult();
+ Parcelable[] accounts = result.getParcelableArray(Constants.ACCOUNTS_KEY);
+ Log.d(TAG, "found " + accounts.length + " accounts");
+ for (Parcelable account : accounts) {
+ Log.d(TAG, " " + account);
+ }
+ } catch (OperationCanceledException e) {
+ Log.d(TAG, "failure", e);
+ } catch (IOException e) {
+ Log.d(TAG, "failure", e);
+ } catch (AuthenticatorException e) {
+ Log.d(TAG, "failure", e);
+ }
+ }
+ }
+
+ AccountManagerCallback<Bundle> newAuthTokensCallback(String type, String authTokenType, String[] features) {
+ return new GetAuthTokenCallback(type, authTokenType, features);
+ }
+
+ class GetAuthTokenCallback implements AccountManagerCallback<Bundle> {
+ final String[] mFeatures;
+ final String mAccountType;
+ final String mAuthTokenType;
+
+ public GetAuthTokenCallback(String type, String authTokenType, String[] features) {
+ mFeatures = features;
+ mAccountType = type;
+ mAuthTokenType = authTokenType;
+ }
+
+ public void run(AccountManagerFuture<Bundle> future) {
+ Log.d(TAG, "GetAuthTokenCallback: type " + mAccountType
+ + ", features "
+ + (mFeatures == null ? "none" : TextUtils.join(",", mFeatures)));
+ try {
+ Bundle result = future.getResult();
+ result.keySet();
+ Log.d(TAG, " result: " + result);
+ } catch (OperationCanceledException e) {
+ Log.d(TAG, "failure", e);
+ } catch (IOException e) {
+ Log.d(TAG, "failure", e);
+ } catch (AuthenticatorException e) {
+ Log.d(TAG, "failure", e);
+ }
+ }
+ }
+
+ private class GetAndInvalidateAuthTokenCallback implements AccountManagerCallback<Bundle> {
+ public void run(AccountManagerFuture<Bundle> future) {
+ try {
+ Bundle bundle = future.getResult();
+ String authToken = bundle.getString(Constants.AUTHTOKEN_KEY);
+ mAccountManager.invalidateAuthToken(COM_GOOGLE_GAIA, authToken);
+ } catch (OperationCanceledException e) {
+ Log.d(TAG, "invalidate: interrupted while getting authToken");
+ } catch (IOException e) {
+ Log.d(TAG, "invalidate: error getting authToken", e);
+ } catch (AuthenticatorException e) {
+ Log.d(TAG, "invalidate: error getting authToken", e);
+ }
+ }
+ }
+
+ private static class ConfirmCredentialsCallback implements AccountManagerCallback<Bundle> {
+ public void run(AccountManagerFuture<Bundle> future) {
+ try {
+ Bundle bundle = future.getResult();
+ bundle.keySet();
+ Log.d(TAG, "confirmCredentials success: " + bundle);
+ } catch (OperationCanceledException e) {
+ Log.d(TAG, "confirmCredentials canceled");
+ } catch (AuthenticatorException e) {
+ Log.d(TAG, "confirmCredentials failed: " + e);
+ } catch (IOException e) {
+ Log.d(TAG, "confirmCredentials failed: " + e);
+ }
+ }
+ }
+}
diff --git a/apps/Development/src/com/android/development/PointerLocation.java b/apps/Development/src/com/android/development/PointerLocation.java
index 668e9ba..9935b7e 100644
--- a/apps/Development/src/com/android/development/PointerLocation.java
+++ b/apps/Development/src/com/android/development/PointerLocation.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
-import android.graphics.Rect;
import android.graphics.Paint.FontMetricsInt;
import android.os.Bundle;
import android.util.Log;
@@ -47,16 +46,9 @@
getWindow().setAttributes(lp);
}
- public class MyView extends View {
- private final Paint mTextPaint;
- private final Paint mTextBackgroundPaint;
- private final Paint mTextLevelPaint;
- private final Paint mPaint;
- private final Paint mTargetPaint;
- private final FontMetricsInt mTextMetrics = new FontMetricsInt();
+ public static class PointerState {
private final ArrayList<Float> mXs = new ArrayList<Float>();
private final ArrayList<Float> mYs = new ArrayList<Float>();
- private int mHeaderBottom;
private boolean mCurDown;
private int mCurX;
private int mCurY;
@@ -64,12 +56,29 @@
private float mCurSize;
private int mCurWidth;
private VelocityTracker mVelocity;
+ }
+
+ public class MyView extends View {
+ private final Paint mTextPaint;
+ private final Paint mTextBackgroundPaint;
+ private final Paint mTextLevelPaint;
+ private final Paint mPaint;
+ private final Paint mTargetPaint;
+ private final Paint mPathPaint;
+ private final FontMetricsInt mTextMetrics = new FontMetricsInt();
+ private int mHeaderBottom;
+ private boolean mCurDown;
+ private int mCurNumPointers;
+ private int mMaxNumPointers;
+ private final ArrayList<PointerState> mPointers
+ = new ArrayList<PointerState>();
public MyView(Context c) {
super(c);
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
- mTextPaint.setTextSize(10);
+ mTextPaint.setTextSize(10
+ * getResources().getDisplayMetrics().density);
mTextPaint.setARGB(255, 0, 0, 0);
mTextBackgroundPaint = new Paint();
mTextBackgroundPaint.setAntiAlias(false);
@@ -84,9 +93,16 @@
mPaint.setStrokeWidth(2);
mTargetPaint = new Paint();
mTargetPaint.setAntiAlias(false);
- mTargetPaint.setARGB(192, 0, 0, 255);
+ mTargetPaint.setARGB(255, 0, 0, 192);
+ mPathPaint = new Paint();
+ mPathPaint.setAntiAlias(false);
+ mPathPaint.setARGB(255, 0, 96, 255);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(1);
+
+ PointerState ps = new PointerState();
+ ps.mVelocity = VelocityTracker.obtain();
+ mPointers.add(ps);
}
@Override
@@ -103,58 +119,106 @@
@Override
protected void onDraw(Canvas canvas) {
- int w = getWidth()/5;
- int base = -mTextMetrics.ascent+1;
- int bottom = mHeaderBottom;
- canvas.drawRect(0, 0, w-1, bottom, mTextBackgroundPaint);
- canvas.drawText("X: " + mCurX, 1, base, mTextPaint);
- canvas.drawRect(w, 0, (w * 2) - 1, bottom, mTextBackgroundPaint);
- canvas.drawText("Y: " + mCurY, 1 + w, base, mTextPaint);
- canvas.drawRect(w * 2, 0, (w * 3) - 1, bottom, mTextBackgroundPaint);
- canvas.drawRect(w * 2, 0, (w * 2) + (mCurPressure * w) - 1, bottom, mTextLevelPaint);
- canvas.drawText("Pres: " + mCurPressure, 1 + w * 2, base, mTextPaint);
- canvas.drawRect(w * 3, 0, (w * 4) - 1, bottom, mTextBackgroundPaint);
- canvas.drawRect(w * 3, 0, (w * 3) + (mCurSize * w) - 1, bottom, mTextLevelPaint);
- canvas.drawText("Size: " + mCurSize, 1 + w * 3, base, mTextPaint);
- canvas.drawRect(w * 4, 0, getWidth(), bottom, mTextBackgroundPaint);
- int velocity = mVelocity == null ? 0 : (int) (mVelocity.getYVelocity() * 1000);
- canvas.drawText("yVel: " + velocity, 1 + w * 4, base, mTextPaint);
+ final int w = getWidth();
+ final int itemW = w/7;
+ final int base = -mTextMetrics.ascent+1;
+ final int bottom = mHeaderBottom;
- final int N = mXs.size();
- float lastX=0, lastY=0;
- mPaint.setARGB(255, 0, 255, 255);
- for (int i=0; i<N; i++) {
- float x = mXs.get(i);
- float y = mYs.get(i);
- if (i > 0) {
- canvas.drawLine(lastX, lastY, x, y, mTargetPaint);
- canvas.drawPoint(lastX, lastY, mPaint);
+ final int NP = mPointers.size();
+
+ if (NP > 0) {
+ final PointerState ps = mPointers.get(0);
+ canvas.drawRect(0, 0, itemW-1, bottom,mTextBackgroundPaint);
+ canvas.drawText("P: " + mCurNumPointers + " / " + mMaxNumPointers,
+ 1, base, mTextPaint);
+
+ canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, mTextBackgroundPaint);
+ canvas.drawText("X: " + ps.mCurX, 1 + itemW, base, mTextPaint);
+
+ canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, mTextBackgroundPaint);
+ canvas.drawText("Y: " + ps.mCurY, 1 + itemW * 2, base, mTextPaint);
+
+ canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint);
+ int velocity = ps.mVelocity == null ? 0 : (int) (ps.mVelocity.getXVelocity() * 1000);
+ canvas.drawText("Xv: " + velocity, 1 + itemW * 3, base, mTextPaint);
+
+ canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint);
+ velocity = ps.mVelocity == null ? 0 : (int) (ps.mVelocity.getYVelocity() * 1000);
+ canvas.drawText("Yv: " + velocity, 1 + itemW * 4, base, mTextPaint);
+
+ canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint);
+ canvas.drawRect(itemW * 5, 0, (itemW * 5) + (ps.mCurPressure * itemW) - 1,
+ bottom, mTextLevelPaint);
+ canvas.drawText("Prs: " + String.format("%.2f", ps.mCurPressure), 1 + itemW * 5,
+ base, mTextPaint);
+
+ canvas.drawRect(itemW * 6, 0, w, bottom, mTextBackgroundPaint);
+ canvas.drawRect(itemW * 6, 0, (itemW * 6) + (ps.mCurSize * itemW) - 1,
+ bottom, mTextLevelPaint);
+ canvas.drawText("Size: " + String.format("%.2f", ps.mCurSize), 1 + itemW * 6,
+ base, mTextPaint);
+ }
+
+ for (int p=0; p<NP; p++) {
+ final PointerState ps = mPointers.get(p);
+
+ if (mCurDown && ps.mCurDown) {
+ canvas.drawLine(0, (int)ps.mCurY, getWidth(), (int)ps.mCurY, mTargetPaint);
+ canvas.drawLine((int)ps.mCurX, 0, (int)ps.mCurX, getHeight(), mTargetPaint);
+ int pressureLevel = (int)(ps.mCurPressure*255);
+ mPaint.setARGB(255, pressureLevel, 128, 255-pressureLevel);
+ canvas.drawPoint(ps.mCurX, ps.mCurY, mPaint);
+ canvas.drawCircle(ps.mCurX, ps.mCurY, ps.mCurWidth, mPaint);
}
- lastX = x;
- lastY = y;
- }
- if (mVelocity != null) {
- mPaint.setARGB(255, 255, 0, 0);
- float xVel = mVelocity.getXVelocity() * (1000/60);
- float yVel = mVelocity.getYVelocity() * (1000/60);
- canvas.drawLine(lastX, lastY, lastX+xVel, lastY+yVel, mPaint);
- } else {
- canvas.drawPoint(lastX, lastY, mPaint);
}
- if (mCurDown) {
- canvas.drawLine(0, (int)mCurY, getWidth(), (int)mCurY, mTargetPaint);
- canvas.drawLine((int)mCurX, 0, (int)mCurX, getHeight(), mTargetPaint);
- int pressureLevel = (int)(mCurPressure*255);
- mPaint.setARGB(255, pressureLevel, 128, 255-pressureLevel);
- canvas.drawPoint(mCurX, mCurY, mPaint);
- canvas.drawCircle(mCurX, mCurY, mCurWidth, mPaint);
+ for (int p=0; p<NP; p++) {
+ final PointerState ps = mPointers.get(p);
+
+ final int N = ps.mXs.size();
+ float lastX=0, lastY=0;
+ boolean haveLast = false;
+ boolean drawn = false;
+ mPaint.setARGB(255, 128, 255, 255);
+ for (int i=0; i<N; i++) {
+ float x = ps.mXs.get(i);
+ float y = ps.mYs.get(i);
+ if (Float.isNaN(x)) {
+ haveLast = false;
+ continue;
+ }
+ if (haveLast) {
+ canvas.drawLine(lastX, lastY, x, y, mPathPaint);
+ canvas.drawPoint(lastX, lastY, mPaint);
+ drawn = true;
+ }
+ lastX = x;
+ lastY = y;
+ haveLast = true;
+ }
+
+ if (drawn) {
+ if (ps.mVelocity != null) {
+ mPaint.setARGB(255, 255, 64, 128);
+ float xVel = ps.mVelocity.getXVelocity() * (1000/60);
+ float yVel = ps.mVelocity.getYVelocity() * (1000/60);
+ canvas.drawLine(lastX, lastY, lastX+xVel, lastY+yVel, mPaint);
+ } else {
+ canvas.drawPoint(lastX, lastY, mPaint);
+ }
+ }
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
+
+ //Log.i("Pointer", "Motion: action=0x" + Integer.toHexString(action)
+ // + " pointers=" + event.getPointerCount());
+
+ int NP = mPointers.size();
+
//mRect.set(0, 0, getWidth(), mHeaderBottom+1);
//invalidate(mRect);
//if (mCurDown) {
@@ -164,26 +228,69 @@
// mRect.setEmpty();
//}
if (action == MotionEvent.ACTION_DOWN) {
- mXs.clear();
- mYs.clear();
- mVelocity = VelocityTracker.obtain();
+ for (int p=0; p<NP; p++) {
+ final PointerState ps = mPointers.get(p);
+ ps.mXs.clear();
+ ps.mYs.clear();
+ ps.mVelocity = VelocityTracker.obtain();
+ ps.mCurDown = false;
+ }
+ mPointers.get(0).mCurDown = true;
+ mMaxNumPointers = 0;
}
- mVelocity.addMovement(event);
- mVelocity.computeCurrentVelocity(1);
- final int N = event.getHistorySize();
- for (int i=0; i<N; i++) {
- mXs.add(event.getHistoricalX(i));
- mYs.add(event.getHistoricalY(i));
+
+ if ((action&MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
+ final int id = (action&MotionEvent.ACTION_POINTER_ID_MASK)
+ >> MotionEvent.ACTION_POINTER_ID_SHIFT;
+ while (NP <= id) {
+ PointerState ps = new PointerState();
+ ps.mVelocity = VelocityTracker.obtain();
+ mPointers.add(ps);
+ NP++;
+ }
+ final PointerState ps = mPointers.get(id);
+ ps.mVelocity = VelocityTracker.obtain();
+ ps.mCurDown = true;
}
- mXs.add(event.getX());
- mYs.add(event.getY());
- mCurDown = action == MotionEvent.ACTION_DOWN
- || action == MotionEvent.ACTION_MOVE;
- mCurX = (int)event.getX();
- mCurY = (int)event.getY();
- mCurPressure = event.getPressure();
- mCurSize = event.getSize();
- mCurWidth = (int)(mCurSize*(getWidth()/3));
+
+ final int NI = event.getPointerCount();
+
+ mCurDown = action != MotionEvent.ACTION_UP
+ && action != MotionEvent.ACTION_CANCEL;
+ mCurNumPointers = mCurDown ? NI : 0;
+ if (mMaxNumPointers < mCurNumPointers) {
+ mMaxNumPointers = mCurNumPointers;
+ }
+
+ for (int i=0; i<NI; i++) {
+ final PointerState ps = mPointers.get(event.getPointerId(i));
+ ps.mVelocity.addMovement(event);
+ ps.mVelocity.computeCurrentVelocity(1);
+ final int N = event.getHistorySize();
+ for (int j=0; j<N; j++) {
+ ps.mXs.add(event.getHistoricalX(i, j));
+ ps.mYs.add(event.getHistoricalY(i, j));
+ }
+ ps.mXs.add(event.getX(i));
+ ps.mYs.add(event.getY(i));
+ ps.mCurX = (int)event.getX(i);
+ ps.mCurY = (int)event.getY(i);
+ //Log.i("Pointer", "Pointer #" + p + ": (" + ps.mCurX
+ // + "," + ps.mCurY + ")");
+ ps.mCurPressure = event.getPressure(i);
+ ps.mCurSize = event.getSize(i);
+ ps.mCurWidth = (int)(ps.mCurSize*(getWidth()/3));
+ }
+
+ if ((action&MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {
+ final int id = (action&MotionEvent.ACTION_POINTER_ID_MASK)
+ >> MotionEvent.ACTION_POINTER_ID_SHIFT;
+ final PointerState ps = mPointers.get(id);
+ ps.mXs.add(Float.NaN);
+ ps.mYs.add(Float.NaN);
+ ps.mCurDown = false;
+ }
+
//if (mCurDown) {
// mRect.union(mCurX-mCurWidth-3, mCurY-mCurWidth-3,
// mCurX+mCurWidth+3, mCurY+mCurWidth+3);
diff --git a/apps/Fallback/res/values-cs/strings.xml b/apps/Fallback/res/values-cs/strings.xml
index 1fc121c..b9d34f9 100644
--- a/apps/Fallback/res/values-cs/strings.xml
+++ b/apps/Fallback/res/values-cs/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"Záloha"</string>
- <string name="title" msgid="8156274565006125136">"Akce není podporována"</string>
- <string name="error" msgid="6539615832923362301">"Tato akce není momentálně podporována."</string>
+ <string name="appTitle">"Záloha"</string>
+ <string name="title">"Akce není podporována"</string>
+ <string name="error">"Tato akce není momentálně podporována."</string>
</resources>
diff --git a/apps/Fallback/res/values-da/strings.xml b/apps/Fallback/res/values-da/strings.xml
index 4584e61..b3dfa63 100644
--- a/apps/Fallback/res/values-da/strings.xml
+++ b/apps/Fallback/res/values-da/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"Reserve"</string>
- <string name="title" msgid="8156274565006125136">"Ikke understøttet handling"</string>
- <string name="error" msgid="6539615832923362301">"Handlingen er ikke understøttet i øjeblikket."</string>
+ <string name="appTitle">"Reserve"</string>
+ <string name="title">"Ikke understøttet handling"</string>
+ <string name="error">"Handlingen er ikke understøttet i øjeblikket."</string>
</resources>
diff --git a/apps/Fallback/res/values-de/strings.xml b/apps/Fallback/res/values-de/strings.xml
index 90bbb16..8d59ddf 100644
--- a/apps/Fallback/res/values-de/strings.xml
+++ b/apps/Fallback/res/values-de/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
- <string name="title" msgid="8156274565006125136">"Nicht unterstützte Aktion"</string>
- <string name="error" msgid="6539615832923362301">"Diese Aktion wird zurzeit nicht unterstützt."</string>
+ <string name="appTitle">"Fallback"</string>
+ <string name="title">"Nicht unterstützte Aktion"</string>
+ <string name="error">"Diese Aktion wird zurzeit nicht unterstützt."</string>
</resources>
diff --git a/apps/Fallback/res/values-el/strings.xml b/apps/Fallback/res/values-el/strings.xml
index b985af7..fecaf4a 100644
--- a/apps/Fallback/res/values-el/strings.xml
+++ b/apps/Fallback/res/values-el/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"Εναλλακτική"</string>
- <string name="title" msgid="8156274565006125136">"Ενέργεια που δεν υποστηρίζεται"</string>
- <string name="error" msgid="6539615832923362301">"Αυτή η ενέργεια δεν υποστηρίζεται αυτήν τη στιγμή."</string>
+ <string name="appTitle">"Εναλλακτική"</string>
+ <string name="title">"Ενέργεια που δεν υποστηρίζεται"</string>
+ <string name="error">"Αυτή η ενέργεια δεν υποστηρίζεται αυτήν τη στιγμή."</string>
</resources>
diff --git a/apps/Fallback/res/values-es-rUS/strings.xml b/apps/Fallback/res/values-es-rUS/strings.xml
index d15a287..0ce5751 100644
--- a/apps/Fallback/res/values-es-rUS/strings.xml
+++ b/apps/Fallback/res/values-es-rUS/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
- <string name="title" msgid="8156274565006125136">"Acción no admitida"</string>
- <string name="error" msgid="6539615832923362301">"Esa acción no se admite actualmente."</string>
+ <string name="appTitle">"Fallback"</string>
+ <string name="title">"Acción no admitida"</string>
+ <string name="error">"Esa acción no se admite actualmente."</string>
</resources>
diff --git a/apps/Fallback/res/values-es/strings.xml b/apps/Fallback/res/values-es/strings.xml
index d15a287..0ce5751 100644
--- a/apps/Fallback/res/values-es/strings.xml
+++ b/apps/Fallback/res/values-es/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
- <string name="title" msgid="8156274565006125136">"Acción no admitida"</string>
- <string name="error" msgid="6539615832923362301">"Esa acción no se admite actualmente."</string>
+ <string name="appTitle">"Fallback"</string>
+ <string name="title">"Acción no admitida"</string>
+ <string name="error">"Esa acción no se admite actualmente."</string>
</resources>
diff --git a/apps/Fallback/res/values-fr/strings.xml b/apps/Fallback/res/values-fr/strings.xml
index df1e299..024ae42 100644
--- a/apps/Fallback/res/values-fr/strings.xml
+++ b/apps/Fallback/res/values-fr/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"Application de secours"</string>
- <string name="title" msgid="8156274565006125136">"Action non prise en charge"</string>
- <string name="error" msgid="6539615832923362301">"Cette action n\'est actuellement pas prise en charge."</string>
+ <string name="appTitle">"Application de secours"</string>
+ <string name="title">"Action non prise en charge"</string>
+ <string name="error">"Cette action n\'est actuellement pas prise en charge."</string>
</resources>
diff --git a/apps/Fallback/res/values-it/strings.xml b/apps/Fallback/res/values-it/strings.xml
index 2d08c1b..d216e59 100644
--- a/apps/Fallback/res/values-it/strings.xml
+++ b/apps/Fallback/res/values-it/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
- <string name="title" msgid="8156274565006125136">"Azione non supportata"</string>
- <string name="error" msgid="6539615832923362301">"L\'azione non è al momento supportata."</string>
+ <string name="appTitle">"Fallback"</string>
+ <string name="title">"Azione non supportata"</string>
+ <string name="error">"L\'azione non è al momento supportata."</string>
</resources>
diff --git a/apps/Fallback/res/values-ja/strings.xml b/apps/Fallback/res/values-ja/strings.xml
index f220909..79aeb42 100644
--- a/apps/Fallback/res/values-ja/strings.xml
+++ b/apps/Fallback/res/values-ja/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
- <string name="title" msgid="8156274565006125136">"サポートされていない操作"</string>
- <string name="error" msgid="6539615832923362301">"現在サポートされていない操作です。"</string>
+ <string name="appTitle">"Fallback"</string>
+ <string name="title">"サポートされていない操作"</string>
+ <string name="error">"現在サポートされていない操作です。"</string>
</resources>
diff --git a/apps/Fallback/res/values-ko/strings.xml b/apps/Fallback/res/values-ko/strings.xml
index 2c2973f..ec1c330 100644
--- a/apps/Fallback/res/values-ko/strings.xml
+++ b/apps/Fallback/res/values-ko/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"폴백"</string>
- <string name="title" msgid="8156274565006125136">"지원되지 않는 작업"</string>
- <string name="error" msgid="6539615832923362301">"이 작업은 현재 지원되지 않습니다."</string>
+ <string name="appTitle">"폴백"</string>
+ <string name="title">"지원되지 않는 작업"</string>
+ <string name="error">"이 작업은 현재 지원되지 않습니다."</string>
</resources>
diff --git a/apps/Fallback/res/values-nb/strings.xml b/apps/Fallback/res/values-nb/strings.xml
index 02814e6..6fed660 100644
--- a/apps/Fallback/res/values-nb/strings.xml
+++ b/apps/Fallback/res/values-nb/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
- <string name="title" msgid="8156274565006125136">"Ustøttet handling"</string>
- <string name="error" msgid="6539615832923362301">"Denne handlingen er ikke støttet nå."</string>
+ <string name="appTitle">"Fallback"</string>
+ <string name="title">"Ustøttet handling"</string>
+ <string name="error">"Denne handlingen er ikke støttet nå."</string>
</resources>
diff --git a/apps/Fallback/res/values-nl/strings.xml b/apps/Fallback/res/values-nl/strings.xml
index 8989efd..f347964 100644
--- a/apps/Fallback/res/values-nl/strings.xml
+++ b/apps/Fallback/res/values-nl/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"Reserve"</string>
- <string name="title" msgid="8156274565006125136">"Niet-ondersteunde actie"</string>
- <string name="error" msgid="6539615832923362301">"Die actie wordt momenteel niet ondersteund."</string>
+ <string name="appTitle">"Reserve"</string>
+ <string name="title">"Niet-ondersteunde actie"</string>
+ <string name="error">"Die actie wordt momenteel niet ondersteund."</string>
</resources>
diff --git a/apps/Fallback/res/values-pl/strings.xml b/apps/Fallback/res/values-pl/strings.xml
index 5740498..73a176a 100644
--- a/apps/Fallback/res/values-pl/strings.xml
+++ b/apps/Fallback/res/values-pl/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"Wycofanie"</string>
- <string name="title" msgid="8156274565006125136">"Nieobsługiwana czynność"</string>
- <string name="error" msgid="6539615832923362301">"Ta czynność nie jest aktualnie obsługiwana."</string>
+ <string name="appTitle">"Wycofanie"</string>
+ <string name="title">"Nieobsługiwana czynność"</string>
+ <string name="error">"Ta czynność nie jest aktualnie obsługiwana."</string>
</resources>
diff --git a/apps/Fallback/res/values-pt-rPT/strings.xml b/apps/Fallback/res/values-pt-rPT/strings.xml
index b226ea5..3c7ec9d 100644
--- a/apps/Fallback/res/values-pt-rPT/strings.xml
+++ b/apps/Fallback/res/values-pt-rPT/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
- <string name="title" msgid="8156274565006125136">"Acção não suportada"</string>
- <string name="error" msgid="6539615832923362301">"Esta acção ainda não é suportada."</string>
+ <string name="appTitle">"Fallback"</string>
+ <string name="title">"Acção não suportada"</string>
+ <string name="error">"Esta·acção·ainda·não·é·suportada."</string>
</resources>
diff --git a/apps/Fallback/res/values-pt/strings.xml b/apps/Fallback/res/values-pt/strings.xml
index 395004c..08a3faa 100644
--- a/apps/Fallback/res/values-pt/strings.xml
+++ b/apps/Fallback/res/values-pt/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
- <string name="title" msgid="8156274565006125136">"Ação não suportada"</string>
- <string name="error" msgid="6539615832923362301">"Essa ação não é suportada no momento."</string>
+ <string name="appTitle">"Fallback"</string>
+ <string name="title">"Ação não suportada"</string>
+ <string name="error">"Essa ação não é suportada no momento."</string>
</resources>
diff --git a/apps/Fallback/res/values-ru/strings.xml b/apps/Fallback/res/values-ru/strings.xml
index 084c3f3..24c3480 100644
--- a/apps/Fallback/res/values-ru/strings.xml
+++ b/apps/Fallback/res/values-ru/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"Переход в обходной режим"</string>
- <string name="title" msgid="8156274565006125136">"Неподдерживаемое действие"</string>
- <string name="error" msgid="6539615832923362301">"В настоящее время это действие не поддерживается."</string>
+ <string name="appTitle">"Переход в обходной режим"</string>
+ <string name="title">"Неподдерживаемое действие"</string>
+ <string name="error">"В настоящее время это действие не поддерживается."</string>
</resources>
diff --git a/apps/Fallback/res/values-sv/strings.xml b/apps/Fallback/res/values-sv/strings.xml
index 224d946..9dae10d 100644
--- a/apps/Fallback/res/values-sv/strings.xml
+++ b/apps/Fallback/res/values-sv/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"Reserv"</string>
- <string name="title" msgid="8156274565006125136">"Åtgärden stöds inte"</string>
- <string name="error" msgid="6539615832923362301">"Den här åtgärden stöds inte för närvarande."</string>
+ <string name="appTitle">"Reserv"</string>
+ <string name="title">"Åtgärden stöds inte"</string>
+ <string name="error">"Den här åtgärden stöds inte för närvarande."</string>
</resources>
diff --git a/apps/Fallback/res/values-tr/strings.xml b/apps/Fallback/res/values-tr/strings.xml
index 9c7ed15..27b860a 100644
--- a/apps/Fallback/res/values-tr/strings.xml
+++ b/apps/Fallback/res/values-tr/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
- <string name="title" msgid="8156274565006125136">"Desteklenmeyen işlem"</string>
- <string name="error" msgid="6539615832923362301">"Bu işlem şu an desteklenmiyor."</string>
+ <string name="appTitle">"Fallback"</string>
+ <string name="title">"Desteklenmeyen işlem"</string>
+ <string name="error">"Bu işlem şu an desteklenmiyor."</string>
</resources>
diff --git a/apps/Fallback/res/values-zh-rCN/strings.xml b/apps/Fallback/res/values-zh-rCN/strings.xml
index 5b1fbf8..e6cfde1 100644
--- a/apps/Fallback/res/values-zh-rCN/strings.xml
+++ b/apps/Fallback/res/values-zh-rCN/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"后备"</string>
- <string name="title" msgid="8156274565006125136">"不支持此操作"</string>
- <string name="error" msgid="6539615832923362301">"目前不支持该操作。"</string>
+ <string name="appTitle">"后备"</string>
+ <string name="title">"不支持此操作"</string>
+ <string name="error">"目前不支持该操作。"</string>
</resources>
diff --git a/apps/Fallback/res/values-zh-rTW/strings.xml b/apps/Fallback/res/values-zh-rTW/strings.xml
index 04d7f4f..52afdbe 100644
--- a/apps/Fallback/res/values-zh-rTW/strings.xml
+++ b/apps/Fallback/res/values-zh-rTW/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"備用"</string>
- <string name="title" msgid="8156274565006125136">"不支援的操作"</string>
- <string name="error" msgid="6539615832923362301">"目前不支援此操作。"</string>
+ <string name="appTitle">"備用"</string>
+ <string name="title">"不支援的操作"</string>
+ <string name="error">"目前不支援此操作。"</string>
</resources>
diff --git a/apps/FontLab/src/com/android/fontlab/FontLab.java b/apps/FontLab/src/com/android/fontlab/FontLab.java
index 27f2d4c..611d34d 100644
--- a/apps/FontLab/src/com/android/fontlab/FontLab.java
+++ b/apps/FontLab/src/com/android/fontlab/FontLab.java
@@ -30,8 +30,7 @@
import android.view.View;
import android.widget.TextView;
-class FontLab extends Activity
-{
+public class FontLab extends Activity {
private static final int MIN_SIZE = 1;
private static final int MAX_SIZE = 60;
@@ -39,6 +38,10 @@
private static final int MAX_SCALE_X = 20;
private static final int MIN_SCALE_X = -19; // -20 would make zero-scale
+ private static final int MAX_GAMMA = 40;
+ private static final int MIN_GAMMA = 1;
+ private static final float GAMMA_RANGE = 20;
+
private static final String[] sText = {
"Applications Contacts Maps Google Browser Text messages Address book"
+ " Development Earth Quake Settings Voicemail Zoolander. Four score"
@@ -107,13 +110,17 @@
updateText();
setDefaultKeyMode(Activity.DEFAULT_KEYS_SHORTCUT);
+
+ mColumn1.setPadding(5, 0, 5, 0);
+ mColumn2.setPadding(5, 0, 5, 0);
}
private void updateTitle() {
Typeface tf = mColumn1.getTypeface();
- String title = " ps=" + mFontSize + " scaleX="
+ String title = " PS=" + mFontSize + " X="
+ (1 + mTextScaleXDelta/SCALE_X_RANGE)
- + " gamma=" + (mGamma/20.f)
+ + " G=" + (mGamma/GAMMA_RANGE)
+ + " S=" + ((mColumn1.getPaintFlags() & Paint.SUBPIXEL_TEXT_FLAG) != 0 ? 1 : 0)
+ " " + sTypefaceName[mFontIndex]
+ " " + sStyleName[tf.getStyle()]
;
@@ -133,9 +140,9 @@
"Regular", "Bold", "Italic", "Bold Italic"
};
private static final String sTypefaceName[] = {
- "Droid Sans",
- "Droid Serif",
- "Droid Mono"
+ "Sans",
+ "Serif",
+ "Mono"
};
private static final Typeface sTypeface[] = {
Typeface.SANS_SERIF,
@@ -198,6 +205,7 @@
int mask = item.getItemId();
mColumn1.setPaintFlags(mColumn1.getPaintFlags() ^ mask);
mColumn2.setPaintFlags(mColumn2.getPaintFlags() ^ mask);
+ updateTitle();
return true;
}
};
@@ -233,10 +241,11 @@
addFontMenu(menu, FONT_INDEX_SANS);
addFontMenu(menu, FONT_INDEX_SERIF);
- addFontMenu(menu, FONT_INDEX_MONO);
+// addFontMenu(menu, FONT_INDEX_MONO);
addStyleMenu(menu, Typeface.BOLD, 'b');
addStyleMenu(menu, Typeface.ITALIC, 'i');
- addFlagMenu(menu, Paint.DEV_KERN_TEXT_FLAG, "DevKern", 'k');
+ addFlagMenu(menu, Paint.SUBPIXEL_TEXT_FLAG, "SubPixel", 's');
+ // addFlagMenu(menu, Paint.DEV_KERN_TEXT_FLAG, "DevKern", 'k');
menu.add(0, 0, 0, "Text").setOnMenuItemClickListener(mTextCallback).setAlphabeticShortcut('t');
return true;
@@ -286,14 +295,14 @@
case KeyEvent.KEYCODE_DPAD_LEFT:
scaleX -= 1;
break;
- /*
case KeyEvent.KEYCODE_U:
+ case KeyEvent.KEYCODE_VOLUME_UP:
changeGamma(1);
return true;
case KeyEvent.KEYCODE_D:
+ case KeyEvent.KEYCODE_VOLUME_DOWN:
changeGamma(-1);
return true;
- */
default:
return super.onKeyDown(keyCode, event);
}
@@ -318,16 +327,18 @@
return super.onKeyDown(keyCode, event);
}
-
- private int mGamma = 28; // current default is 1.4 (* 20)
+
+ // default to gamma of 1.0
+ private int mGamma = Math.round(1.0f * GAMMA_RANGE);
+
private void changeGamma(int delta) {
- int gamma = Math.min(100, Math.max(1, mGamma + delta));
+ int gamma = Math.min(MAX_GAMMA, Math.max(MIN_GAMMA, mGamma + delta));
if (gamma != mGamma) {
mGamma = gamma;
updateTitle();
-// Paint.setTextGamma(mGamma / 20.f);
+ float blackGamma = mGamma / GAMMA_RANGE;
+ Typeface.setGammaForText(blackGamma, 1 / blackGamma);
mContentView.invalidate();
- android.util.Log.d("skia", "setTextGamma " + mGamma);
}
}
diff --git a/apps/Term/Android.mk b/apps/Term/Android.mk
index 843aec5..9ff6c0d 100644
--- a/apps/Term/Android.mk
+++ b/apps/Term/Android.mk
@@ -1,3 +1,26 @@
+#
+# 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.
+#
+
+# This makefile shows how to build a shared library and an activity that
+# bundles the shared library and calls it using JNI.
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build activity
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
@@ -7,4 +30,11 @@
LOCAL_PACKAGE_NAME := Term
+LOCAL_JNI_SHARED_LIBRARIES := libterm
+
include $(BUILD_PACKAGE)
+
+# ============================================================
+
+# Also build all of the sub-targets under this one: the shared library.
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/apps/Term/jni/Android.mk b/apps/Term/jni/Android.mk
new file mode 100644
index 0000000..2fe4a75
--- /dev/null
+++ b/apps/Term/jni/Android.mk
@@ -0,0 +1,54 @@
+#
+# 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.
+#
+
+# This makefile supplies the rules for building a library of JNI code for
+# use by our example of how to bundle a shared library with an APK.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := eng
+
+# This is the target being built.
+LOCAL_MODULE:= libterm
+
+
+# All of the source files that we will compile.
+LOCAL_SRC_FILES:= \
+ termExec.cpp
+
+# All of the shared libraries we link against.
+LOCAL_SHARED_LIBRARIES := \
+ libutils
+
+# No static libraries.
+LOCAL_STATIC_LIBRARIES :=
+
+# Also need the JNI headers.
+LOCAL_C_INCLUDES += \
+ $(JNI_H_INCLUDE)
+
+# No special compiler flags.
+LOCAL_CFLAGS +=
+
+# Don't prelink this library. For more efficient code, you may want
+# to add this library to the prelink map and set this to true. However,
+# it's difficult to do this for applications that are not supplied as
+# part of a system image.
+
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/apps/Term/jni/termExec.cpp b/apps/Term/jni/termExec.cpp
new file mode 100644
index 0000000..d0666cc
--- /dev/null
+++ b/apps/Term/jni/termExec.cpp
@@ -0,0 +1,347 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Exec"
+
+#include "jni.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <termios.h>
+
+static jclass class_fileDescriptor;
+static jfieldID field_fileDescriptor_descriptor;
+static jmethodID method_fileDescriptor_init;
+
+
+class String8 {
+public:
+ String8() {
+ mString = 0;
+ }
+
+ ~String8() {
+ if (mString) {
+ free(mString);
+ }
+ }
+
+ void set(const char16_t* o, size_t numChars) {
+ mString = (char*) malloc(numChars + 1);
+ for (size_t i = 0; i < numChars; i++) {
+ mString[i] = (char) o[i];
+ }
+ mString[numChars] = '\0';
+ }
+
+ const char* string() {
+ return mString;
+ }
+private:
+ char* mString;
+};
+
+static int create_subprocess(const char *cmd, const char *arg0, const char *arg1,
+ int* pProcessId)
+{
+ char *devname;
+ int ptm;
+ pid_t pid;
+
+ ptm = open("/dev/ptmx", O_RDWR); // | O_NOCTTY);
+ if(ptm < 0){
+ LOGE("[ cannot open /dev/ptmx - %s ]\n",strerror(errno));
+ return -1;
+ }
+ fcntl(ptm, F_SETFD, FD_CLOEXEC);
+
+ if(grantpt(ptm) || unlockpt(ptm) ||
+ ((devname = (char*) ptsname(ptm)) == 0)){
+ LOGE("[ trouble with /dev/ptmx - %s ]\n", strerror(errno));
+ return -1;
+ }
+
+ pid = fork();
+ if(pid < 0) {
+ LOGE("- fork failed: %s -\n", strerror(errno));
+ return -1;
+ }
+
+ if(pid == 0){
+ close(ptm);
+
+ int pts;
+
+ setsid();
+
+ pts = open(devname, O_RDWR);
+ if(pts < 0) exit(-1);
+
+ dup2(pts, 0);
+ dup2(pts, 1);
+ dup2(pts, 2);
+
+ execl(cmd, cmd, arg0, arg1, NULL);
+ exit(-1);
+ } else {
+ *pProcessId = (int) pid;
+ return ptm;
+ }
+}
+
+
+static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz,
+ jstring cmd, jstring arg0, jstring arg1, jintArray processIdArray)
+{
+ const jchar* str = cmd ? env->GetStringCritical(cmd, 0) : 0;
+ String8 cmd_8;
+ if (str) {
+ cmd_8.set(str, env->GetStringLength(cmd));
+ env->ReleaseStringCritical(cmd, str);
+ }
+
+ str = arg0 ? env->GetStringCritical(arg0, 0) : 0;
+ const char* arg0Str = 0;
+ String8 arg0_8;
+ if (str) {
+ arg0_8.set(str, env->GetStringLength(arg0));
+ env->ReleaseStringCritical(arg0, str);
+ arg0Str = arg0_8.string();
+ }
+
+ str = arg1 ? env->GetStringCritical(arg1, 0) : 0;
+ const char* arg1Str = 0;
+ String8 arg1_8;
+ if (str) {
+ arg1_8.set(str, env->GetStringLength(arg1));
+ env->ReleaseStringCritical(arg1, str);
+ arg1Str = arg1_8.string();
+ }
+
+ int procId;
+ int ptm = create_subprocess(cmd_8.string(), arg0Str, arg1Str, &procId);
+
+ if (processIdArray) {
+ int procIdLen = env->GetArrayLength(processIdArray);
+ if (procIdLen > 0) {
+ jboolean isCopy;
+
+ int* pProcId = (int*) env->GetPrimitiveArrayCritical(processIdArray, &isCopy);
+ if (pProcId) {
+ *pProcId = procId;
+ env->ReleasePrimitiveArrayCritical(processIdArray, pProcId, 0);
+ }
+ }
+ }
+
+ jobject result = env->NewObject(class_fileDescriptor, method_fileDescriptor_init);
+
+ if (!result) {
+ LOGE("Couldn't create a FileDescriptor.");
+ }
+ else {
+ env->SetIntField(result, field_fileDescriptor_descriptor, ptm);
+ }
+
+ return result;
+}
+
+
+static void android_os_Exec_setPtyWindowSize(JNIEnv *env, jobject clazz,
+ jobject fileDescriptor, jint row, jint col, jint xpixel, jint ypixel)
+{
+ int fd;
+ struct winsize sz;
+
+ fd = env->GetIntField(fileDescriptor, field_fileDescriptor_descriptor);
+
+ if (env->ExceptionOccurred() != NULL) {
+ return;
+ }
+
+ sz.ws_row = row;
+ sz.ws_col = col;
+ sz.ws_xpixel = xpixel;
+ sz.ws_ypixel = ypixel;
+
+ ioctl(fd, TIOCSWINSZ, &sz);
+}
+
+static int android_os_Exec_waitFor(JNIEnv *env, jobject clazz,
+ jint procId) {
+ int status;
+ waitpid(procId, &status, 0);
+ int result = 0;
+ if (WIFEXITED(status)) {
+ result = WEXITSTATUS(status);
+ }
+ return result;
+}
+
+static void android_os_Exec_close(JNIEnv *env, jobject clazz, jobject fileDescriptor)
+{
+ int fd;
+ struct winsize sz;
+
+ fd = env->GetIntField(fileDescriptor, field_fileDescriptor_descriptor);
+
+ if (env->ExceptionOccurred() != NULL) {
+ return;
+ }
+
+ close(fd);
+}
+
+
+static int register_FileDescriptor(JNIEnv *env)
+{
+ class_fileDescriptor = env->FindClass("java/io/FileDescriptor");
+
+ if (class_fileDescriptor == NULL) {
+ LOGE("Can't find java/io/FileDescriptor");
+ return -1;
+ }
+
+ field_fileDescriptor_descriptor = env->GetFieldID(class_fileDescriptor, "descriptor", "I");
+
+ if (field_fileDescriptor_descriptor == NULL) {
+ LOGE("Can't find FileDescriptor.descriptor");
+ return -1;
+ }
+
+ method_fileDescriptor_init = env->GetMethodID(class_fileDescriptor, "<init>", "()V");
+ if (method_fileDescriptor_init == NULL) {
+ LOGE("Can't find FileDescriptor.init");
+ return -1;
+ }
+ return 0;
+}
+
+
+static const char *classPathName = "com/android/term/Exec";
+
+static JNINativeMethod method_table[] = {
+ { "createSubprocess", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[I)Ljava/io/FileDescriptor;",
+ (void*) android_os_Exec_createSubProcess },
+ { "setPtyWindowSize", "(Ljava/io/FileDescriptor;IIII)V",
+ (void*) android_os_Exec_setPtyWindowSize},
+ { "waitFor", "(I)I",
+ (void*) android_os_Exec_waitFor},
+ { "close", "(Ljava/io/FileDescriptor;)V",
+ (void*) android_os_Exec_close}
+};
+
+/*
+ * Register several native methods for one class.
+ */
+static int registerNativeMethods(JNIEnv* env, const char* className,
+ JNINativeMethod* gMethods, int numMethods)
+{
+ jclass clazz;
+
+ clazz = env->FindClass(className);
+ if (clazz == NULL) {
+ LOGE("Native registration unable to find class '%s'", className);
+ return JNI_FALSE;
+ }
+ if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
+ LOGE("RegisterNatives failed for '%s'", className);
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+/*
+ * Register native methods for all classes we know about.
+ *
+ * returns JNI_TRUE on success.
+ */
+static int registerNatives(JNIEnv* env)
+{
+ if (!registerNativeMethods(env, classPathName, method_table,
+ sizeof(method_table) / sizeof(method_table[0]))) {
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+
+// ----------------------------------------------------------------------------
+
+/*
+ * This is called by the VM when the shared library is first loaded.
+ */
+
+typedef union {
+ JNIEnv* env;
+ void* venv;
+} UnionJNIEnvToVoid;
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+ UnionJNIEnvToVoid uenv;
+ uenv.venv = NULL;
+ jint result = -1;
+ JNIEnv* env = NULL;
+
+ LOGI("JNI_OnLoad");
+
+ if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
+ LOGE("ERROR: GetEnv failed");
+ goto bail;
+ }
+ env = uenv.env;
+
+ if ((result = register_FileDescriptor(env)) < 0) {
+ LOGE("ERROR: registerFileDescriptor failed");
+ goto bail;
+ }
+
+ if (registerNatives(env) != JNI_TRUE) {
+ LOGE("ERROR: registerNatives failed");
+ goto bail;
+ }
+
+ result = JNI_VERSION_1_4;
+
+bail:
+ return result;
+}
diff --git a/apps/Term/src/com/android/term/Exec.java b/apps/Term/src/com/android/term/Exec.java
new file mode 100644
index 0000000..b53acfc
--- /dev/null
+++ b/apps/Term/src/com/android/term/Exec.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.term;
+
+import java.io.FileDescriptor;
+
+/**
+ * Utility methods for creating and managing a subprocess.
+ * <p>
+ * Note: The native methods access a package-private
+ * java.io.FileDescriptor field to get and set the raw Linux
+ * file descriptor. This might break if the implementation of
+ * java.io.FileDescriptor is changed.
+ */
+
+public class Exec
+{
+ static {
+ System.loadLibrary("term");
+ }
+
+ /**
+ * Create a subprocess. Differs from java.lang.ProcessBuilder in
+ * that a pty is used to communicate with the subprocess.
+ * <p>
+ * Callers are responsible for calling Exec.close() on the returned
+ * file descriptor.
+ *
+ * @param cmd The command to execute
+ * @param arg0 The first argument to the command, may be null
+ * @param arg1 the second argument to the command, may be null
+ * @param processId A one-element array to which the process ID of the
+ * started process will be written.
+ * @return the file descriptor of the started process.
+ *
+ */
+ public static native FileDescriptor createSubprocess(
+ String cmd, String arg0, String arg1, int[] processId);
+
+ /**
+ * Set the widow size for a given pty. Allows programs
+ * connected to the pty learn how large their screen is.
+ */
+ public static native void setPtyWindowSize(FileDescriptor fd,
+ int row, int col, int xpixel, int ypixel);
+
+ /**
+ * Causes the calling thread to wait for the process associated with the
+ * receiver to finish executing.
+ *
+ * @return The exit value of the Process being waited on
+ *
+ */
+ public static native int waitFor(int processId);
+
+ /**
+ * Close a given file descriptor.
+ */
+ public static native void close(FileDescriptor fd);
+}
+
diff --git a/apps/Term/src/com/android/term/Term.java b/apps/Term/src/com/android/term/Term.java
index 34cd7e1..6041baf 100644
--- a/apps/Term/src/com/android/term/Term.java
+++ b/apps/Term/src/com/android/term/Term.java
@@ -16,6 +16,12 @@
package com.android.term;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
@@ -35,7 +41,6 @@
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Exec;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
@@ -54,12 +59,6 @@
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-
/**
* A terminal emulator activity.
*/
@@ -96,10 +95,7 @@
/**
* The pseudo-teletype (pty) file descriptor that we use to communicate with
- * another process, typically a shell. Currently we just use this to get the
- * mTermIn / mTermOut file descriptors, but when we implement resizing of
- * the terminal we will need it to issue the ioctl to inform the other
- * process that we've changed the terminal size.
+ * another process, typically a shell.
*/
private FileDescriptor mTermFd;
@@ -188,6 +184,15 @@
updatePrefs();
}
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mTermFd != null) {
+ Exec.close(mTermFd);
+ mTermFd = null;
+ }
+ }
+
private void startListening() {
int[] processId = new int[1];
diff --git a/build/Android.mk b/build/Android.mk
index cba96c6..d804ce7 100644
--- a/build/Android.mk
+++ b/build/Android.mk
@@ -4,7 +4,7 @@
# anywhere else, and the rules don't support. Aditionally, the depenencies on
# these files don't really matter, because they are all generated as part of
# building the docs. So for the dependency, we just use the
-# offline-sdk-timestamp file, which is the $@ of the droiddoc rule.
+# api-stubs-timestamp file, which is the $@ of the droiddoc rule.
# We also need to depend on framework-res.apk, in order to pull the
# resource files out of there for aapt.
#
@@ -22,7 +22,7 @@
$(full_target): PRIVATE_CLASS_INTERMEDIATES_DIR := $(classes_dir)
$(full_target): PRIVATE_FRAMEWORK_RES_PACKAGE := $(framework_res_package)
-$(full_target): $(OUT_DOCS)/offline-sdk-timestamp $(framework_res_package)
+$(full_target): $(OUT_DOCS)/api-stubs-timestamp $(framework_res_package)
@echo Compiling SDK Stubs: $@
$(hide) rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR)
$(hide) mkdir -p $(PRIVATE_CLASS_INTERMEDIATES_DIR)
diff --git a/build/tools/make_windows_sdk.sh b/build/tools/make_windows_sdk.sh
index 9b688e6..d694ea4 100755
--- a/build/tools/make_windows_sdk.sh
+++ b/build/tools/make_windows_sdk.sh
@@ -1,11 +1,17 @@
#!/bin/bash
# Quick semi-auto file to build Windows SDK tools.
#
-# Limitations:
+# Limitations and requirements:
# - Expects the emulator has been built first, will pick it up from prebuilt.
# - Run in Cygwin
-# - Needs Cygwin package zip
# - Expects to have one of the existing SDK (Darwin or Linux) to build the Windows one
+# - Needs Cygwin packages: autoconf, bison, curl, flex, gcc, g++, git,
+# gnupg, make, mingw-zlib, python, zip, unzip
+# - Must NOT have cygwin package readline (its GPL license might taint the SDK if
+# it gets compiled in)
+# - Does not need a Java Development Kit or any other tools outside of cygwin.
+# - If you think you may have Windows versions of tools (e.g. make) installed, it may
+# reduce confusion levels to 'export PATH=/usr/bin'
set -e # Fail this script as soon as a command fails -- fail early, fail fast
@@ -60,6 +66,10 @@
# SDK number if you want, but not after, e.g these are valid:
# android_sdk_4242_platform.zip or blah_42_.zip
#
+ # Note that the root directory name in the zip must match the zip
+ # name, too, so there's no point just changing the zip name to match
+ # the above format.
+ #
# SDK_NUMBER will be empty if nothing matched.
filename=`basename "$SDK_ZIP"`
SDK_NUMBER=`echo $filename | sed -n 's/^.*_\([^_./]\+\)_[^_.]*\..*$/\1/p'`
diff --git a/emulator/keymaps/AVRCP.kl b/emulator/keymaps/AVRCP.kl
index d0eba10..aeb3c8a 100644
--- a/emulator/keymaps/AVRCP.kl
+++ b/emulator/keymaps/AVRCP.kl
@@ -1,6 +1,7 @@
-key 164 MEDIA_PLAY_PAUSE WAKE
-key 128 MEDIA_STOP WAKE
+key 200 MEDIA_PLAY_PAUSE WAKE
+key 201 MEDIA_PLAY_PAUSE WAKE
+key 166 MEDIA_STOP WAKE
key 163 MEDIA_NEXT WAKE
key 165 MEDIA_PREVIOUS WAKE
key 168 MEDIA_REWIND WAKE
-key 159 MEDIA_FAST_FORWARD WAKE
+key 208 MEDIA_FAST_FORWARD WAKE
diff --git a/emulator/qtools/dmtrace.cpp b/emulator/qtools/dmtrace.cpp
index 6d9250a..c486c5f 100644
--- a/emulator/qtools/dmtrace.cpp
+++ b/emulator/qtools/dmtrace.cpp
@@ -5,6 +5,7 @@
#include <unistd.h>
#include <inttypes.h>
#include <string.h>
+#include <unistd.h>
#include "dmtrace.h"
static const short kVersion = 2;
@@ -163,7 +164,7 @@
// sig = "()I"
// Find the first parenthesis, the start of the signature.
- char *paren = strchr(name, '(');
+ char *paren = (char*)strchr(name, '(');
// If not found, then add the original name.
if (paren == NULL) {
@@ -180,7 +181,7 @@
*paren = 0;
// Search for the last period, the start of the method name
- char *dot = strrchr(name, '.');
+ char *dot = (char*)strrchr(name, '.');
// If not found, then add the original name.
if (dot == NULL || dot == name) {
diff --git a/emulator/qtools/trace_reader.cpp b/emulator/qtools/trace_reader.cpp
index d2af64f..47b5d93 100644
--- a/emulator/qtools/trace_reader.cpp
+++ b/emulator/qtools/trace_reader.cpp
@@ -1009,10 +1009,10 @@
// be freed by the caller after it is no longer needed.
static char *ExtractDexPathFromMmap(const char *mmap_path)
{
- char *end = rindex(mmap_path, '@');
+ const char *end = rindex(mmap_path, '@');
if (end == NULL)
return NULL;
- char *start = rindex(mmap_path, '/');
+ const char *start = rindex(mmap_path, '/');
if (start == NULL)
return NULL;
int len = end - start;
diff --git a/ide/eclipse/.classpath b/ide/eclipse/.classpath
index 71e8d29..71694d4 100644
--- a/ide/eclipse/.classpath
+++ b/ide/eclipse/.classpath
@@ -9,8 +9,6 @@
<classpathentry kind="src" path="packages/apps/Email/src"/>
<classpathentry kind="src" path="packages/apps/GoogleSearch/src"/>
<classpathentry kind="src" path="packages/apps/HTMLViewer/src"/>
- <classpathentry kind="src" path="packages/apps/IM/src"/>
- <classpathentry kind="src" path="packages/apps/IM/plugin"/>
<classpathentry kind="src" path="packages/apps/Launcher/src"/>
<classpathentry kind="src" path="packages/apps/Music/src"/>
<classpathentry kind="src" path="packages/apps/Mms/src"/>
@@ -28,8 +26,6 @@
<classpathentry kind="src" path="packages/providers/ImProvider/src"/>
<classpathentry kind="src" path="packages/providers/MediaProvider/src"/>
<classpathentry kind="src" path="packages/providers/TelephonyProvider/src"/>
- <classpathentry kind="src" path="vendor/google/apps/Street/src"/>
- <classpathentry kind="src" path="vendor/google/apps/YouTube/src"/>
<classpathentry kind="src" path="frameworks/base/awt"/>
<classpathentry kind="src" path="frameworks/base/cmds/am/src"/>
<classpathentry kind="src" path="frameworks/base/cmds/input/src"/>
@@ -39,6 +35,7 @@
<classpathentry kind="src" path="frameworks/base/core/config/sdk"/>
<classpathentry kind="src" path="frameworks/base/graphics/java"/>
<classpathentry kind="src" path="frameworks/base/im/java"/>
+ <classpathentry kind="src" path="frameworks/base/keystore/java"/>
<classpathentry kind="src" path="frameworks/base/location/java"/>
<classpathentry kind="src" path="frameworks/base/media/java"/>
<classpathentry kind="src" path="frameworks/base/opengl/java"/>
@@ -49,6 +46,7 @@
<classpathentry kind="src" path="frameworks/base/telephony/java"/>
<classpathentry kind="src" path="frameworks/base/test-runner"/>
<classpathentry kind="src" path="frameworks/base/tts/java"/>
+ <classpathentry kind="src" path="frameworks/base/vpn/java"/>
<classpathentry kind="src" path="frameworks/base/wifi/java"/>
<classpathentry kind="src" path="frameworks/policies/base/phone"/>
<classpathentry kind="src" path="development/samples/ApiDemos/src"/>
@@ -94,17 +92,15 @@
<classpathentry kind="src" path="dalvik/libcore/x-net/src/main/java"/>
<classpathentry kind="src" path="dalvik/libcore/xml/src/main/java"/>
<classpathentry kind="src" path="out/target/common/obj/APPS/ApiDemos_intermediates/src/src"/>
- <classpathentry kind="src" path="out/target/common/obj/APPS/Browser_intermediates/src/src"/>
- <classpathentry kind="src" path="out/target/common/obj/APPS/IM_intermediates/src/src"/>
+ <classpathentry kind="src" path="out/target/common/obj/APPS/Email_intermediates/src/src"/>
<classpathentry kind="src" path="out/target/common/obj/APPS/Music_intermediates/src/src"/>
<classpathentry kind="src" path="out/target/common/obj/APPS/Phone_intermediates/src/src"/>
<classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/com.android.im.plugin_intermediates/src"/>
<classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java"/>
- <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/im/java"/>
<classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/location/java"/>
<classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java"/>
<classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony/java"/>
- <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/tts/java"/>
+ <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/vpn/java"/>
<classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/wifi/java"/>
<classpathentry kind="src" path="out/target/common/R"/>
<classpathentry kind="src" path="external/tagsoup/src"/>
@@ -114,6 +110,6 @@
<classpathentry kind="lib" path="external/googleclient/googleclient-lib.jar"/>
<classpathentry kind="lib" path="out/target/common/obj/JAVA_LIBRARIES/google-framework_intermediates/javalib.jar"/>
<classpathentry kind="lib" path="out/target/common/obj/JAVA_LIBRARIES/googlelogin-client_intermediates/javalib.jar"/>
- <classpathentry kind="lib" path="packages/apps/Calculator/arity-1.3.1.jar"/>
+ <classpathentry kind="lib" path="packages/apps/Calculator/arity-1.3.3.jar"/>
<classpathentry kind="output" path="out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/classes"/>
</classpath>
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index 032f5c2..1e5de05 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -72,6 +72,15 @@
</intent-filter>
</activity>
+ <activity android:name=".app.WallpaperActivity"
+ android:label="@string/activity_wallpaper"
+ android:theme="@style/Theme.Wallpaper">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.SAMPLE_CODE" />
+ </intent-filter>
+ </activity>
+
<activity android:name=".app.TranslucentActivity"
android:label="@string/activity_translucent"
android:theme="@style/Theme.Translucent">
@@ -1562,6 +1571,13 @@
</intent-filter>
</activity>
+ <activity android:name=".graphics.ColorFilters" android:label="Graphics/ColorFilters">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.SAMPLE_CODE" />
+ </intent-filter>
+ </activity>
+
<activity android:name=".graphics.CreateBitmap" android:label="Graphics/CreateBitmap">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/samples/ApiDemos/res/drawable/btn_check_off.png b/samples/ApiDemos/res/drawable/btn_check_off.png
new file mode 100644
index 0000000..56d3861
--- /dev/null
+++ b/samples/ApiDemos/res/drawable/btn_check_off.png
Binary files differ
diff --git a/samples/ApiDemos/res/drawable/btn_check_on.png b/samples/ApiDemos/res/drawable/btn_check_on.png
new file mode 100644
index 0000000..791ac1d
--- /dev/null
+++ b/samples/ApiDemos/res/drawable/btn_check_on.png
Binary files differ
diff --git a/samples/ApiDemos/res/drawable/btn_circle_normal.png b/samples/ApiDemos/res/drawable/btn_circle_normal.png
new file mode 100644
index 0000000..fc5af1c
--- /dev/null
+++ b/samples/ApiDemos/res/drawable/btn_circle_normal.png
Binary files differ
diff --git a/samples/ApiDemos/res/drawable/btn_default_normal.9.png b/samples/ApiDemos/res/drawable/btn_default_normal.9.png
new file mode 100644
index 0000000..a2d5ccd
--- /dev/null
+++ b/samples/ApiDemos/res/drawable/btn_default_normal.9.png
Binary files differ
diff --git a/samples/ApiDemos/res/layout/service_start_arguments_controller.xml b/samples/ApiDemos/res/layout/service_start_arguments_controller.xml
index f10a2c3..19b7498 100644
--- a/samples/ApiDemos/res/layout/service_start_arguments_controller.xml
+++ b/samples/ApiDemos/res/layout/service_start_arguments_controller.xml
@@ -43,5 +43,15 @@
android:text="@string/start3_service">
</Button>
+ <Button android:id="@+id/startfail"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="@string/startfail_service">
+ </Button>
+
+ <Button android:id="@+id/kill"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="@string/kill_process">
+ </Button>
+
</LinearLayout>
diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml
index c2c9829..a1f2000 100644
--- a/samples/ApiDemos/res/values/strings.xml
+++ b/samples/ApiDemos/res/values/strings.xml
@@ -38,6 +38,8 @@
custom Theme.Dialog theme to make an activity that looks like a
customized dialog, here with an ugly frame.</string>
+ <string name="activity_wallpaper">App/Activity/Wallpaper</string>
+
<string name="activity_translucent">App/Activity/Translucent</string>
<string name="translucent_background">Example of how you can make an
activity have a translucent background, compositing over
@@ -115,9 +117,6 @@
<string name="remote_service_disconnected">Disconnected from remote service</string>
<string name="remote_call_failed">Failure calling remote service</string>
- <string name="service_arguments_started">"Started with arguments: "</string>
- <string name="service_arguments_stopped">Finished arguments,
- stopping.</string>
<string name="service_start_arguments_label">Sample Service Start Arguments
</string>
@@ -128,10 +127,13 @@
service can be started with arguments, and run until all arguments are
processed.
</string>
- <string name="start1_service">Start with \"One\"</string>
- <string name="start2_service">Start with \"Two\"</string>
- <string name="start3_service">Start with \"Three\"</string>
-
+ <string name="start1_service">Start \"One\" no redeliver</string>
+ <string name="start2_service">Start \"Two\" no redeliver</string>
+ <string name="start3_service">Start \"Three\" w/redeliver</string>
+ <string name="startfail_service">Start failed delivery</string>
+ <string name="service_created">Service created.</string>
+ <string name="service_destroyed">Service destroyed.</string>
+
<string name="one_shot_received">The one-shot alarm has gone off</string>
<string name="repeating_received">The repeating alarm has gone off</string>
diff --git a/samples/ApiDemos/res/values/styles.xml b/samples/ApiDemos/res/values/styles.xml
index 3c9c681..8cc8312 100644
--- a/samples/ApiDemos/res/values/styles.xml
+++ b/samples/ApiDemos/res/values/styles.xml
@@ -37,9 +37,16 @@
<item name="android:windowBackground">@drawable/filled_box</item>
</style>
+ <!-- A theme that has a wallpaper background. Here we explicitly specify
+ that this theme is to inherit from the system's wallpaper theme,
+ which sets up various attributes correctly. -->
+ <style name="Theme.Wallpaper" parent="android:style/Theme.Wallpaper">
+ <item name="android:colorForeground">#fff</item>
+ </style>
+
<!-- A theme that has a translucent background. Here we explicitly specify
that this theme is to inherit from the system's translucent theme,
- which sets up various attributes correctly.. -->
+ which sets up various attributes correctly. -->
<style name="Theme.Translucent" parent="android:style/Theme.Translucent">
<item name="android:windowBackground">@drawable/translucent_background</item>
<item name="android:windowNoTitle">true</item>
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArguments.java b/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArguments.java
index 5e16158..f4f9af1 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArguments.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArguments.java
@@ -27,6 +27,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.util.Log;
import android.widget.Toast;
@@ -41,9 +42,12 @@
* happen in the service. This is generally how background services should
* interact with the user, rather than doing something more disruptive such as
* calling startActivity().
+ *
+ * <p>For applications targeting Android 1.5 or beyond, you may want consider
+ * using the android.app.IntentService class, which takes care of all the
+ * work of creating the extra thread and dispatching commands to it.
*/
-public class ServiceStartArguments extends Service
-{
+public class ServiceStartArguments extends Service {
private NotificationManager mNM;
private Intent mInvokeIntent;
private volatile Looper mServiceLooper;
@@ -58,16 +62,22 @@
public void handleMessage(Message msg)
{
Bundle arguments = (Bundle)msg.obj;
- String txt = getResources()
- .getString(R.string.service_arguments_started);
- txt = txt + arguments.getString("name");
- Log.i("ServiceStartArguments", "Message: " + msg + ", " + txt);
+ String txt = arguments.getString("name");
+
+ Log.i("ServiceStartArguments", "Message: " + msg + ", "
+ + arguments.getString("name"));
- showNotification();
+ if ((msg.arg2&Service.START_FLAG_REDELIVERY) == 0) {
+ txt = "New cmd #" + msg.arg1 + ": " + txt;
+ } else {
+ txt = "Re-delivered #" + msg.arg1 + ": " + txt;
+ }
+
+ showNotification(txt);
- // Normally we would do some work here... for our sample, we will
- // just sleep for 10 seconds.
+ // Normally we would do some work here... for our sample, we will
+ // just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
@@ -78,6 +88,8 @@
}
}
+ hideNotification();
+
Log.i("ServiceStartArguments", "Done with #" + msg.arg1);
stopSelf(msg.arg1);
}
@@ -88,14 +100,19 @@
public void onCreate() {
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
+ Toast.makeText(this, R.string.service_created,
+ Toast.LENGTH_SHORT).show();
+
// This is who should be launched if the user selects our persistent
// notification.
mInvokeIntent = new Intent(this, ServiceStartArgumentsController.class);
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
- // main thread, which we don't want to block.
- HandlerThread thread = new HandlerThread("ServiceStartArguments");
+ // main thread, which we don't want to block. We also make it
+ // background priority so CPU-intensive work will not disrupt our UI.
+ HandlerThread thread = new HandlerThread("ServiceStartArguments",
+ Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
mServiceLooper = thread.getLooper();
@@ -103,25 +120,45 @@
}
@Override
- public void onStart(Intent intent, int startId) {
+ public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("ServiceStartArguments",
"Starting #" + startId + ": " + intent.getExtras());
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
+ msg.arg2 = flags;
msg.obj = intent.getExtras();
mServiceHandler.sendMessage(msg);
Log.i("ServiceStartArguments", "Sending: " + msg);
+
+ // For the start fail button, we will simulate the process dying
+ // for some reason in onStartCommand().
+ if (intent.getBooleanExtra("fail", false)) {
+ // Don't do this if we are in a retry... the system will
+ // eventually give up if we keep crashing.
+ if ((flags&START_FLAG_RETRY) == 0) {
+ // Since the process hasn't finished handling the command,
+ // it will be restarted with the command again, regardless of
+ // whether we return START_REDELIVER_INTENT.
+ Process.killProcess(Process.myPid());
+ }
+ }
+
+ // Normally we would consistently return one kind of result...
+ // however, here we will select between these two, so you can see
+ // how they impact the behavior. Try killing the process while it
+ // is in the middle of executing the different commands.
+ return intent.getBooleanExtra("redeliver", false)
+ ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
mServiceLooper.quit();
- // Cancel the persistent notification.
- mNM.cancel(R.string.service_arguments_started);
+ hideNotification();
// Tell the user we stopped.
- Toast.makeText(ServiceStartArguments.this, R.string.service_arguments_stopped,
+ Toast.makeText(ServiceStartArguments.this, R.string.service_destroyed,
Toast.LENGTH_SHORT).show();
}
@@ -133,10 +170,7 @@
/**
* Show a notification while this service is running.
*/
- private void showNotification() {
- // In this sample, we'll use the same text for the ticker and the expanded notification
- CharSequence text = getText(R.string.service_arguments_started);
-
+ private void showNotification(String text) {
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.stat_sample, text,
System.currentTimeMillis());
@@ -149,9 +183,16 @@
notification.setLatestEventInfo(this, getText(R.string.service_start_arguments_label),
text, contentIntent);
+ // We show this for as long as our service is processing a command.
+ notification.flags |= Notification.FLAG_ONGOING_EVENT;
+
// Send the notification.
// We use a string id because it is a unique number. We use it later to cancel.
- mNM.notify(R.string.service_arguments_started, notification);
+ mNM.notify(R.string.service_created, notification);
+ }
+
+ private void hideNotification() {
+ mNM.cancel(R.string.service_created);
}
}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArgumentsController.java b/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArgumentsController.java
index 2c53ff4..9d79e2e 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArgumentsController.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArgumentsController.java
@@ -21,14 +21,13 @@
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
+import android.os.Process;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import com.example.android.apis.R;
-import java.util.HashMap;
-
/**
* Example of explicitly starting the {@link ServiceStartArguments}.
*/
@@ -46,26 +45,51 @@
button.setOnClickListener(mStart2Listener);
button = (Button)findViewById(R.id.start3);
button.setOnClickListener(mStart3Listener);
+ button = (Button)findViewById(R.id.startfail);
+ button.setOnClickListener(mStartFailListener);
+ button = (Button)findViewById(R.id.kill);
+ button.setOnClickListener(mKillListener);
}
private OnClickListener mStart1Listener = new OnClickListener() {
public void onClick(View v) {
startService(new Intent(ServiceStartArgumentsController.this,
- ServiceStartArguments.class).putExtra("name", "One"));
+ ServiceStartArguments.class)
+ .putExtra("name", "One"));
}
};
private OnClickListener mStart2Listener = new OnClickListener() {
public void onClick(View v) {
startService(new Intent(ServiceStartArgumentsController.this,
- ServiceStartArguments.class).putExtra("name", "Two"));
+ ServiceStartArguments.class)
+ .putExtra("name", "Two"));
}
};
private OnClickListener mStart3Listener = new OnClickListener() {
public void onClick(View v) {
startService(new Intent(ServiceStartArgumentsController.this,
- ServiceStartArguments.class).putExtra("name", "Three"));
+ ServiceStartArguments.class)
+ .putExtra("name", "Three")
+ .putExtra("redeliver", true));
+ }
+ };
+
+ private OnClickListener mStartFailListener = new OnClickListener() {
+ public void onClick(View v) {
+ startService(new Intent(ServiceStartArgumentsController.this,
+ ServiceStartArguments.class)
+ .putExtra("name", "Failure")
+ .putExtra("fail", true));
+ }
+ };
+
+ private OnClickListener mKillListener = new OnClickListener() {
+ public void onClick(View v) {
+ // This is to simulate the service being killed while it is
+ // running in the background.
+ Process.killProcess(Process.myPid());
}
};
}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/WallpaperActivity.java b/samples/ApiDemos/src/com/example/android/apis/app/WallpaperActivity.java
new file mode 100644
index 0000000..8d7f5a1
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/WallpaperActivity.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.app;
+
+// Need the following import to get access to the app resources, since this
+// class is in a sub-package.
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+/**
+ * <h3>Wallpaper Activity</h3>
+ *
+ * <p>This demonstrates the how to write an activity that has the system
+ * wallpaper behind it.</p>
+ */
+public class WallpaperActivity extends Activity {
+ /**
+ * Initialization of the Activity after it is first created. Must at least
+ * call {@link android.app.Activity#setContentView setContentView()} to
+ * describe what is to be displayed in the screen.
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // Be sure to call the super class.
+ super.onCreate(savedInstanceState);
+
+ // See assets/res/any/layout/translucent_background.xml for this
+ // view layout definition, which is being set here as
+ // the content of our screen.
+ setContentView(R.layout.translucent_background);
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/ColorFilters.java b/samples/ApiDemos/src/com/example/android/apis/graphics/ColorFilters.java
new file mode 100644
index 0000000..92d18ba
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/ColorFilters.java
@@ -0,0 +1,184 @@
+/*
+ * 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.example.android.apis.graphics;
+
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.*;
+import android.graphics.drawable.*;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.*;
+
+public class ColorFilters extends GraphicsActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(new SampleView(this));
+
+ }
+
+ private static class SampleView extends View {
+ private Activity mActivity;
+ private Drawable mDrawable;
+ private Drawable[] mDrawables;
+ private Paint mPaint;
+ private Paint mPaint2;
+ private float mPaintTextOffset;
+ private int[] mColors;
+ private PorterDuff.Mode[] mModes;
+ private int mModeIndex;
+
+ private static void addToTheRight(Drawable curr, Drawable prev) {
+ Rect r = prev.getBounds();
+ int x = r.right + 12;
+ int center = (r.top + r.bottom) >> 1;
+ int h = curr.getIntrinsicHeight();
+ int y = center - (h >> 1);
+
+ curr.setBounds(x, y, x + curr.getIntrinsicWidth(), y + h);
+ }
+
+ public SampleView(Activity activity) {
+ super(activity);
+ mActivity = activity;
+ Context context = activity;
+ setFocusable(true);
+
+ mDrawable = context.getResources().getDrawable(R.drawable.btn_default_normal);
+ mDrawable.setBounds(0, 0, 150, 48);
+ mDrawable.setDither(true);
+
+ int[] resIDs = new int[] {
+ R.drawable.btn_circle_normal,
+ R.drawable.btn_check_off,
+ R.drawable.btn_check_on
+ };
+ mDrawables = new Drawable[resIDs.length];
+ Drawable prev = mDrawable;
+ for (int i = 0; i < resIDs.length; i++) {
+ mDrawables[i] = context.getResources().getDrawable(resIDs[i]);
+ mDrawables[i].setDither(true);
+ addToTheRight(mDrawables[i], prev);
+ prev = mDrawables[i];
+ }
+
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ mPaint.setTextSize(16);
+ mPaint.setTextAlign(Paint.Align.CENTER);
+
+ mPaint2 = new Paint(mPaint);
+ mPaint2.setAlpha(64);
+
+ Paint.FontMetrics fm = mPaint.getFontMetrics();
+ mPaintTextOffset = (fm.descent + fm.ascent) * 0.5f;
+
+ mColors = new int[] {
+ 0,
+ 0xCC0000FF,
+ 0x880000FF,
+ 0x440000FF,
+ 0xFFCCCCFF,
+ 0xFF8888FF,
+ 0xFF4444FF,
+ };
+
+ mModes = new PorterDuff.Mode[] {
+ PorterDuff.Mode.SRC_ATOP,
+ PorterDuff.Mode.MULTIPLY,
+ };
+ mModeIndex = 0;
+
+ updateTitle();
+ }
+
+ private void swapPaintColors() {
+ if (mPaint.getColor() == 0xFF000000) {
+ mPaint.setColor(0xFFFFFFFF);
+ mPaint2.setColor(0xFF000000);
+ } else {
+ mPaint.setColor(0xFF000000);
+ mPaint2.setColor(0xFFFFFFFF);
+ }
+ mPaint2.setAlpha(64);
+ }
+
+ private void updateTitle() {
+ mActivity.setTitle(mModes[mModeIndex].toString());
+ }
+
+ private void drawSample(Canvas canvas, ColorFilter filter) {
+ Rect r = mDrawable.getBounds();
+ float x = (r.left + r.right) * 0.5f;
+ float y = (r.top + r.bottom) * 0.5f - mPaintTextOffset;
+
+ mDrawable.setColorFilter(filter);
+ mDrawable.draw(canvas);
+ canvas.drawText("Label", x+1, y+1, mPaint2);
+ canvas.drawText("Label", x, y, mPaint);
+
+ for (Drawable dr : mDrawables) {
+ dr.setColorFilter(filter);
+ dr.draw(canvas);
+ }
+ }
+
+ @Override protected void onDraw(Canvas canvas) {
+ canvas.drawColor(0xFFCCCCCC);
+
+ canvas.translate(8, 12);
+ for (int color : mColors) {
+ ColorFilter filter;
+ if (color == 0) {
+ filter = null;
+ } else {
+ filter = new PorterDuffColorFilter(color,
+ mModes[mModeIndex]);
+ }
+ drawSample(canvas, filter);
+ canvas.translate(0, 55);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ float x = event.getX();
+ float y = event.getY();
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ break;
+ case MotionEvent.ACTION_MOVE:
+ break;
+ case MotionEvent.ACTION_UP:
+ // update mode every other time we change paint colors
+ if (mPaint.getColor() == 0xFFFFFFFF) {
+ mModeIndex = (mModeIndex + 1) % mModes.length;
+ updateTitle();
+ }
+ swapPaintColors();
+ invalidate();
+ break;
+ }
+ return true;
+ }
+ }
+}
+
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/CubeRenderer.java b/samples/ApiDemos/src/com/example/android/apis/graphics/CubeRenderer.java
index 527e2bc..0f15f91 100644
--- a/samples/ApiDemos/src/com/example/android/apis/graphics/CubeRenderer.java
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/CubeRenderer.java
@@ -64,29 +64,6 @@
mAngle += 1.2f;
}
- public int[] getConfigSpec() {
- if (mTranslucentBackground) {
- // We want a depth buffer and an alpha buffer
- int[] configSpec = {
- EGL10.EGL_RED_SIZE, 8,
- EGL10.EGL_GREEN_SIZE, 8,
- EGL10.EGL_BLUE_SIZE, 8,
- EGL10.EGL_ALPHA_SIZE, 8,
- EGL10.EGL_DEPTH_SIZE, 16,
- EGL10.EGL_NONE
- };
- return configSpec;
- } else {
- // We want a depth buffer, don't care about the
- // details of the color buffer.
- int[] configSpec = {
- EGL10.EGL_DEPTH_SIZE, 16,
- EGL10.EGL_NONE
- };
- return configSpec;
- }
- }
-
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/TouchRotateActivity.java b/samples/ApiDemos/src/com/example/android/apis/graphics/TouchRotateActivity.java
index 09d3694..c0f32a7 100644
--- a/samples/ApiDemos/src/com/example/android/apis/graphics/TouchRotateActivity.java
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/TouchRotateActivity.java
@@ -134,16 +134,6 @@
mCube.draw(gl);
}
- public int[] getConfigSpec() {
- // We want a depth buffer, don't care about the
- // details of the color buffer.
- int[] configSpec = {
- EGL10.EGL_DEPTH_SIZE, 16,
- EGL10.EGL_NONE
- };
- return configSpec;
- }
-
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/spritetext/SpriteTextRenderer.java b/samples/ApiDemos/src/com/example/android/apis/graphics/spritetext/SpriteTextRenderer.java
index b9369cd..223300a 100644
--- a/samples/ApiDemos/src/com/example/android/apis/graphics/spritetext/SpriteTextRenderer.java
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/spritetext/SpriteTextRenderer.java
@@ -50,16 +50,6 @@
mLabelPaint.setARGB(0xff, 0x00, 0x00, 0x00);
}
- public int[] getConfigSpec() {
- // We don't need a depth buffer, and don't care about our
- // color depth.
- int[] configSpec = {
- EGL10.EGL_DEPTH_SIZE, 0,
- EGL10.EGL_NONE
- };
- return configSpec;
- }
-
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
/*
* By default, OpenGL enables features that improve quality
diff --git a/samples/BrowserPlugin/Android.mk b/samples/BrowserPlugin/Android.mk
new file mode 100644
index 0000000..16047d5
--- /dev/null
+++ b/samples/BrowserPlugin/Android.mk
@@ -0,0 +1,36 @@
+# 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.
+#
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build application
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := SampleBrowserPlugin
+
+LOCAL_JNI_SHARED_LIBRARIES := libsampleplugin
+
+include $(BUILD_PACKAGE)
+
+# ============================================================
+
+# Also build all of the sub-targets under this one: the shared library.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/BrowserPlugin/AndroidManifest.xml b/samples/BrowserPlugin/AndroidManifest.xml
new file mode 100644
index 0000000..ae6b5db
--- /dev/null
+++ b/samples/BrowserPlugin/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.sampleplugin"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-permission android:name="android.webkit.permission.PLUGIN"/>
+
+ <uses-sdk android:minSdkVersion="3" />
+
+ <application android:icon="@drawable/sample_browser_plugin"
+ android:label="@string/sample_browser_plugin">
+ <service android:name="SamplePlugin">
+ <intent-filter>
+ <action android:name="android.webkit.PLUGIN" />
+ </intent-filter>
+ </service>
+ </application>
+
+</manifest>
diff --git a/samples/BrowserPlugin/MODULE_LICENSE_APACHE2 b/samples/BrowserPlugin/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/samples/BrowserPlugin/MODULE_LICENSE_APACHE2
diff --git a/samples/BrowserPlugin/NOTICE b/samples/BrowserPlugin/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/samples/BrowserPlugin/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/samples/BrowserPlugin/README b/samples/BrowserPlugin/README
new file mode 100644
index 0000000..08b04a5
--- /dev/null
+++ b/samples/BrowserPlugin/README
@@ -0,0 +1,173 @@
+# 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.
+#
+
+##############################
+######### CONTENTS ###########
+A. INTRODUCTION
+B. PLUGIN STRUCTURE
+C. HOW TO DEPLOY
+D. SUB-PLUGINS
+ 1. ANIMATION
+ 2. AUDIO
+ 3. BACKGROUND
+ 4. FORM
+ 5. PAINT
+
+
+##############################
+## (A) INTRODUCTION ##########
+
+The sample plugin is intended to give plugin developers a point of reference to
+see how an android browser plugin is created and how to use the available APIs.
+A plugin is packaged like a standard apk and can be installed either via the
+market or adb. The sample plugin attempts to exercise as many of the APIs as
+possible but unfortunately not all are covered.
+
+Trying to have a single plugin demonstrate all possible API interactions on one
+screen was not practical. On the other hand we also didn't want a separate
+plugin for each interction, as that would result in many separate apk's that
+would need to be maintained. To solve this problem we developed the idea to use
+"sub-plugins". With a sub-plugin only one specific feature of the plugin would
+be active at a time, but they would all share as much common code as possible.
+A detailed description of each sub-plugin and its function can be found in the
+sub-plugins section.
+
+##############################
+## (B) PLUGIN STRUCTURE ######
+
+The sample plugin is packaged as one plugin but contains many unique sub-plugins
+(e.g. audio and paint). The package consists of two pieces: (1) Java code
+containing the config; (2) C++ shared library containing the brower/plugin
+bindings and the sub-plugin classes.
+
+~~~~ (1) JAVA ~~~~~
+Android.mk: specifies the name of the APK (SampleBrowserPlugin) as well as which
+ shared libraries to include.
+
+AndroidManifest.xml: similar to a standard android manifest file, except that it
+ must contain the "uses-permission" and "intent-filter"
+ elements that are plugin specific.
+
+src/*: location of the java files which in our case is just an empty service
+
+res/*: location of the static resources (e.g. an icon for the plugin)
+
+~~~~ (2) C++ ~~~~~
+jni/Android.mk: specifies the build parameters for the shared library that is to
+ be included with the apk. The library contains all the bindings
+ between the plugin and the browser.
+
+jni/main.*: this code is the binding point between the plugin and the browser.
+ It supports all of the functions required for a standard netscape
+ style plugin as well as all the android specific APIs. The initial
+ starting point for the plugin is the NP_Initialize function. The
+ NPP_New function is responsible for reading the input args and
+ selecting the appropriate sub-plugin to instantiate. Most other
+ functions either return fixed values or pass their inputs to the
+ sub-plugin for processing.
+
+jni/PluginObject.*: The pluginObject provides a convenient container in which to
+ store variables (the plugin's state). This objects two main
+ responsibilities are (1) to construct and store the NPClass
+ object (done using code provided by Apple) and (2) provide
+ the abstract class for the sub-plugin objects and a place to
+ store the sub-plugin after it is instantiated.
+
+jni/*/*: Each of the sub-plugins has a folder that contains its class definition
+ and logic. The sub-plugin receives events from the browser and it can
+ also communicate with the browser using the netscape plugin functions
+ as well as the specialized android interfaces.
+
+
+##############################
+## (C) HOW TO DEPLOY #########
+
+To compile and install a plugin on a device/emulator simply...
+
+1. run "make SampleBrowserPlugin" (compiles libsampleplugin.so and builds the apk)
+2. the previous command produces an apk file so record its location
+3. run "adb install [apk_file]" to install it on a device/emulator
+4. the browser will auto recognize the plugin is available
+
+Now that the plugin is installed you can manage it just like you would any other
+application via Settings -> Applications -> Manage applications. To execute the
+plugin you need to include an html snippet (similar to the one found below) in
+a document that is accessible by the browser. The mime-type cannot change but
+you can change the width, height, and parameters. The parameters are used to
+notify the plugin which sub-plugin to execute and which drawing model to use.
+
+<object type="application/x-testbrowserplugin" height=50 width=250>
+ <param name="DrawingModel" value="Surface" />
+ <param name="PluginType" value="Background" />
+</object>
+
+
+##############################
+## (D) SUB-PLUGINS ###########
+
+Each sub-plugin corresponds to exactly one plugin type and can support one or
+more drawing models. In the subsections below there are descriptions of each of
+the sub-plugins as well as the information required to create the html snippets.
+
+#######################
+## (D1) ANIMATION #####
+
+PLUGIN TYPE: Animation
+DRAWING MODEL: Bitmap
+
+This plugin draws a ball bouncing around the screen. If the plugin is not entirely
+on the screen and it it touched, then it will attempt to center itself on screen.
+
+#######################
+## (D2) AUDIO #########
+
+PLUGIN TYPE: Audio
+DRAWING MODEL: Bitmap
+
+This plugin plays a raw audio file located at /sdcard/sample.raw (need to supply
+your own). It uses touch to trigger the play, pause, and stop buttons.
+
+#######################
+## (D3) BACKGROUND ####
+
+PLUGIN TYPE: Background
+DRAWING MODEL: Surface
+
+This plugin has minimal visual components but mainly runs API tests in the
+background. The plugin handles scaling its own bitmap on zoom which in this
+case is a simple string of text. The output of this plugin is found in the logs
+as it prints errors if it detects any API failures. Some of the API's tested are
+timers, javascript access, and bitmap formatting.
+
+#######################
+## (D4) FORM ##########
+
+PLUGIN TYPE: Form
+DRAWING MODEL: Bitmap
+
+This plugin mimics a simple username/password form. You can select a textbox by
+either touching it or using the navigation keys. Once selected the box will
+highlight and the keyboard will appear. If the textbox selected is not fully
+in view then the plugin will ensure it is centered on the screen.
+
+#######################
+## (D5) PAINT #########
+
+PLUGIN TYPE: Paint
+DRAWING MODEL: Surface
+
+This plugin provides a surface that the user can "paint" on. The inputs method
+can be toggled between mouse (dots) and touch (lines). This plugin has a fixed
+surface and allows the browser to scale the surface when zooming.
diff --git a/samples/BrowserPlugin/jni/Android.mk b/samples/BrowserPlugin/jni/Android.mk
new file mode 100644
index 0000000..3d1c88f
--- /dev/null
+++ b/samples/BrowserPlugin/jni/Android.mk
@@ -0,0 +1,59 @@
+##
+##
+## Copyright 2008, The Android Open Source Project
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions
+## are met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in the
+## documentation and/or other materials provided with the distribution.
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+##
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ main.cpp \
+ PluginObject.cpp \
+ animation/AnimationPlugin.cpp \
+ audio/AudioPlugin.cpp \
+ background/BackgroundPlugin.cpp \
+ form/FormPlugin.cpp \
+ paint/PaintPlugin.cpp \
+
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH) \
+ $(LOCAL_PATH)/animation \
+ $(LOCAL_PATH)/audio \
+ $(LOCAL_PATH)/background \
+ $(LOCAL_PATH)/form \
+ $(LOCAL_PATH)/paint \
+ external/webkit/WebCore/bridge \
+ external/webkit/WebCore/plugins \
+ external/webkit/WebCore/platform/android/JavaVM \
+ external/webkit/WebKit/android/plugins
+
+LOCAL_SRC_FILES := $(LOCAL_SRC_FILES)
+LOCAL_CFLAGS += -fvisibility=hidden
+LOCAL_PRELINK_MODULE:=false
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+
+LOCAL_MODULE:= libsampleplugin
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/samples/BrowserPlugin/jni/PluginObject.cpp b/samples/BrowserPlugin/jni/PluginObject.cpp
new file mode 100644
index 0000000..80f5e7c
--- /dev/null
+++ b/samples/BrowserPlugin/jni/PluginObject.cpp
@@ -0,0 +1,177 @@
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under AppleÕs copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include "main.h"
+#include "PluginObject.h"
+
+static void pluginInvalidate(NPObject *obj);
+static bool pluginHasProperty(NPObject *obj, NPIdentifier name);
+static bool pluginHasMethod(NPObject *obj, NPIdentifier name);
+static bool pluginGetProperty(NPObject *obj, NPIdentifier name, NPVariant *variant);
+static bool pluginSetProperty(NPObject *obj, NPIdentifier name, const NPVariant *variant);
+static bool pluginInvoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);
+static bool pluginInvokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result);
+static NPObject *pluginAllocate(NPP npp, NPClass *theClass);
+static void pluginDeallocate(NPObject *obj);
+static bool pluginRemoveProperty(NPObject *npobj, NPIdentifier name);
+static bool pluginEnumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count);
+
+
+
+static NPClass pluginClass = {
+ NP_CLASS_STRUCT_VERSION,
+ pluginAllocate,
+ pluginDeallocate,
+ pluginInvalidate,
+ pluginHasMethod,
+ pluginInvoke,
+ pluginInvokeDefault,
+ pluginHasProperty,
+ pluginGetProperty,
+ pluginSetProperty,
+ pluginRemoveProperty,
+ pluginEnumerate
+};
+
+NPClass *getPluginClass(void)
+{
+ return &pluginClass;
+}
+
+static bool identifiersInitialized = false;
+
+#define ID_TESTFILE_PROPERTY 0
+#define NUM_PROPERTY_IDENTIFIERS 1
+
+static NPIdentifier pluginPropertyIdentifiers[NUM_PROPERTY_IDENTIFIERS];
+static const NPUTF8 *pluginPropertyIdentifierNames[NUM_PROPERTY_IDENTIFIERS] = {
+ "testfile"
+};
+
+#define ID_GETTESTFILE_METHOD 0
+#define NUM_METHOD_IDENTIFIERS 1
+
+static NPIdentifier pluginMethodIdentifiers[NUM_METHOD_IDENTIFIERS];
+static const NPUTF8 *pluginMethodIdentifierNames[NUM_METHOD_IDENTIFIERS] = {
+ "getTestFile"
+};
+
+static void initializeIdentifiers(void)
+{
+ browser->getstringidentifiers(pluginPropertyIdentifierNames, NUM_PROPERTY_IDENTIFIERS, pluginPropertyIdentifiers);
+ browser->getstringidentifiers(pluginMethodIdentifierNames, NUM_METHOD_IDENTIFIERS, pluginMethodIdentifiers);
+}
+
+static bool pluginHasProperty(NPObject *obj, NPIdentifier name)
+{
+ int i;
+ for (i = 0; i < NUM_PROPERTY_IDENTIFIERS; i++)
+ if (name == pluginPropertyIdentifiers[i])
+ return true;
+ return false;
+}
+
+static bool pluginHasMethod(NPObject *obj, NPIdentifier name)
+{
+ int i;
+ for (i = 0; i < NUM_METHOD_IDENTIFIERS; i++)
+ if (name == pluginMethodIdentifiers[i])
+ return true;
+ return false;
+}
+
+static bool pluginGetProperty(NPObject *obj, NPIdentifier name, NPVariant *variant)
+{
+ PluginObject *plugin = (PluginObject *)obj;
+ if (name == pluginPropertyIdentifiers[ID_TESTFILE_PROPERTY]) {
+ BOOLEAN_TO_NPVARIANT(true, *variant);
+ return true;
+ }
+ return false;
+}
+
+static bool pluginSetProperty(NPObject *obj, NPIdentifier name, const NPVariant *variant)
+{
+ return false;
+}
+
+static bool pluginInvoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result)
+{
+ PluginObject *plugin = (PluginObject *)obj;
+ if (name == pluginMethodIdentifiers[ID_GETTESTFILE_METHOD]) {
+ return true;
+ }
+ return false;
+}
+
+static bool pluginInvokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result)
+{
+ return false;
+}
+
+static void pluginInvalidate(NPObject *obj)
+{
+ // Release any remaining references to JavaScript objects.
+}
+
+static NPObject *pluginAllocate(NPP npp, NPClass *theClass)
+{
+ PluginObject *newInstance = (PluginObject*) malloc(sizeof(PluginObject));
+ newInstance->header._class = theClass;
+ newInstance->header.referenceCount = 1;
+
+ if (!identifiersInitialized) {
+ identifiersInitialized = true;
+ initializeIdentifiers();
+ }
+
+ newInstance->npp = npp;
+
+ return &newInstance->header;
+}
+
+static void pluginDeallocate(NPObject *obj)
+{
+ free(obj);
+}
+
+static bool pluginRemoveProperty(NPObject *npobj, NPIdentifier name)
+{
+ return false;
+}
+
+static bool pluginEnumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count)
+{
+ return false;
+}
diff --git a/samples/BrowserPlugin/jni/PluginObject.h b/samples/BrowserPlugin/jni/PluginObject.h
new file mode 100644
index 0000000..82f6f48
--- /dev/null
+++ b/samples/BrowserPlugin/jni/PluginObject.h
@@ -0,0 +1,74 @@
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under AppleÕs copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PluginObject__DEFINED
+#define PluginObject__DEFINED
+
+#include "main.h"
+
+class SubPlugin {
+public:
+ SubPlugin(NPP inst) : m_inst(inst) {}
+ virtual ~SubPlugin() {}
+ virtual int16 handleEvent(const ANPEvent* evt) = 0;
+ virtual bool supportsDrawingModel(ANPDrawingModel) = 0;
+
+ NPP inst() const { return m_inst; }
+
+private:
+ NPP m_inst;
+};
+
+enum PluginTypes {
+ kAnimation_PluginType = 1,
+ kAudio_PluginType = 2,
+ kBackground_PluginType = 3,
+ kForm_PluginType = 4,
+ kText_PluginType = 5,
+ kPaint_PluginType = 6,
+};
+typedef uint32_t PluginType;
+
+typedef struct PluginObject {
+ NPObject header;
+ NPP npp;
+ NPWindow* window;
+
+ PluginType pluginType;
+ SubPlugin* activePlugin;
+
+} PluginObject;
+
+NPClass *getPluginClass(void);
+
+#endif // PluginObject__DEFINED
diff --git a/samples/BrowserPlugin/jni/animation/AnimationPlugin.cpp b/samples/BrowserPlugin/jni/animation/AnimationPlugin.cpp
new file mode 100644
index 0000000..b6175c1
--- /dev/null
+++ b/samples/BrowserPlugin/jni/animation/AnimationPlugin.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "AnimationPlugin.h"
+
+#include <math.h>
+#include <string.h>
+
+extern NPNetscapeFuncs* browser;
+extern ANPLogInterfaceV0 gLogI;
+extern ANPCanvasInterfaceV0 gCanvasI;
+extern ANPPaintInterfaceV0 gPaintI;
+extern ANPPathInterfaceV0 gPathI;
+extern ANPWindowInterfaceV0 gWindowI;
+
+static uint16 rnd16(float x, int inset) {
+ int ix = (int)roundf(x) + inset;
+ if (ix < 0) {
+ ix = 0;
+ }
+ return static_cast<uint16>(ix);
+}
+
+static void inval(NPP instance, const ANPRectF& r, bool doAA) {
+ const int inset = doAA ? -1 : 0;
+
+ NPRect inval;
+ inval.left = rnd16(r.left, inset);
+ inval.top = rnd16(r.top, inset);
+ inval.right = rnd16(r.right, -inset);
+ inval.bottom = rnd16(r.bottom, -inset);
+ browser->invalidaterect(instance, &inval);
+}
+
+static void bounce(float* x, float* dx, const float max) {
+ *x += *dx;
+ if (*x < 0) {
+ *x = 0;
+ if (*dx < 0) {
+ *dx = -*dx;
+ }
+ } else if (*x > max) {
+ *x = max;
+ if (*dx > 0) {
+ *dx = -*dx;
+ }
+ }
+}
+///////////////////////////////////////////////////////////////////////////////
+
+BallAnimation::BallAnimation(NPP inst) : SubPlugin(inst) {
+ m_x = m_y = 0;
+ m_dx = 7 * SCALE;
+ m_dy = 5 * SCALE;
+
+ memset(&m_oval, 0, sizeof(m_oval));
+
+ m_paint = gPaintI.newPaint();
+ gPaintI.setFlags(m_paint, gPaintI.getFlags(m_paint) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setColor(m_paint, 0xFFFF0000);
+
+ //register for touch events
+ ANPEventFlags flags = kTouch_ANPEventFlag;
+ NPError err = browser->setvalue(inst, kAcceptEvents_ANPSetValue, &flags);
+ if (err != NPERR_NO_ERROR) {
+ gLogI.log(inst, kError_ANPLogType, "Error selecting input events.");
+ }
+}
+
+BallAnimation::~BallAnimation() {
+ gPaintI.deletePaint(m_paint);
+}
+
+bool BallAnimation::supportsDrawingModel(ANPDrawingModel model) {
+ return (model == kBitmap_ANPDrawingModel);
+}
+
+void BallAnimation::drawPlugin(const ANPBitmap& bitmap, const ANPRectI& clip) {
+
+ // create a canvas
+ ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap);
+
+ // clip the canvas
+ ANPRectF clipR;
+ clipR.left = clip.left;
+ clipR.top = clip.top;
+ clipR.right = clip.right;
+ clipR.bottom = clip.bottom;
+ gCanvasI.clipRect(canvas, &clipR);
+
+ // setup variables
+ PluginObject *obj = (PluginObject*) inst()->pdata;
+ const float OW = 20;
+ const float OH = 20;
+ const int W = obj->window->width;
+ const int H = obj->window->height;
+
+ // paint the canvas (using the path API)
+ gCanvasI.drawColor(canvas, 0xFFFFFFFF);
+ {
+ ANPPath* path = gPathI.newPath();
+
+ float cx = W * 0.5f;
+ float cy = H * 0.5f;
+ gPathI.moveTo(path, 0, 0);
+ gPathI.quadTo(path, cx, cy, W, 0);
+ gPathI.quadTo(path, cx, cy, W, H);
+ gPathI.quadTo(path, cx, cy, 0, H);
+ gPathI.quadTo(path, cx, cy, 0, 0);
+
+ gPaintI.setColor(m_paint, 0xFF0000FF);
+ gCanvasI.drawPath(canvas, path, m_paint);
+
+ ANPRectF bounds;
+ memset(&bounds, 0, sizeof(bounds));
+ gPathI.getBounds(path, &bounds);
+ gPathI.deletePath(path);
+ }
+
+ // draw the oval
+ inval(inst(), m_oval, true); // inval the old
+ m_oval.left = m_x;
+ m_oval.top = m_y;
+ m_oval.right = m_x + OW;
+ m_oval.bottom = m_y + OH;
+ inval(inst(), m_oval, true); // inval the new
+ gPaintI.setColor(m_paint, 0xFFFF0000);
+ gCanvasI.drawOval(canvas, &m_oval, m_paint);
+
+ // update the coordinates of the oval
+ bounce(&m_x, &m_dx, obj->window->width - OW);
+ bounce(&m_y, &m_dy, obj->window->height - OH);
+
+ // delete the canvas
+ gCanvasI.deleteCanvas(canvas);
+}
+
+void BallAnimation::showEntirePluginOnScreen() {
+ NPP instance = this->inst();
+ PluginObject *obj = (PluginObject*) instance->pdata;
+ NPWindow *window = obj->window;
+
+ ANPRectI visibleRects[1];
+
+ visibleRects[0].left = 0;
+ visibleRects[0].top = 0;
+ visibleRects[0].right = window->width;
+ visibleRects[0].bottom = window->height;
+
+ gWindowI.setVisibleRects(instance, visibleRects, 1);
+ gWindowI.clearVisibleRects(instance);
+}
+
+int16 BallAnimation::handleEvent(const ANPEvent* evt) {
+ NPP instance = this->inst();
+
+ switch (evt->eventType) {
+ case kDraw_ANPEventType:
+ switch (evt->data.draw.model) {
+ case kBitmap_ANPDrawingModel:
+ drawPlugin(evt->data.draw.data.bitmap, evt->data.draw.clip);
+ return 1;
+ default:
+ break; // unknown drawing model
+ }
+ case kTouch_ANPEventType:
+ if (kDown_ANPTouchAction == evt->data.touch.action) {
+ showEntirePluginOnScreen();
+ }
+ return 1;
+ default:
+ break;
+ }
+ return 0; // unknown or unhandled event
+}
diff --git a/samples/BrowserPlugin/jni/animation/AnimationPlugin.h b/samples/BrowserPlugin/jni/animation/AnimationPlugin.h
new file mode 100644
index 0000000..ef2a3f54
--- /dev/null
+++ b/samples/BrowserPlugin/jni/animation/AnimationPlugin.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginObject.h"
+
+#ifndef pluginGraphics__DEFINED
+#define pluginGraphics__DEFINED
+
+class BallAnimation : public SubPlugin {
+public:
+ BallAnimation(NPP inst);
+ virtual ~BallAnimation();
+ virtual bool supportsDrawingModel(ANPDrawingModel);
+ virtual int16 handleEvent(const ANPEvent* evt);
+private:
+ void drawPlugin(const ANPBitmap& bitmap, const ANPRectI& clip);
+ void showEntirePluginOnScreen();
+
+ float m_x;
+ float m_y;
+ float m_dx;
+ float m_dy;
+
+ ANPRectF m_oval;
+ ANPPaint* m_paint;
+
+ static const float SCALE = 0.1;
+};
+
+#endif // pluginGraphics__DEFINED
diff --git a/samples/BrowserPlugin/jni/audio/AudioPlugin.cpp b/samples/BrowserPlugin/jni/audio/AudioPlugin.cpp
new file mode 100644
index 0000000..58d340c
--- /dev/null
+++ b/samples/BrowserPlugin/jni/audio/AudioPlugin.cpp
@@ -0,0 +1,382 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "AudioPlugin.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <math.h>
+#include <string.h>
+
+extern NPNetscapeFuncs* browser;
+extern ANPLogInterfaceV0 gLogI;
+extern ANPCanvasInterfaceV0 gCanvasI;
+extern ANPPaintInterfaceV0 gPaintI;
+extern ANPAudioTrackInterfaceV0 gSoundI;
+extern ANPTypefaceInterfaceV0 gTypefaceI;
+
+
+static void inval(NPP instance) {
+ browser->invalidaterect(instance, NULL);
+}
+
+static uint16 rnd16(float x, int inset) {
+ int ix = (int)roundf(x) + inset;
+ if (ix < 0) {
+ ix = 0;
+ }
+ return static_cast<uint16>(ix);
+}
+
+static void inval(NPP instance, const ANPRectF& r, bool doAA) {
+ const int inset = doAA ? -1 : 0;
+
+ PluginObject *obj = reinterpret_cast<PluginObject*>(instance->pdata);
+ NPRect inval;
+ inval.left = rnd16(r.left, inset);
+ inval.top = rnd16(r.top, inset);
+ inval.right = rnd16(r.right, -inset);
+ inval.bottom = rnd16(r.bottom, -inset);
+ browser->invalidaterect(instance, &inval);
+}
+
+static void audioCallback(ANPAudioEvent evt, void* user, ANPAudioBuffer* buffer) {
+ switch (evt) {
+ case kMoreData_ANPAudioEvent: {
+ SoundPlay* play = reinterpret_cast<SoundPlay*>(user);
+ size_t amount = fread(buffer->bufferData, 1, buffer->size, play->file);
+ buffer->size = amount;
+ if (amount == 0) {
+ gSoundI.stop(play->track);
+ fclose(play->file);
+ play->file = NULL;
+ // TODO need to notify our main thread to delete the track now
+ }
+
+ if (play->fileSize > 0) {
+ // TODO we need to properly update the progress value
+ play->progress = 1;
+ inval(play->instance);
+ }
+
+
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AudioPlugin::AudioPlugin(NPP inst) : SubPlugin(inst) {
+
+ const char path[] = "/sdcard/sample.raw";
+
+ // open a file stream
+ FILE* f = fopen(path, "r");
+ gLogI.log(inst, kDebug_ANPLogType, "--- path %s FILE %p", path, f);
+
+ // setup our private audio struct's default values
+ m_soundPlay = new SoundPlay;
+ m_soundPlay->instance = inst;
+ m_soundPlay->progress = 0;
+ m_soundPlay->fileSize = 0;
+ m_soundPlay->file = f;
+ m_soundPlay->track = NULL;
+
+ // create the audio track
+ if (f) {
+ m_soundPlay->track = gSoundI.newTrack(44100, kPCM16Bit_ANPSampleFormat, 2, audioCallback, m_soundPlay);
+ if (!m_soundPlay->track) {
+ fclose(f);
+ m_soundPlay->file = NULL;
+ }
+ }
+
+ // get the audio file's size
+ int fileDescriptor = open(path, O_RDONLY);
+ struct stat fileStatus;
+
+ if(fileDescriptor <= 0) {
+ gLogI.log(inst, kError_ANPLogType, "fopen error");
+ }
+ else if (fstat(fileDescriptor, &fileStatus) != 0) {
+ gLogI.log(inst, kDebug_ANPLogType, "File Size: %d", fileStatus.st_size);
+ m_soundPlay->fileSize = fileStatus.st_size;
+ } else {
+ gLogI.log(inst, kError_ANPLogType, "fstat error");
+ }
+
+ // configure the UI elements
+ m_activeTouch = false;
+
+ memset(&m_trackRect, 0, sizeof(m_trackRect));
+ memset(&m_playRect, 0, sizeof(m_playRect));
+ memset(&m_pauseRect, 0, sizeof(m_pauseRect));
+ memset(&m_stopRect, 0, sizeof(m_stopRect));
+
+ m_paintTrack = gPaintI.newPaint();
+ gPaintI.setFlags(m_paintTrack, gPaintI.getFlags(m_paintTrack) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setColor(m_paintTrack, 0xFFC0C0C0);
+
+ m_paintRect = gPaintI.newPaint();
+ gPaintI.setFlags(m_paintRect, gPaintI.getFlags(m_paintRect) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setColor(m_paintRect, 0xFFA8A8A8);
+
+ m_paintText = gPaintI.newPaint();
+ gPaintI.setFlags(m_paintText, gPaintI.getFlags(m_paintText) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setColor(m_paintText, 0xFF2F4F4F);
+ gPaintI.setTextSize(m_paintText, 18);
+
+ m_paintTrackProgress = gPaintI.newPaint();
+ gPaintI.setFlags(m_paintTrackProgress, gPaintI.getFlags(m_paintTrackProgress) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setColor(m_paintTrackProgress, 0xFF545454);
+
+ m_paintActiveRect = gPaintI.newPaint();
+ gPaintI.setFlags(m_paintActiveRect, gPaintI.getFlags(m_paintActiveRect) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setColor(m_paintActiveRect, 0xFF545454);
+
+ ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle);
+ gPaintI.setTypeface(m_paintText, tf);
+ gTypefaceI.unref(tf);
+
+ //register for touch events
+ ANPEventFlags flags = kTouch_ANPEventFlag;
+ NPError err = browser->setvalue(inst, kAcceptEvents_ANPSetValue, &flags);
+ if (err != NPERR_NO_ERROR) {
+ gLogI.log(inst, kError_ANPLogType, "Error selecting input events.");
+ }
+}
+
+AudioPlugin::~AudioPlugin() {
+ gPaintI.deletePaint(m_paintTrack);
+ gPaintI.deletePaint(m_paintRect);
+ gPaintI.deletePaint(m_paintText);
+ gPaintI.deletePaint(m_paintTrackProgress);
+ gPaintI.deletePaint(m_paintActiveRect);
+ if(m_soundPlay->track)
+ gSoundI.deleteTrack(m_soundPlay->track);
+ delete m_soundPlay;
+}
+
+bool AudioPlugin::supportsDrawingModel(ANPDrawingModel model) {
+ return (model == kBitmap_ANPDrawingModel);
+}
+
+void AudioPlugin::drawPlugin(const ANPBitmap& bitmap, const ANPRectI& clip) {
+ ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap);
+
+ ANPRectF clipR;
+ clipR.left = clip.left;
+ clipR.top = clip.top;
+ clipR.right = clip.right;
+ clipR.bottom = clip.bottom;
+ gCanvasI.clipRect(canvas, &clipR);
+
+ draw(canvas);
+ gCanvasI.deleteCanvas(canvas);
+}
+
+void AudioPlugin::draw(ANPCanvas* canvas) {
+ NPP instance = this->inst();
+ PluginObject *obj = (PluginObject*) instance->pdata;
+
+ gLogI.log(instance, kError_ANPLogType, "Drawing");
+
+ const float trackHeight = 30;
+ const float buttonWidth = 60;
+ const float buttonHeight = 30;
+ const int W = obj->window->width;
+ const int H = obj->window->height;
+
+ // color the plugin canvas
+ gCanvasI.drawColor(canvas, 0xFFCDCDCD);
+
+ // get font metrics
+ ANPFontMetrics fontMetrics;
+ gPaintI.getFontMetrics(m_paintText, &fontMetrics);
+
+ // draw the track box (1 px from the edge)
+ m_trackRect.left = 1;
+ m_trackRect.top = 1;
+ m_trackRect.right = W - 2;
+ m_trackRect.bottom = 1 + trackHeight;
+ gCanvasI.drawRect(canvas, &m_trackRect, m_paintTrack);
+
+ // draw the progress bar
+ if (m_soundPlay->progress > 0) {
+ // TODO need to draw progress bar to cover the proper percentage of the track bar
+ gCanvasI.drawRect(canvas, &m_trackRect, m_paintTrackProgress);
+ }
+
+ // draw the play box (under track box)
+ m_playRect.left = m_trackRect.left + 5;
+ m_playRect.top = m_trackRect.bottom + 10;
+ m_playRect.right = m_playRect.left + buttonWidth;
+ m_playRect.bottom = m_playRect.top + buttonHeight;
+ gCanvasI.drawRect(canvas, &m_playRect, getPaint(&m_playRect));
+ // draw the play box (under track box)
+ const char playText[] = "Play";
+ gCanvasI.drawText(canvas, playText, sizeof(playText)-1, m_playRect.left + 5,
+ m_playRect.top - fontMetrics.fTop, m_paintText);
+
+ // draw the pause box (under track box)
+ m_pauseRect.left = m_playRect.right + 20;
+ m_pauseRect.top = m_trackRect.bottom + 10;
+ m_pauseRect.right = m_pauseRect.left + buttonWidth;
+ m_pauseRect.bottom = m_pauseRect.top + buttonHeight;
+ gCanvasI.drawRect(canvas, &m_pauseRect, getPaint(&m_pauseRect));
+ // draw the text in the pause box
+ const char pauseText[] = "Pause";
+ gCanvasI.drawText(canvas, pauseText, sizeof(pauseText)-1, m_pauseRect.left + 5,
+ m_pauseRect.top - fontMetrics.fTop, m_paintText);
+
+ // draw the stop box (under track box)
+ m_stopRect.left = m_pauseRect.right + 20;
+ m_stopRect.top = m_trackRect.bottom + 10;
+ m_stopRect.right = m_stopRect.left + buttonWidth;
+ m_stopRect.bottom = m_stopRect.top + buttonHeight;
+ gCanvasI.drawRect(canvas, &m_stopRect, getPaint(&m_stopRect));
+ // draw the text in the pause box
+ const char stopText[] = "Stop";
+ gCanvasI.drawText(canvas, stopText, sizeof(stopText)-1, m_stopRect.left + 5,
+ m_stopRect.top - fontMetrics.fTop, m_paintText);
+}
+
+ANPPaint* AudioPlugin::getPaint(ANPRectF* input) {
+ return (input == m_activeRect) ? m_paintActiveRect : m_paintRect;
+}
+
+int16 AudioPlugin::handleEvent(const ANPEvent* evt) {
+ NPP instance = this->inst();
+
+ switch (evt->eventType) {
+ case kDraw_ANPEventType:
+ switch (evt->data.draw.model) {
+ case kBitmap_ANPDrawingModel:
+ drawPlugin(evt->data.draw.data.bitmap, evt->data.draw.clip);
+ return 1;
+ default:
+ break; // unknown drawing model
+ }
+
+ case kTouch_ANPEventType: {
+ int x = evt->data.touch.x;
+ int y = evt->data.touch.y;
+ if (kDown_ANPTouchAction == evt->data.touch.action) {
+
+ m_activeTouchRect = validTouch(x,y);
+ if(m_activeTouchRect) {
+ m_activeTouch = true;
+ return 1;
+ }
+
+ } else if (kUp_ANPTouchAction == evt->data.touch.action && m_activeTouch) {
+ handleTouch(x, y);
+ m_activeTouch = false;
+ return 1;
+ } else if (kCancel_ANPTouchAction == evt->data.touch.action) {
+ m_activeTouch = false;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return 0; // unknown or unhandled event
+}
+
+void AudioPlugin::invalActiveRect() {
+
+}
+
+ANPRectF* AudioPlugin::validTouch(int x, int y) {
+
+ if (m_playRect.left && x < m_playRect.right && y > m_playRect.top && y < m_playRect.bottom)
+ return &m_playRect;
+ else if (m_pauseRect.left && x < m_pauseRect.right && y > m_pauseRect.top && y < m_pauseRect.bottom)
+ return &m_pauseRect;
+ else if (x > m_stopRect.left && x < m_stopRect.right && y > m_stopRect.top && y < m_stopRect.bottom)
+ return &m_stopRect;
+ else
+ return NULL;
+}
+
+void AudioPlugin::handleTouch(int x, int y) {
+ NPP instance = this->inst();
+
+ // if the track is null then return
+ if (NULL == m_soundPlay->track) {
+ gLogI.log(instance, kError_ANPLogType, "---- %p unable to create track",
+ instance);
+ return;
+ }
+
+ // check to make sure the currentRect matches the activeRect
+ ANPRectF* currentRect = validTouch(x,y);
+ if (m_activeTouchRect != currentRect)
+ return;
+
+ if (currentRect == &m_playRect) {
+
+ gLogI.log(instance, kDebug_ANPLogType, "---- %p starting track (%d)",
+ m_soundPlay->track, gSoundI.isStopped(m_soundPlay->track));
+
+ if (gSoundI.isStopped(m_soundPlay->track)) {
+ gSoundI.start(m_soundPlay->track);
+ }
+ }
+ else if (currentRect == &m_pauseRect) {
+
+ gLogI.log(instance, kDebug_ANPLogType, "---- %p pausing track (%d)",
+ m_soundPlay->track, gSoundI.isStopped(m_soundPlay->track));
+
+ if (!gSoundI.isStopped(m_soundPlay->track)) {
+ gSoundI.pause(m_soundPlay->track);
+ }
+ }
+ else if (currentRect == &m_stopRect) {
+
+ gLogI.log(instance, kDebug_ANPLogType, "---- %p stopping track (%d)",
+ m_soundPlay->track, gSoundI.isStopped(m_soundPlay->track));
+
+ if (!gSoundI.isStopped(m_soundPlay->track)) {
+ gSoundI.stop(m_soundPlay->track);
+ }
+ if (m_soundPlay->file) {
+ fseek(m_soundPlay->file, 0, SEEK_SET);
+ }
+ }
+ else {
+ return;
+ }
+
+ // set the currentRect to be the activeRect
+ m_activeRect = currentRect;
+ inval(instance);
+}
diff --git a/samples/BrowserPlugin/jni/audio/AudioPlugin.h b/samples/BrowserPlugin/jni/audio/AudioPlugin.h
new file mode 100644
index 0000000..129d33a
--- /dev/null
+++ b/samples/BrowserPlugin/jni/audio/AudioPlugin.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginObject.h"
+#include <stdio.h>
+
+#ifndef audioPlugin__DEFINED
+#define audioPlugin__DEFINED
+
+struct SoundPlay {
+ NPP instance;
+ ANPAudioTrack* track;
+ FILE* file;
+ int fileSize;
+ int progress; // value between 0 and 100
+};
+
+class AudioPlugin : public SubPlugin {
+public:
+ AudioPlugin(NPP inst);
+ virtual ~AudioPlugin();
+ virtual bool supportsDrawingModel(ANPDrawingModel);
+ virtual int16 handleEvent(const ANPEvent* evt);
+private:
+ void draw(ANPCanvas*);
+ void drawPlugin(const ANPBitmap& bitmap, const ANPRectI& clip);
+
+ void handleTouch(int x, int y);
+ void invalActiveRect();
+ ANPPaint* getPaint(ANPRectF*);
+ ANPRectF* validTouch(int x, int y);
+
+ ANPRectF m_trackRect;
+ ANPRectF m_playRect;
+ ANPRectF m_pauseRect;
+ ANPRectF m_stopRect;
+
+ ANPPaint* m_paintTrack;
+ ANPPaint* m_paintRect;
+ ANPPaint* m_paintText;
+
+ ANPPaint* m_paintTrackProgress;
+ ANPPaint* m_paintActiveRect;
+
+ SoundPlay* m_soundPlay;
+
+ bool m_activeTouch;
+ ANPRectF* m_activeTouchRect;
+ ANPRectF* m_activeRect;
+};
+
+#endif // audioPlugin__DEFINED
diff --git a/samples/BrowserPlugin/jni/background/BackgroundPlugin.cpp b/samples/BrowserPlugin/jni/background/BackgroundPlugin.cpp
new file mode 100644
index 0000000..eac5db2
--- /dev/null
+++ b/samples/BrowserPlugin/jni/background/BackgroundPlugin.cpp
@@ -0,0 +1,418 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "BackgroundPlugin.h"
+#include "android_npapi.h"
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <time.h>
+#include <math.h>
+#include <string.h>
+
+extern NPNetscapeFuncs* browser;
+extern ANPBitmapInterfaceV0 gBitmapI;
+extern ANPCanvasInterfaceV0 gCanvasI;
+extern ANPLogInterfaceV0 gLogI;
+extern ANPPaintInterfaceV0 gPaintI;
+extern ANPSurfaceInterfaceV0 gSurfaceI;
+extern ANPTypefaceInterfaceV0 gTypefaceI;
+
+#define ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0]))
+
+static uint32_t getMSecs() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (uint32_t) (tv.tv_sec * 1000 + tv.tv_usec / 1000 ); // microseconds to milliseconds
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+BackgroundPlugin::BackgroundPlugin(NPP inst) : SubPlugin(inst) {
+
+ // initialize the drawing surface
+ m_surfaceReady = false;
+ m_surface = gSurfaceI.newRasterSurface(inst, kRGB_565_ANPBitmapFormat, false);
+ if(!m_surface)
+ gLogI.log(inst, kError_ANPLogType, "----%p Unable to create RGBA surface", inst);
+
+ //initialize bitmap transparency variables
+ mFinishedStageOne = false;
+ mFinishedStageTwo = false;
+ mFinishedStageThree = false;
+
+ // test basic plugin functionality
+ test_logging(); // android logging
+ test_timers(); // plugin timers
+ test_bitmaps(); // android bitmaps
+ test_domAccess();
+ test_javascript();
+}
+
+BackgroundPlugin::~BackgroundPlugin() {
+ gSurfaceI.deleteSurface(m_surface);
+}
+
+bool BackgroundPlugin::supportsDrawingModel(ANPDrawingModel model) {
+ return (model == kSurface_ANPDrawingModel);
+}
+
+void BackgroundPlugin::drawPlugin(int surfaceWidth, int surfaceHeight) {
+
+ // get the plugin's dimensions according to the DOM
+ PluginObject *obj = (PluginObject*) inst()->pdata;
+ const int W = obj->window->width;
+ const int H = obj->window->height;
+
+ // compute the current zoom level
+ const float zoomFactorW = static_cast<float>(surfaceWidth) / W;
+ const float zoomFactorH = static_cast<float>(surfaceHeight) / H;
+
+ // check to make sure the zoom level is uniform
+ if (zoomFactorW + .01 < zoomFactorH && zoomFactorW - .01 > zoomFactorH)
+ gLogI.log(inst(), kError_ANPLogType, " ------ %p zoom is out of sync (%f,%f)",
+ inst(), zoomFactorW, zoomFactorH);
+
+ // scale the variables based on the zoom level
+ const int fontSize = (int)(zoomFactorW * 16);
+ const int leftMargin = (int)(zoomFactorW * 10);
+
+ // lock the surface
+ ANPBitmap bitmap;
+ if (!m_surfaceReady || !gSurfaceI.lock(m_surface, &bitmap, NULL)) {
+ gLogI.log(inst(), kError_ANPLogType, " ------ %p unable to lock the plugin", inst());
+ return;
+ }
+
+ // create a canvas
+ ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap);
+ gCanvasI.drawColor(canvas, 0xFFFFFFFF);
+
+ ANPPaint* paint = gPaintI.newPaint();
+ gPaintI.setFlags(paint, gPaintI.getFlags(paint) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setColor(paint, 0xFFFF0000);
+ gPaintI.setTextSize(paint, fontSize);
+
+ ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle);
+ gPaintI.setTypeface(paint, tf);
+ gTypefaceI.unref(tf);
+
+ ANPFontMetrics fm;
+ gPaintI.getFontMetrics(paint, &fm);
+
+ gPaintI.setColor(paint, 0xFF0000FF);
+ const char c[] = "This is a background plugin.";
+ gCanvasI.drawText(canvas, c, sizeof(c)-1, leftMargin, -fm.fTop, paint);
+
+ // clean up variables and unlock the surface
+ gPaintI.deletePaint(paint);
+ gCanvasI.deleteCanvas(canvas);
+ gSurfaceI.unlock(m_surface);
+}
+
+int16 BackgroundPlugin::handleEvent(const ANPEvent* evt) {
+ switch (evt->eventType) {
+ case kDraw_ANPEventType:
+ gLogI.log(inst(), kError_ANPLogType, " ------ %p the plugin did not request draw events", inst());
+ break;
+ case kSurface_ANPEventType:
+ switch (evt->data.surface.action) {
+ case kCreated_ANPSurfaceAction:
+ m_surfaceReady = true;
+ return 1;
+ case kDestroyed_ANPSurfaceAction:
+ m_surfaceReady = false;
+ return 1;
+ case kChanged_ANPSurfaceAction:
+ drawPlugin(evt->data.surface.data.changed.width,
+ evt->data.surface.data.changed.height);
+ return 1;
+ }
+ break;
+ case kLifecycle_ANPEventType:
+ if (evt->data.lifecycle.action == kOnLoad_ANPLifecycleAction) {
+ gLogI.log(inst(), kDebug_ANPLogType, " ------ %p the plugin received an onLoad event", inst());
+ return 1;
+ }
+ break;
+ case kTouch_ANPEventType:
+ gLogI.log(inst(), kError_ANPLogType, " ------ %p the plugin did not request touch events", inst());
+ break;
+ case kKey_ANPEventType:
+ gLogI.log(inst(), kError_ANPLogType, " ------ %p the plugin did not request key events", inst());
+ break;
+ default:
+ break;
+ }
+ return 0; // unknown or unhandled event
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// LOGGING TESTS
+///////////////////////////////////////////////////////////////////////////////
+
+
+void BackgroundPlugin::test_logging() {
+ NPP instance = this->inst();
+
+ //LOG_ERROR(instance, " ------ %p Testing Log Error", instance);
+ gLogI.log(instance, kError_ANPLogType, " ------ %p Testing Log Error", instance);
+ gLogI.log(instance, kWarning_ANPLogType, " ------ %p Testing Log Warning", instance);
+ gLogI.log(instance, kDebug_ANPLogType, " ------ %p Testing Log Debug", instance);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// TIMER TESTS
+///////////////////////////////////////////////////////////////////////////////
+
+#define TIMER_INTERVAL 50
+static void timer_oneshot(NPP instance, uint32 timerID);
+static void timer_repeat(NPP instance, uint32 timerID);
+static void timer_neverfires(NPP instance, uint32 timerID);
+static void timer_latency(NPP instance, uint32 timerID);
+
+void BackgroundPlugin::test_timers() {
+ NPP instance = this->inst();
+
+ //Setup the testing counters
+ mTimerRepeatCount = 5;
+ mTimerLatencyCount = 5;
+
+ // test for bogus timerID
+ browser->unscheduletimer(instance, 999999);
+ // test one-shot
+ browser->scheduletimer(instance, 100, false, timer_oneshot);
+ // test repeat
+ browser->scheduletimer(instance, 50, true, timer_repeat);
+ // test timer latency
+ browser->scheduletimer(instance, TIMER_INTERVAL, true, timer_latency);
+ mStartTime = mPrevTime = getMSecs();
+ // test unschedule immediately
+ uint32 id = browser->scheduletimer(instance, 100, false, timer_neverfires);
+ browser->unscheduletimer(instance, id);
+ // test double unschedule (should be no-op)
+ browser->unscheduletimer(instance, id);
+
+}
+
+static void timer_oneshot(NPP instance, uint32 timerID) {
+ gLogI.log(instance, kDebug_ANPLogType, "-------- oneshot timer\n");
+}
+
+static void timer_repeat(NPP instance, uint32 timerID) {
+ BackgroundPlugin *obj = ((BackgroundPlugin*) ((PluginObject*) instance->pdata)->activePlugin);
+
+ gLogI.log(instance, kDebug_ANPLogType, "-------- repeat timer %d\n",
+ obj->mTimerRepeatCount);
+ if (--obj->mTimerRepeatCount == 0) {
+ browser->unscheduletimer(instance, timerID);
+ }
+}
+
+static void timer_neverfires(NPP instance, uint32 timerID) {
+ gLogI.log(instance, kError_ANPLogType, "-------- timer_neverfires!!!\n");
+}
+
+static void timer_latency(NPP instance, uint32 timerID) {
+ BackgroundPlugin *obj = ((BackgroundPlugin*) ((PluginObject*) instance->pdata)->activePlugin);
+
+ obj->mTimerLatencyCurrentCount += 1;
+
+ uint32_t now = getMSecs();
+ uint32_t interval = now - obj->mPrevTime;
+ uint32_t dur = now - obj->mStartTime;
+ uint32_t expectedDur = obj->mTimerLatencyCurrentCount * TIMER_INTERVAL;
+ int32_t drift = dur - expectedDur;
+ int32_t avgDrift = drift / obj->mTimerLatencyCurrentCount;
+
+ obj->mPrevTime = now;
+
+ gLogI.log(instance, kDebug_ANPLogType,
+ "-------- latency test: [%3d] interval %d expected %d, total %d expected %d, drift %d avg %d\n",
+ obj->mTimerLatencyCurrentCount, interval, TIMER_INTERVAL, dur,
+ expectedDur, drift, avgDrift);
+
+ if (--obj->mTimerLatencyCount == 0) {
+ browser->unscheduletimer(instance, timerID);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// BITMAP TESTS
+///////////////////////////////////////////////////////////////////////////////
+
+static void test_formats(NPP instance);
+
+void BackgroundPlugin::test_bitmaps() {
+ test_formats(this->inst());
+}
+
+static void test_formats(NPP instance) {
+
+ // TODO pull names from enum in npapi instead of hardcoding them
+ static const struct {
+ ANPBitmapFormat fFormat;
+ const char* fName;
+ } gRecs[] = {
+ { kUnknown_ANPBitmapFormat, "unknown" },
+ { kRGBA_8888_ANPBitmapFormat, "8888" },
+ { kRGB_565_ANPBitmapFormat, "565" },
+ };
+
+ ANPPixelPacking packing;
+ for (size_t i = 0; i < ARRAY_COUNT(gRecs); i++) {
+ if (gBitmapI.getPixelPacking(gRecs[i].fFormat, &packing)) {
+ gLogI.log(instance, kDebug_ANPLogType,
+ "pixel format [%d] %s has packing ARGB [%d %d] [%d %d] [%d %d] [%d %d]\n",
+ gRecs[i].fFormat, gRecs[i].fName,
+ packing.AShift, packing.ABits,
+ packing.RShift, packing.RBits,
+ packing.GShift, packing.GBits,
+ packing.BShift, packing.BBits);
+ } else {
+ gLogI.log(instance, kDebug_ANPLogType,
+ "pixel format [%d] %s has no packing\n",
+ gRecs[i].fFormat, gRecs[i].fName);
+ }
+ }
+}
+
+void BackgroundPlugin::test_bitmap_transparency(const ANPEvent* evt) {
+ NPP instance = this->inst();
+
+ // check default & set transparent
+ if (!mFinishedStageOne) {
+
+ gLogI.log(instance, kDebug_ANPLogType, "BEGIN: testing bitmap transparency");
+
+ //check to make sure it is not transparent
+ if (evt->data.draw.data.bitmap.format == kRGBA_8888_ANPBitmapFormat) {
+ gLogI.log(instance, kError_ANPLogType, "bitmap default format is transparent");
+ }
+
+ //make it transparent (any non-null value will set it to true)
+ bool value = true;
+ NPError err = browser->setvalue(instance, NPPVpluginTransparentBool, &value);
+ if (err != NPERR_NO_ERROR) {
+ gLogI.log(instance, kError_ANPLogType, "Error setting transparency.");
+ }
+
+ mFinishedStageOne = true;
+ browser->invalidaterect(instance, NULL);
+ }
+ // check transparent & set opaque
+ else if (!mFinishedStageTwo) {
+
+ //check to make sure it is transparent
+ if (evt->data.draw.data.bitmap.format != kRGBA_8888_ANPBitmapFormat) {
+ gLogI.log(instance, kError_ANPLogType, "bitmap did not change to transparent format");
+ }
+
+ //make it opaque
+ NPError err = browser->setvalue(instance, NPPVpluginTransparentBool, NULL);
+ if (err != NPERR_NO_ERROR) {
+ gLogI.log(instance, kError_ANPLogType, "Error setting transparency.");
+ }
+
+ mFinishedStageTwo = true;
+ }
+ // check opaque
+ else if (!mFinishedStageThree) {
+
+ //check to make sure it is not transparent
+ if (evt->data.draw.data.bitmap.format == kRGBA_8888_ANPBitmapFormat) {
+ gLogI.log(instance, kError_ANPLogType, "bitmap default format is transparent");
+ }
+
+ gLogI.log(instance, kDebug_ANPLogType, "END: testing bitmap transparency");
+
+ mFinishedStageThree = true;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// DOM TESTS
+///////////////////////////////////////////////////////////////////////////////
+
+void BackgroundPlugin::test_domAccess() {
+ NPP instance = this->inst();
+
+ gLogI.log(instance, kDebug_ANPLogType, " ------ %p Testing DOM Access", instance);
+
+ // Get the plugin's DOM object
+ NPObject* windowObject = NULL;
+ browser->getvalue(instance, NPNVWindowNPObject, &windowObject);
+
+ if (!windowObject)
+ gLogI.log(instance, kError_ANPLogType, " ------ %p Unable to retrieve DOM Window", instance);
+
+ // Retrieve a property from the plugin's DOM object
+ NPIdentifier topIdentifier = browser->getstringidentifier("top");
+ NPVariant topObjectVariant;
+ browser->getproperty(instance, windowObject, topIdentifier, &topObjectVariant);
+
+ if (topObjectVariant.type != NPVariantType_Object)
+ gLogI.log(instance, kError_ANPLogType, " ------ %p Invalid Variant type for DOM Property: %d,%d", instance, topObjectVariant.type, NPVariantType_Object);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// JAVASCRIPT TESTS
+///////////////////////////////////////////////////////////////////////////////
+
+
+void BackgroundPlugin::test_javascript() {
+ NPP instance = this->inst();
+
+ gLogI.log(instance, kDebug_ANPLogType, " ------ %p Testing JavaScript Access", instance);
+
+ // Get the plugin's DOM object
+ NPObject* windowObject = NULL;
+ browser->getvalue(instance, NPNVWindowNPObject, &windowObject);
+
+ if (!windowObject)
+ gLogI.log(instance, kError_ANPLogType, " ------ %p Unable to retrieve DOM Window", instance);
+
+ // create a string (JS code) that is stored in memory allocated by the browser
+ const char* jsString = "1200 + 34";
+ void* stringMem = browser->memalloc(strlen(jsString));
+ memcpy(stringMem, jsString, strlen(jsString));
+
+ // execute the javascript in the plugin's DOM object
+ NPString script = { (char*)stringMem, strlen(jsString) };
+ NPVariant scriptVariant;
+ if (!browser->evaluate(instance, windowObject, &script, &scriptVariant))
+ gLogI.log(instance, kError_ANPLogType, " ------ %p Unable to eval the JS.", instance);
+
+ if (scriptVariant.type == NPVariantType_Int32) {
+ if (scriptVariant.value.intValue != 1234)
+ gLogI.log(instance, kError_ANPLogType, " ------ %p Invalid Value for JS Return: %d,1234", instance, scriptVariant.value.intValue);
+ } else {
+ gLogI.log(instance, kError_ANPLogType, " ------ %p Invalid Variant type for JS Return: %d,%d", instance, scriptVariant.type, NPVariantType_Int32);
+ }
+
+ // free the memory allocated within the browser
+ browser->memfree(stringMem);
+}
diff --git a/samples/BrowserPlugin/jni/background/BackgroundPlugin.h b/samples/BrowserPlugin/jni/background/BackgroundPlugin.h
new file mode 100644
index 0000000..ed428b5
--- /dev/null
+++ b/samples/BrowserPlugin/jni/background/BackgroundPlugin.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginObject.h"
+
+#ifndef backgroundPlugin__DEFINED
+#define backgroundPlugin__DEFINED
+
+class BackgroundPlugin : public SubPlugin {
+public:
+ BackgroundPlugin(NPP inst);
+ virtual ~BackgroundPlugin();
+ virtual bool supportsDrawingModel(ANPDrawingModel);
+ virtual int16 handleEvent(const ANPEvent* evt);
+
+ // Timer Testing Variables
+ uint32_t mStartTime;
+ uint32_t mPrevTime;
+ int mTimerRepeatCount;
+ int mTimerLatencyCount;
+ int mTimerLatencyCurrentCount;
+
+ // Bitmap Transparency Variables
+ bool mFinishedStageOne; // check default & set transparent
+ bool mFinishedStageTwo; // check transparent & set opaque
+ bool mFinishedStageThree; // check opaque
+
+private:
+ void drawPlugin(int surfaceWidth, int surfaceHeight);
+
+ bool m_surfaceReady;
+ ANPSurface* m_surface;
+
+ void test_logging();
+ void test_timers();
+ void test_bitmaps();
+ void test_bitmap_transparency(const ANPEvent* evt);
+ void test_domAccess();
+ void test_javascript();
+
+};
+
+#endif // backgroundPlugin__DEFINED
diff --git a/samples/BrowserPlugin/jni/form/FormPlugin.cpp b/samples/BrowserPlugin/jni/form/FormPlugin.cpp
new file mode 100644
index 0000000..a92d447
--- /dev/null
+++ b/samples/BrowserPlugin/jni/form/FormPlugin.cpp
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "FormPlugin.h"
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <time.h>
+#include <math.h>
+#include <string.h>
+
+extern NPNetscapeFuncs* browser;
+extern ANPLogInterfaceV0 gLogI;
+extern ANPCanvasInterfaceV0 gCanvasI;
+extern ANPPaintInterfaceV0 gPaintI;
+extern ANPTypefaceInterfaceV0 gTypefaceI;
+extern ANPWindowInterfaceV0 gWindowI;
+
+
+static void inval(NPP instance) {
+ browser->invalidaterect(instance, NULL);
+}
+
+static uint16 rnd16(float x, int inset) {
+ int ix = (int)roundf(x) + inset;
+ if (ix < 0) {
+ ix = 0;
+ }
+ return static_cast<uint16>(ix);
+}
+
+static void inval(NPP instance, const ANPRectF& r, bool doAA) {
+ const int inset = doAA ? -1 : 0;
+
+ PluginObject *obj = reinterpret_cast<PluginObject*>(instance->pdata);
+ NPRect inval;
+ inval.left = rnd16(r.left, inset);
+ inval.top = rnd16(r.top, inset);
+ inval.right = rnd16(r.right, -inset);
+ inval.bottom = rnd16(r.bottom, -inset);
+ browser->invalidaterect(instance, &inval);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+FormPlugin::FormPlugin(NPP inst) : SubPlugin(inst) {
+
+ m_hasFocus = false;
+ m_activeInput = NULL;
+
+ memset(&m_usernameInput, 0, sizeof(m_usernameInput));
+ memset(&m_passwordInput, 0, sizeof(m_passwordInput));
+
+ m_usernameInput.text[0] = '\0';
+ m_usernameInput.charPtr = 0;
+
+ m_passwordInput.text[0] = '\0';
+ m_passwordInput.charPtr = 0;
+
+ m_paintInput = gPaintI.newPaint();
+ gPaintI.setFlags(m_paintInput, gPaintI.getFlags(m_paintInput) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setColor(m_paintInput, 0xFFFFFFFF);
+
+ m_paintActive = gPaintI.newPaint();
+ gPaintI.setFlags(m_paintActive, gPaintI.getFlags(m_paintActive) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setColor(m_paintActive, 0xFFFFFF00);
+
+ m_paintText = gPaintI.newPaint();
+ gPaintI.setFlags(m_paintText, gPaintI.getFlags(m_paintText) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setColor(m_paintText, 0xFF000000);
+ gPaintI.setTextSize(m_paintText, 18);
+
+ ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle);
+ gPaintI.setTypeface(m_paintText, tf);
+ gTypefaceI.unref(tf);
+
+ //register for key and visibleRect events
+ ANPEventFlags flags = kKey_ANPEventFlag;
+ NPError err = browser->setvalue(inst, kAcceptEvents_ANPSetValue, &flags);
+ if (err != NPERR_NO_ERROR) {
+ gLogI.log(inst, kError_ANPLogType, "Error selecting input events.");
+ }
+}
+
+FormPlugin::~FormPlugin() {
+ gPaintI.deletePaint(m_paintInput);
+ gPaintI.deletePaint(m_paintActive);
+ gPaintI.deletePaint(m_paintText);
+}
+
+bool FormPlugin::supportsDrawingModel(ANPDrawingModel model) {
+ return (model == kBitmap_ANPDrawingModel);
+}
+
+void FormPlugin::drawPlugin(const ANPBitmap& bitmap, const ANPRectI& clip) {
+ ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap);
+
+ ANPRectF clipR;
+ clipR.left = clip.left;
+ clipR.top = clip.top;
+ clipR.right = clip.right;
+ clipR.bottom = clip.bottom;
+ gCanvasI.clipRect(canvas, &clipR);
+
+ draw(canvas);
+ gCanvasI.deleteCanvas(canvas);
+}
+
+void FormPlugin::draw(ANPCanvas* canvas) {
+ NPP instance = this->inst();
+ PluginObject *obj = (PluginObject*) instance->pdata;
+
+ const float inputWidth = 60;
+ const float inputHeight = 30;
+ const int W = obj->window->width;
+ const int H = obj->window->height;
+
+ // color the plugin canvas
+ gCanvasI.drawColor(canvas, (m_hasFocus) ? 0xFFCDCDCD : 0xFF545454);
+
+ // draw the username box (5 px from the top edge)
+ m_usernameInput.rect.left = 5;
+ m_usernameInput.rect.top = 5;
+ m_usernameInput.rect.right = W - 5;
+ m_usernameInput.rect.bottom = m_usernameInput.rect.top + inputHeight;
+ gCanvasI.drawRect(canvas, &m_usernameInput.rect, getPaint(&m_usernameInput));
+ drawText(canvas, m_usernameInput);
+
+ // draw the password box (5 px from the bottom edge)
+ m_passwordInput.rect.left = 5;
+ m_passwordInput.rect.top = H - (inputHeight + 5);
+ m_passwordInput.rect.right = W - 5;
+ m_passwordInput.rect.bottom = m_passwordInput.rect.top + inputHeight;
+ gCanvasI.drawRect(canvas, &m_passwordInput.rect, getPaint(&m_passwordInput));
+ drawPassword(canvas, m_passwordInput);
+
+ //invalidate the canvas
+ //inval(instance);
+}
+
+ANPPaint* FormPlugin::getPaint(TextInput* input) {
+ return (input == m_activeInput) ? m_paintActive : m_paintInput;
+}
+
+void FormPlugin::drawText(ANPCanvas* canvas, TextInput textInput) {
+
+ // get font metrics
+ ANPFontMetrics fontMetrics;
+ gPaintI.getFontMetrics(m_paintText, &fontMetrics);
+
+ gCanvasI.drawText(canvas, textInput.text, textInput.charPtr,
+ textInput.rect.left + 5,
+ textInput.rect.bottom - fontMetrics.fBottom, m_paintText);
+}
+
+void FormPlugin::drawPassword(ANPCanvas* canvas, TextInput passwordInput) {
+
+ // get font metrics
+ ANPFontMetrics fontMetrics;
+ gPaintI.getFontMetrics(m_paintText, &fontMetrics);
+
+ // comput the circle dimensions and initial location
+ float initialX = passwordInput.rect.left + 5;
+ float ovalBottom = passwordInput.rect.bottom - 2;
+ float ovalTop = ovalBottom - (fontMetrics.fBottom - fontMetrics.fTop);
+ float ovalWidth = ovalBottom - ovalTop;
+ float ovalSpacing = 3;
+
+ // draw circles instead of the actual text
+ for (uint32_t x = 0; x < passwordInput.charPtr; x++) {
+ ANPRectF oval;
+ oval.left = initialX + ((ovalWidth + ovalSpacing) * (float) x);
+ oval.right = oval.left + ovalWidth;
+ oval.top = ovalTop;
+ oval.bottom = ovalBottom;
+ gCanvasI.drawOval(canvas, &oval, m_paintText);
+ }
+}
+
+int16 FormPlugin::handleEvent(const ANPEvent* evt) {
+ NPP instance = this->inst();
+
+ switch (evt->eventType) {
+ case kDraw_ANPEventType:
+ switch (evt->data.draw.model) {
+ case kBitmap_ANPDrawingModel:
+ drawPlugin(evt->data.draw.data.bitmap, evt->data.draw.clip);
+ return 1;
+ default:
+ break; // unknown drawing model
+ }
+ break;
+
+ case kLifecycle_ANPEventType:
+ if (evt->data.lifecycle.action == kLoseFocus_ANPLifecycleAction) {
+ gLogI.log(instance, kDebug_ANPLogType, "----%p Loosing Focus", instance);
+
+ if (m_activeInput) {
+ // hide the keyboard
+ gWindowI.showKeyboard(instance, false);
+
+ //reset the activeInput
+ m_activeInput = NULL;
+ }
+
+ m_hasFocus = false;
+ inval(instance);
+ return 1;
+ }
+ else if (evt->data.lifecycle.action == kGainFocus_ANPLifecycleAction) {
+ gLogI.log(instance, kDebug_ANPLogType, "----%p Gaining Focus", instance);
+ m_hasFocus = true;
+ inval(instance);
+ return 1;
+ }
+ break;
+
+ case kMouse_ANPEventType: {
+
+ int x = evt->data.mouse.x;
+ int y = evt->data.mouse.y;
+ if (kDown_ANPMouseAction == evt->data.mouse.action) {
+
+ TextInput* currentInput = validTap(x,y);
+
+ if (currentInput)
+ gWindowI.showKeyboard(instance, true);
+ else if (m_activeInput)
+ gWindowI.showKeyboard(instance, false);
+
+ if (currentInput != m_activeInput)
+ switchActiveInput(currentInput);
+
+ return 1;
+ }
+ break;
+ }
+
+ case kKey_ANPEventType:
+ if (evt->data.key.action == kDown_ANPKeyAction) {
+
+ //handle navigation keys
+ if (evt->data.key.nativeCode >= kDpadUp_ANPKeyCode
+ && evt->data.key.nativeCode <= kDpadCenter_ANPKeyCode) {
+ return handleNavigation(evt->data.key.nativeCode) ? 1 : 0;
+ }
+
+ if (m_activeInput) {
+ handleTextInput(m_activeInput, evt->data.key.nativeCode,
+ evt->data.key.unichar);
+ inval(instance, m_activeInput->rect, true);
+ }
+ }
+ return 1;
+
+ default:
+ break;
+ }
+ return 0; // unknown or unhandled event
+}
+
+void FormPlugin::switchActiveInput(TextInput* newInput) {
+ NPP instance = this->inst();
+
+ if (m_activeInput) {
+ inval(instance, m_activeInput->rect, true); // inval the old
+ gWindowI.clearVisibleRects(instance);
+ }
+
+ m_activeInput = newInput; // set the new active input
+
+ if (m_activeInput) {
+ inval(instance, m_activeInput->rect, true); // inval the new
+ scrollIntoView(m_activeInput);
+ }
+}
+
+bool FormPlugin::handleNavigation(ANPKeyCode keyCode) {
+ NPP instance = this->inst();
+
+ gLogI.log(instance, kDebug_ANPLogType, "----%p Recvd Nav Key %d", instance, keyCode);
+
+ if (!m_activeInput) {
+ gWindowI.showKeyboard(instance, true);
+ switchActiveInput(&m_usernameInput);
+ }
+ else if (m_activeInput == &m_usernameInput) {
+ if (keyCode == kDpadDown_ANPKeyCode) {
+ switchActiveInput(&m_passwordInput);
+ }
+ else if (keyCode == kDpadCenter_ANPKeyCode)
+ gWindowI.showKeyboard(instance, false);
+ else if (keyCode == kDpadUp_ANPKeyCode)
+ return false;
+ }
+ else if (m_activeInput == &m_passwordInput) {
+ if (keyCode == kDpadUp_ANPKeyCode) {
+ switchActiveInput(&m_usernameInput);
+ }
+ else if (keyCode == kDpadCenter_ANPKeyCode)
+ gWindowI.showKeyboard(instance, false);
+ else if (keyCode == kDpadDown_ANPKeyCode)
+ return false;
+ }
+
+ return true;
+}
+
+void FormPlugin::handleTextInput(TextInput* input, ANPKeyCode keyCode, int32_t unichar) {
+ NPP instance = this->inst();
+
+ //make sure the input field is in view
+ scrollIntoView(input);
+
+ //handle the delete operation
+ if (keyCode == kDel_ANPKeyCode) {
+ if (input->charPtr > 0) {
+ input->charPtr--;
+ }
+ return;
+ }
+
+ //check to see that the input is not full
+ if (input->charPtr >= (sizeof(input->text) - 1))
+ return;
+
+ //add the character
+ input->text[input->charPtr] = static_cast<char>(unichar);
+ input->charPtr++;
+
+ gLogI.log(instance, kDebug_ANPLogType, "----%p Text: %c", instance, unichar);
+}
+
+void FormPlugin::scrollIntoView(TextInput* input) {
+ NPP instance = this->inst();
+ PluginObject *obj = (PluginObject*) instance->pdata;
+ NPWindow *window = obj->window;
+
+ // find the textInput's global rect coordinates
+ ANPRectI visibleRects[1];
+ visibleRects[0].left = input->rect.left;
+ visibleRects[0].top = input->rect.top;
+ visibleRects[0].right = input->rect.right;
+ visibleRects[0].bottom = input->rect.bottom;
+
+ gWindowI.setVisibleRects(instance, visibleRects, 1);
+}
+
+TextInput* FormPlugin::validTap(int x, int y) {
+
+ if (x > m_usernameInput.rect.left && x < m_usernameInput.rect.right &&
+ y > m_usernameInput.rect.top && y < m_usernameInput.rect.bottom)
+ return &m_usernameInput;
+ else if (x >m_passwordInput.rect.left && x < m_passwordInput.rect.right &&
+ y > m_passwordInput.rect.top && y < m_passwordInput.rect.bottom)
+ return &m_passwordInput;
+ else
+ return NULL;
+}
diff --git a/samples/BrowserPlugin/jni/form/FormPlugin.h b/samples/BrowserPlugin/jni/form/FormPlugin.h
new file mode 100644
index 0000000..041ffb8
--- /dev/null
+++ b/samples/BrowserPlugin/jni/form/FormPlugin.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginObject.h"
+
+#ifndef formPlugin__DEFINED
+#define formPlugin__DEFINED
+
+struct TextInput {
+ ANPRectF rect;
+ char text[30];
+ uint32_t charPtr;
+};
+
+class FormPlugin : public SubPlugin {
+public:
+ FormPlugin(NPP inst);
+ virtual ~FormPlugin();
+ virtual bool supportsDrawingModel(ANPDrawingModel);
+ virtual int16 handleEvent(const ANPEvent* evt);
+private:
+ void draw(ANPCanvas*);
+ void drawPlugin(const ANPBitmap& bitmap, const ANPRectI& clip);
+
+ bool m_hasFocus;
+
+ TextInput* m_activeInput;
+ TextInput m_usernameInput;
+ TextInput m_passwordInput;
+
+ ANPPaint* m_paintInput;
+ ANPPaint* m_paintActive;
+ ANPPaint* m_paintText;
+
+ ANPRectI m_visibleRect;
+
+ void drawText(ANPCanvas*, TextInput);
+ void drawPassword(ANPCanvas*, TextInput);
+
+ bool handleNavigation(ANPKeyCode keyCode);
+ void handleTextInput(TextInput* input, ANPKeyCode keyCode, int32_t unichar);
+ void scrollIntoView(TextInput* input);
+ void switchActiveInput(TextInput* input);
+
+ ANPPaint* getPaint(TextInput*);
+ TextInput* validTap(int x, int y);
+
+};
+
+#endif // formPlugin__DEFINED
diff --git a/samples/BrowserPlugin/jni/main.cpp b/samples/BrowserPlugin/jni/main.cpp
new file mode 100644
index 0000000..b5aea95
--- /dev/null
+++ b/samples/BrowserPlugin/jni/main.cpp
@@ -0,0 +1,404 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "main.h"
+#include "PluginObject.h"
+#include "AnimationPlugin.h"
+#include "AudioPlugin.h"
+#include "BackgroundPlugin.h"
+#include "FormPlugin.h"
+#include "PaintPlugin.h"
+#include "android_npapi.h"
+
+NPNetscapeFuncs* browser;
+#define EXPORT __attribute__((visibility("default")))
+
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc,
+ char* argn[], char* argv[], NPSavedData* saved);
+NPError NPP_Destroy(NPP instance, NPSavedData** save);
+NPError NPP_SetWindow(NPP instance, NPWindow* window);
+NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype);
+NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason);
+int32 NPP_WriteReady(NPP instance, NPStream* stream);
+int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len,
+ void* buffer);
+void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname);
+void NPP_Print(NPP instance, NPPrint* platformPrint);
+int16 NPP_HandleEvent(NPP instance, void* event);
+void NPP_URLNotify(NPP instance, const char* URL, NPReason reason,
+ void* notifyData);
+NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value);
+NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value);
+
+extern "C" {
+EXPORT NPError NP_Initialize(NPNetscapeFuncs* browserFuncs, NPPluginFuncs* pluginFuncs, void *java_env, void *application_context);
+EXPORT NPError NP_GetValue(NPP instance, NPPVariable variable, void *value);
+EXPORT const char* NP_GetMIMEDescription(void);
+EXPORT void NP_Shutdown(void);
+};
+
+ANPAudioTrackInterfaceV0 gSoundI;
+ANPBitmapInterfaceV0 gBitmapI;
+ANPCanvasInterfaceV0 gCanvasI;
+ANPLogInterfaceV0 gLogI;
+ANPPaintInterfaceV0 gPaintI;
+ANPPathInterfaceV0 gPathI;
+ANPSurfaceInterfaceV0 gSurfaceI;
+ANPSystemInterfaceV0 gSystemI;
+ANPTypefaceInterfaceV0 gTypefaceI;
+ANPWindowInterfaceV0 gWindowI;
+
+#define ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0]))
+#define DEBUG_PLUGIN_EVENTS 0
+
+NPError NP_Initialize(NPNetscapeFuncs* browserFuncs, NPPluginFuncs* pluginFuncs, void *java_env, void *application_context)
+{
+ // Make sure we have a function table equal or larger than we are built against.
+ if (browserFuncs->size < sizeof(NPNetscapeFuncs)) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ // Copy the function table (structure)
+ browser = (NPNetscapeFuncs*) malloc(sizeof(NPNetscapeFuncs));
+ memcpy(browser, browserFuncs, sizeof(NPNetscapeFuncs));
+
+ // Build the plugin function table
+ pluginFuncs->version = 11;
+ pluginFuncs->size = sizeof(pluginFuncs);
+ pluginFuncs->newp = NPP_New;
+ pluginFuncs->destroy = NPP_Destroy;
+ pluginFuncs->setwindow = NPP_SetWindow;
+ pluginFuncs->newstream = NPP_NewStream;
+ pluginFuncs->destroystream = NPP_DestroyStream;
+ pluginFuncs->asfile = NPP_StreamAsFile;
+ pluginFuncs->writeready = NPP_WriteReady;
+ pluginFuncs->write = (NPP_WriteProcPtr)NPP_Write;
+ pluginFuncs->print = NPP_Print;
+ pluginFuncs->event = NPP_HandleEvent;
+ pluginFuncs->urlnotify = NPP_URLNotify;
+ pluginFuncs->getvalue = NPP_GetValue;
+ pluginFuncs->setvalue = NPP_SetValue;
+
+ static const struct {
+ NPNVariable v;
+ uint32_t size;
+ ANPInterface* i;
+ } gPairs[] = {
+ { kAudioTrackInterfaceV0_ANPGetValue, sizeof(gSoundI), &gSoundI },
+ { kBitmapInterfaceV0_ANPGetValue, sizeof(gBitmapI), &gBitmapI },
+ { kCanvasInterfaceV0_ANPGetValue, sizeof(gCanvasI), &gCanvasI },
+ { kLogInterfaceV0_ANPGetValue, sizeof(gLogI), &gLogI },
+ { kPaintInterfaceV0_ANPGetValue, sizeof(gPaintI), &gPaintI },
+ { kPathInterfaceV0_ANPGetValue, sizeof(gPathI), &gPathI },
+ { kSurfaceInterfaceV0_ANPGetValue, sizeof(gSurfaceI), &gSurfaceI },
+ { kSystemInterfaceV0_ANPGetValue, sizeof(gSystemI), &gSystemI },
+ { kTypefaceInterfaceV0_ANPGetValue, sizeof(gTypefaceI), &gTypefaceI },
+ { kWindowInterfaceV0_ANPGetValue, sizeof(gWindowI), &gWindowI },
+ };
+ for (size_t i = 0; i < ARRAY_COUNT(gPairs); i++) {
+ gPairs[i].i->inSize = gPairs[i].size;
+ NPError err = browser->getvalue(NULL, gPairs[i].v, gPairs[i].i);
+ if (err) {
+ return err;
+ }
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+void NP_Shutdown(void)
+{
+
+}
+
+const char *NP_GetMIMEDescription(void)
+{
+ return "application/x-testbrowserplugin:tst:Test plugin mimetype is application/x-testbrowserplugin";
+}
+
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc,
+ char* argn[], char* argv[], NPSavedData* saved)
+{
+
+ /* BEGIN: STANDARD PLUGIN FRAMEWORK */
+ PluginObject *obj = NULL;
+
+ // Scripting functions appeared in NPAPI version 14
+ if (browser->version >= 14) {
+ instance->pdata = browser->createobject (instance, getPluginClass());
+ obj = static_cast<PluginObject*>(instance->pdata);
+ bzero(obj, sizeof(*obj));
+ }
+ /* END: STANDARD PLUGIN FRAMEWORK */
+
+ // select the drawing model based on user input
+ ANPDrawingModel model = kBitmap_ANPDrawingModel;
+
+ for (int i = 0; i < argc; i++) {
+ if (!strcmp(argn[i], "DrawingModel")) {
+ if (!strcmp(argv[i], "Bitmap")) {
+ model = kBitmap_ANPDrawingModel;
+ }
+ else if (!strcmp(argv[i], "Surface")) {
+ model = kSurface_ANPDrawingModel;
+ }
+ gLogI.log(instance, kDebug_ANPLogType, "------ %p DrawingModel is %d", instance, model);
+ break;
+ }
+ }
+
+ // notify the plugin API of the drawing model we wish to use. This must be
+ // done prior to creating certain subPlugin objects (e.g. surfaceViews)
+ NPError err = browser->setvalue(instance, kRequestDrawingModel_ANPSetValue,
+ reinterpret_cast<void*>(model));
+ if (err) {
+ gLogI.log(instance, kError_ANPLogType, "request model %d err %d", model, err);
+ return err;
+ }
+
+ const char* path = gSystemI.getApplicationDataDirectory();
+ if (path) {
+ gLogI.log(instance, kDebug_ANPLogType, "Application data dir is %s", path);
+ } else {
+ gLogI.log(instance, kError_ANPLogType, "Can't find Application data dir");
+ }
+
+ // select the pluginType
+ for (int i = 0; i < argc; i++) {
+ if (!strcmp(argn[i], "PluginType")) {
+ if (!strcmp(argv[i], "Animation")) {
+ obj->pluginType = kAnimation_PluginType;
+ obj->activePlugin = new BallAnimation(instance);
+ }
+ else if (!strcmp(argv[i], "Audio")) {
+ obj->pluginType = kAudio_PluginType;
+ obj->activePlugin = new AudioPlugin(instance);
+ }
+ else if (!strcmp(argv[i], "Background")) {
+ obj->pluginType = kBackground_PluginType;
+ obj->activePlugin = new BackgroundPlugin(instance);
+ }
+ else if (!strcmp(argv[i], "Form")) {
+ obj->pluginType = kForm_PluginType;
+ obj->activePlugin = new FormPlugin(instance);
+ }
+ else if (!strcmp(argv[i], "Paint")) {
+ obj->pluginType = kPaint_PluginType;
+ obj->activePlugin = new PaintPlugin(instance);
+ }
+ gLogI.log(instance, kDebug_ANPLogType, "------ %p PluginType is %d", instance, obj->pluginType);
+ break;
+ }
+ }
+
+ // if no pluginType is specified then default to Animation
+ if (!obj->pluginType) {
+ gLogI.log(instance, kError_ANPLogType, "------ %p No PluginType attribute was found", instance);
+ obj->pluginType = kAnimation_PluginType;
+ obj->activePlugin = new BallAnimation(instance);
+ }
+
+ // check to ensure the pluginType supports the model
+ if (!obj->activePlugin->supportsDrawingModel(model)) {
+ gLogI.log(instance, kError_ANPLogType, "------ %p Unsupported DrawingModel (%d)", instance, model);
+ return NPERR_GENERIC_ERROR;
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_Destroy(NPP instance, NPSavedData** save)
+{
+ PluginObject *obj = (PluginObject*) instance->pdata;
+ delete obj->activePlugin;
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_SetWindow(NPP instance, NPWindow* window)
+{
+ PluginObject *obj = (PluginObject*) instance->pdata;
+
+ // Do nothing if browser didn't support NPN_CreateObject which would have created the PluginObject.
+ if (obj != NULL) {
+ obj->window = window;
+ }
+
+ browser->invalidaterect(instance, NULL);
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype)
+{
+ *stype = NP_ASFILEONLY;
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason)
+{
+ return NPERR_NO_ERROR;
+}
+
+int32 NPP_WriteReady(NPP instance, NPStream* stream)
+{
+ return 0;
+}
+
+int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer)
+{
+ return 0;
+}
+
+void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname)
+{
+}
+
+void NPP_Print(NPP instance, NPPrint* platformPrint)
+{
+}
+
+int16 NPP_HandleEvent(NPP instance, void* event)
+{
+ PluginObject *obj = reinterpret_cast<PluginObject*>(instance->pdata);
+ const ANPEvent* evt = reinterpret_cast<const ANPEvent*>(event);
+
+#if DEBUG_PLUGIN_EVENTS
+ switch (evt->eventType) {
+ case kDraw_ANPEventType:
+
+ if (evt->data.draw.model == kBitmap_ANPDrawingModel) {
+
+ static ANPBitmapFormat currentFormat = -1;
+ if (evt->data.draw.data.bitmap.format != currentFormat) {
+ currentFormat = evt->data.draw.data.bitmap.format;
+ gLogI.log(instance, kDebug_ANPLogType, "---- %p Draw (bitmap)"
+ " clip=%d,%d,%d,%d format=%d", instance,
+ evt->data.draw.clip.left,
+ evt->data.draw.clip.top,
+ evt->data.draw.clip.right,
+ evt->data.draw.clip.bottom,
+ evt->data.draw.data.bitmap.format);
+ }
+ }
+ break;
+
+ case kKey_ANPEventType:
+ gLogI.log(instance, kDebug_ANPLogType, "---- %p Key action=%d"
+ " code=%d vcode=%d unichar=%d repeat=%d mods=%x", instance,
+ evt->data.key.action,
+ evt->data.key.nativeCode,
+ evt->data.key.virtualCode,
+ evt->data.key.unichar,
+ evt->data.key.repeatCount,
+ evt->data.key.modifiers);
+ break;
+
+ case kLifecycle_ANPEventType:
+ gLogI.log(instance, kDebug_ANPLogType, "---- %p Lifecycle action=%d",
+ instance, evt->data.lifecycle.action);
+ break;
+
+ case kTouch_ANPEventType:
+ gLogI.log(instance, kDebug_ANPLogType, "---- %p Touch action=%d [%d %d]",
+ instance, evt->data.touch.action, evt->data.touch.x,
+ evt->data.touch.y);
+ break;
+
+ case kMouse_ANPEventType:
+ gLogI.log(instance, kDebug_ANPLogType, "---- %p Mouse action=%d [%d %d]",
+ instance, evt->data.mouse.action, evt->data.mouse.x,
+ evt->data.mouse.y);
+ break;
+
+ case kVisibleRect_ANPEventType:
+ gLogI.log(instance, kDebug_ANPLogType, "---- %p VisibleRect [%d %d %d %d]",
+ instance, evt->data.visibleRect.rect.left, evt->data.visibleRect.rect.top,
+ evt->data.visibleRect.rect.right, evt->data.visibleRect.rect.bottom);
+ break;
+
+ default:
+ gLogI.log(instance, kError_ANPLogType, "---- %p Unknown Event [%d]",
+ instance, evt->eventType);
+ break;
+ }
+#endif
+
+ if(!obj->activePlugin) {
+ gLogI.log(instance, kError_ANPLogType, "the active plugin is null.");
+ return 0; // unknown or unhandled event
+ }
+ else {
+ return obj->activePlugin->handleEvent(evt);
+ }
+}
+
+void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
+{
+
+}
+
+EXPORT NPError NP_GetValue(NPP instance, NPPVariable variable, void *value) {
+
+ if (variable == NPPVpluginNameString) {
+ const char **str = (const char **)value;
+ *str = "Test Plugin";
+ return NPERR_NO_ERROR;
+ }
+
+ if (variable == NPPVpluginDescriptionString) {
+ const char **str = (const char **)value;
+ *str = "Description of Test Plugin";
+ return NPERR_NO_ERROR;
+ }
+
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value)
+{
+ if (variable == NPPVpluginScriptableNPObject) {
+ void **v = (void **)value;
+ PluginObject *obj = (PluginObject*) instance->pdata;
+
+ if (obj)
+ browser->retainobject((NPObject*)obj);
+
+ *v = obj;
+ return NPERR_NO_ERROR;
+ }
+
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value)
+{
+ return NPERR_GENERIC_ERROR;
+}
+
diff --git a/samples/BrowserPlugin/jni/main.h b/samples/BrowserPlugin/jni/main.h
new file mode 100644
index 0000000..4532911
--- /dev/null
+++ b/samples/BrowserPlugin/jni/main.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <npapi.h>
+#include <npfunctions.h>
+#include <npruntime.h>
+#include "android_npapi.h"
+
+extern NPNetscapeFuncs* browser;
diff --git a/samples/BrowserPlugin/jni/paint/PaintPlugin.cpp b/samples/BrowserPlugin/jni/paint/PaintPlugin.cpp
new file mode 100644
index 0000000..a34870f
--- /dev/null
+++ b/samples/BrowserPlugin/jni/paint/PaintPlugin.cpp
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PaintPlugin.h"
+
+#include <fcntl.h>
+#include <math.h>
+#include <string.h>
+
+extern NPNetscapeFuncs* browser;
+extern ANPLogInterfaceV0 gLogI;
+extern ANPCanvasInterfaceV0 gCanvasI;
+extern ANPPaintInterfaceV0 gPaintI;
+extern ANPPathInterfaceV0 gPathI;
+extern ANPSurfaceInterfaceV0 gSurfaceI;
+extern ANPTypefaceInterfaceV0 gTypefaceI;
+
+///////////////////////////////////////////////////////////////////////////////
+
+PaintPlugin::PaintPlugin(NPP inst) : SubPlugin(inst) {
+
+ m_isTouchActive = false;
+ m_isTouchCurrentInput = true;
+ m_activePaintColor = s_redColor;
+
+ memset(&m_drawingSurface, 0, sizeof(m_drawingSurface));
+ memset(&m_inputToggle, 0, sizeof(m_inputToggle));
+ memset(&m_colorToggle, 0, sizeof(m_colorToggle));
+ memset(&m_clearSurface, 0, sizeof(m_clearSurface));
+
+ // initialize the drawing surface
+ m_surfaceReady = false;
+ m_surface = gSurfaceI.newRasterSurface(inst, kRGBA_8888_ANPBitmapFormat, true);
+ if(!m_surface)
+ gLogI.log(inst, kError_ANPLogType, "----%p Unable to create RGBA surface", inst);
+
+ // initialize the path
+ m_touchPath = gPathI.newPath();
+ if(!m_touchPath)
+ gLogI.log(inst, kError_ANPLogType, "----%p Unable to create the touch path", inst);
+
+ // initialize the paint colors
+ m_paintSurface = gPaintI.newPaint();
+ gPaintI.setFlags(m_paintSurface, gPaintI.getFlags(m_paintSurface) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setColor(m_paintSurface, 0xFFC0C0C0);
+ gPaintI.setTextSize(m_paintSurface, 18);
+
+ m_paintButton = gPaintI.newPaint();
+ gPaintI.setFlags(m_paintButton, gPaintI.getFlags(m_paintButton) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setColor(m_paintButton, 0xFFA8A8A8);
+
+ // initialize the typeface (set the colors)
+ ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle);
+ gPaintI.setTypeface(m_paintSurface, tf);
+ gTypefaceI.unref(tf);
+
+ //register for touch events
+ ANPEventFlags flags = kTouch_ANPEventFlag;
+ NPError err = browser->setvalue(inst, kAcceptEvents_ANPSetValue, &flags);
+ if (err != NPERR_NO_ERROR) {
+ gLogI.log(inst, kError_ANPLogType, "Error selecting input events.");
+ }
+}
+
+PaintPlugin::~PaintPlugin() {
+ gSurfaceI.deleteSurface(m_surface);
+ gPathI.deletePath(m_touchPath);
+ gPaintI.deletePaint(m_paintSurface);
+ gPaintI.deletePaint(m_paintButton);
+}
+
+bool PaintPlugin::supportsDrawingModel(ANPDrawingModel model) {
+ return (model == kSurface_ANPDrawingModel);
+}
+
+ANPCanvas* PaintPlugin::getCanvas(ANPRectI* dirtyRect) {
+
+ ANPBitmap bitmap;
+ if (!m_surfaceReady || !gSurfaceI.lock(m_surface, &bitmap, dirtyRect))
+ return NULL;
+
+ ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap);
+
+ // clip the canvas to the dirty rect b/c the surface is only required to
+ // copy a minimum of the dirty rect and may copy more. The clipped canvas
+ // however will never write to pixels outside of the clipped area.
+ if (dirtyRect) {
+ ANPRectF clipR;
+ clipR.left = dirtyRect->left;
+ clipR.top = dirtyRect->top;
+ clipR.right = dirtyRect->right;
+ clipR.bottom = dirtyRect->bottom;
+ gCanvasI.clipRect(canvas, &clipR);
+ }
+
+ return canvas;
+}
+
+ANPCanvas* PaintPlugin::getCanvas(ANPRectF* dirtyRect) {
+
+ ANPRectI newRect;
+ newRect.left = (int) dirtyRect->left;
+ newRect.top = (int) dirtyRect->top;
+ newRect.right = (int) dirtyRect->right;
+ newRect.bottom = (int) dirtyRect->bottom;
+
+ return getCanvas(&newRect);
+}
+
+void PaintPlugin::releaseCanvas(ANPCanvas* canvas) {
+ gSurfaceI.unlock(m_surface);
+ gCanvasI.deleteCanvas(canvas);
+}
+
+void PaintPlugin::drawCleanPlugin(ANPCanvas* canvas) {
+ NPP instance = this->inst();
+ PluginObject *obj = (PluginObject*) instance->pdata;
+
+ // if no canvas get a locked canvas
+ if (!canvas)
+ canvas = getCanvas();
+
+ if (!canvas)
+ return;
+
+ const float buttonWidth = 60;
+ const float buttonHeight = 30;
+ const int W = obj->window->width;
+ const int H = obj->window->height;
+
+ // color the plugin canvas
+ gCanvasI.drawColor(canvas, 0xFFCDCDCD);
+
+ // get font metrics
+ ANPFontMetrics fontMetrics;
+ gPaintI.getFontMetrics(m_paintSurface, &fontMetrics);
+
+ // draw the input toggle button
+ m_inputToggle.left = 5;
+ m_inputToggle.top = H - buttonHeight - 5;
+ m_inputToggle.right = m_inputToggle.left + buttonWidth;
+ m_inputToggle.bottom = m_inputToggle.top + buttonHeight;
+ gCanvasI.drawRect(canvas, &m_inputToggle, m_paintButton);
+ const char* inputText = m_isTouchCurrentInput ? "Touch" : "Mouse";
+ gCanvasI.drawText(canvas, inputText, strlen(inputText), m_inputToggle.left + 5,
+ m_inputToggle.top - fontMetrics.fTop, m_paintSurface);
+
+ // draw the color selector button
+ m_colorToggle.left = (W/2) - (buttonWidth/2);
+ m_colorToggle.top = H - buttonHeight - 5;
+ m_colorToggle.right = m_colorToggle.left + buttonWidth;
+ m_colorToggle.bottom = m_colorToggle.top + buttonHeight;
+ gCanvasI.drawRect(canvas, &m_colorToggle, m_paintButton);
+ const char* colorText = getColorText();
+ gCanvasI.drawText(canvas, colorText, strlen(colorText), m_colorToggle.left + 5,
+ m_colorToggle.top - fontMetrics.fTop, m_paintSurface);
+
+ // draw the clear canvas button
+ m_clearSurface.left = W - buttonWidth - 5;
+ m_clearSurface.top = H - buttonHeight - 5;
+ m_clearSurface.right = m_clearSurface.left + buttonWidth;
+ m_clearSurface.bottom = m_clearSurface.top + buttonHeight;
+ gCanvasI.drawRect(canvas, &m_clearSurface, m_paintButton);
+ const char* clearText = "Clear";
+ gCanvasI.drawText(canvas, clearText, strlen(clearText), m_clearSurface.left + 5,
+ m_clearSurface.top - fontMetrics.fTop, m_paintSurface);
+
+ // draw the drawing surface box (5 px from the edge)
+ m_drawingSurface.left = 5;
+ m_drawingSurface.top = 5;
+ m_drawingSurface.right = W - 5;
+ m_drawingSurface.bottom = m_colorToggle.top - 5;
+ gCanvasI.drawRect(canvas, &m_drawingSurface, m_paintSurface);
+
+ // release the canvas
+ releaseCanvas(canvas);
+}
+
+const char* PaintPlugin::getColorText() {
+
+ if (m_activePaintColor == s_blueColor)
+ return "Blue";
+ else if (m_activePaintColor == s_greenColor)
+ return "Green";
+ else
+ return "Red";
+}
+
+int16 PaintPlugin::handleEvent(const ANPEvent* evt) {
+ switch (evt->eventType) {
+ case kSurface_ANPEventType:
+ switch (evt->data.surface.action) {
+ case kCreated_ANPSurfaceAction:
+ m_surfaceReady = true;
+ drawCleanPlugin();
+ return 1;
+ case kDestroyed_ANPSurfaceAction:
+ m_surfaceReady = false;
+ return 1;
+ case kChanged_ANPSurfaceAction:
+ // get the plugin's dimensions according to the DOM
+ PluginObject *obj = (PluginObject*) inst()->pdata;
+ const int pW = obj->window->width;
+ const int pH = obj->window->height;
+ // get the plugin's surface dimensions
+ const int sW = evt->data.surface.data.changed.width;
+ const int sH = evt->data.surface.data.changed.height;
+ if (pW != sW || pH != sH)
+ gLogI.log(inst(), kError_ANPLogType,
+ "----%p Invalid Surface Dimensions (%d,%d):(%d,%d)",
+ inst(), pW, pH, sW, sH);
+ return 1;
+ }
+ break;
+
+ case kTouch_ANPEventType: {
+ float x = (float) evt->data.touch.x;
+ float y = (float) evt->data.touch.y;
+ if (kDown_ANPTouchAction == evt->data.touch.action && m_isTouchCurrentInput) {
+
+ ANPRectF* rect = validTouch(evt->data.touch.x, evt->data.touch.y);
+ if(rect == &m_drawingSurface) {
+ m_isTouchActive = true;
+ gPathI.moveTo(m_touchPath, x, y);
+ paintTouch();
+ return 1;
+ }
+
+ } else if (kMove_ANPTouchAction == evt->data.touch.action && m_isTouchActive) {
+ gPathI.lineTo(m_touchPath, x, y);
+ paintTouch();
+ return 1;
+ } else if (kUp_ANPTouchAction == evt->data.touch.action && m_isTouchActive) {
+ gPathI.lineTo(m_touchPath, x, y);
+ paintTouch();
+ m_isTouchActive = false;
+ gPathI.reset(m_touchPath);
+ return 1;
+ } else if (kCancel_ANPTouchAction == evt->data.touch.action) {
+ m_isTouchActive = false;
+ gPathI.reset(m_touchPath);
+ return 1;
+ }
+
+ break;
+ }
+ case kMouse_ANPEventType: {
+
+ if (m_isTouchActive)
+ gLogI.log(inst(), kError_ANPLogType, "----%p Received unintended mouse event", inst());
+
+ if (kDown_ANPMouseAction == evt->data.mouse.action) {
+ ANPRectF* rect = validTouch(evt->data.mouse.x, evt->data.mouse.y);
+ if (rect == &m_drawingSurface)
+ paintMouse(evt->data.mouse.x, evt->data.mouse.y);
+ else if (rect == &m_inputToggle)
+ toggleInputMethod();
+ else if (rect == &m_colorToggle)
+ togglePaintColor();
+ else if (rect == &m_clearSurface)
+ drawCleanPlugin();
+ }
+ return 1;
+ }
+ default:
+ break;
+ }
+ return 0; // unknown or unhandled event
+}
+
+ANPRectF* PaintPlugin::validTouch(int x, int y) {
+
+ //convert to float
+ float fx = (int) x;
+ float fy = (int) y;
+
+ if (fx > m_drawingSurface.left && fx < m_drawingSurface.right && fy > m_drawingSurface.top && fy < m_drawingSurface.bottom)
+ return &m_drawingSurface;
+ else if (fx > m_inputToggle.left && fx < m_inputToggle.right && fy > m_inputToggle.top && fy < m_inputToggle.bottom)
+ return &m_inputToggle;
+ else if (fx > m_colorToggle.left && fx < m_colorToggle.right && fy > m_colorToggle.top && fy < m_colorToggle.bottom)
+ return &m_colorToggle;
+ else if (fx > m_clearSurface.left && fx < m_clearSurface.right && fy > m_clearSurface.top && fy < m_clearSurface.bottom)
+ return &m_clearSurface;
+ else
+ return NULL;
+}
+
+void PaintPlugin::toggleInputMethod() {
+ m_isTouchCurrentInput = !m_isTouchCurrentInput;
+
+ // lock only the input toggle and redraw the canvas
+ ANPCanvas* lockedCanvas = getCanvas(&m_inputToggle);
+ drawCleanPlugin(lockedCanvas);
+}
+
+void PaintPlugin::togglePaintColor() {
+ if (m_activePaintColor == s_blueColor)
+ m_activePaintColor = s_redColor;
+ else if (m_activePaintColor == s_greenColor)
+ m_activePaintColor = s_blueColor;
+ else
+ m_activePaintColor = s_greenColor;
+
+ // lock only the color toggle and redraw the canvas
+ ANPCanvas* lockedCanvas = getCanvas(&m_colorToggle);
+ drawCleanPlugin(lockedCanvas);
+}
+
+void PaintPlugin::paintMouse(int x, int y) {
+ //TODO do not paint outside the drawing surface
+
+ //create the paint color
+ ANPPaint* fillPaint = gPaintI.newPaint();
+ gPaintI.setFlags(fillPaint, gPaintI.getFlags(fillPaint) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setStyle(fillPaint, kFill_ANPPaintStyle);
+ gPaintI.setColor(fillPaint, m_activePaintColor);
+
+ // handle the simple "mouse" paint (draw a point)
+ ANPRectF point;
+ point.left = (float) x-3;
+ point.top = (float) y-3;
+ point.right = (float) x+3;
+ point.bottom = (float) y+3;
+
+ // get a canvas that is only locked around the point and draw it
+ ANPCanvas* canvas = getCanvas(&point);
+ gCanvasI.drawOval(canvas, &point, fillPaint);
+
+ // clean up
+ releaseCanvas(canvas);
+ gPaintI.deletePaint(fillPaint);
+}
+
+void PaintPlugin::paintTouch() {
+ //TODO do not paint outside the drawing surface
+
+ //create the paint color
+ ANPPaint* strokePaint = gPaintI.newPaint();
+ gPaintI.setFlags(strokePaint, gPaintI.getFlags(strokePaint) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setColor(strokePaint, m_activePaintColor);
+ gPaintI.setStyle(strokePaint, kStroke_ANPPaintStyle);
+ gPaintI.setStrokeWidth(strokePaint, 6.0);
+ gPaintI.setStrokeCap(strokePaint, kRound_ANPPaintCap);
+ gPaintI.setStrokeJoin(strokePaint, kRound_ANPPaintJoin);
+
+ // handle the complex "touch" paint (draw a line)
+ ANPRectF bounds;
+ gPathI.getBounds(m_touchPath, &bounds);
+
+ // get a canvas that is only locked around the point and draw the path
+ ANPCanvas* canvas = getCanvas(&bounds);
+ gCanvasI.drawPath(canvas, m_touchPath, strokePaint);
+
+ // clean up
+ releaseCanvas(canvas);
+ gPaintI.deletePaint(strokePaint);
+}
diff --git a/samples/BrowserPlugin/jni/paint/PaintPlugin.h b/samples/BrowserPlugin/jni/paint/PaintPlugin.h
new file mode 100644
index 0000000..7e6f235
--- /dev/null
+++ b/samples/BrowserPlugin/jni/paint/PaintPlugin.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginObject.h"
+#include <stdio.h>
+
+#ifndef paintPlugin__DEFINED
+#define paintPlugin__DEFINED
+
+class PaintPlugin : public SubPlugin {
+public:
+ PaintPlugin(NPP inst);
+ virtual ~PaintPlugin();
+ virtual bool supportsDrawingModel(ANPDrawingModel);
+ virtual int16 handleEvent(const ANPEvent* evt);
+private:
+ void drawCleanPlugin(ANPCanvas* canvas = NULL);
+ ANPCanvas* getCanvas(ANPRectI* dirtyRect = NULL);
+ ANPCanvas* getCanvas(ANPRectF* dirtyRect);
+ const char* getColorText();
+ void paintMouse(int x, int y);
+ void paintTouch();
+ void releaseCanvas(ANPCanvas*);
+ void toggleInputMethod();
+ void togglePaintColor();
+ ANPRectF* validTouch(int x, int y);
+
+ bool m_isTouchActive;
+ bool m_isTouchCurrentInput;
+ bool m_surfaceReady;
+
+ ANPSurface* m_surface;
+ ANPPath* m_touchPath;
+
+ ANPRectF m_drawingSurface;
+ ANPRectF m_inputToggle;
+ ANPRectF m_colorToggle;
+ ANPRectF m_clearSurface;
+
+ ANPPaint* m_paintSurface;
+ ANPPaint* m_paintButton;
+
+ ANPColor m_activePaintColor;
+ static const ANPColor s_redColor = 0xFFFF0000;
+ static const ANPColor s_greenColor = 0xFF00FF00;
+ static const ANPColor s_blueColor = 0xFF0000FF;
+};
+
+#endif // paintPlugin__DEFINED
diff --git a/samples/BrowserPlugin/res/drawable/sample_browser_plugin.png b/samples/BrowserPlugin/res/drawable/sample_browser_plugin.png
new file mode 100755
index 0000000..47c79d1
--- /dev/null
+++ b/samples/BrowserPlugin/res/drawable/sample_browser_plugin.png
Binary files differ
diff --git a/samples/BrowserPlugin/res/values/strings.xml b/samples/BrowserPlugin/res/values/strings.xml
new file mode 100644
index 0000000..1f8dd49
--- /dev/null
+++ b/samples/BrowserPlugin/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<resources>
+ <string name="sample_browser_plugin">Sample Browser Plugin</string>
+</resources>
diff --git a/samples/BrowserPlugin/src/com/android/sampleplugin/SamplePlugin.java b/samples/BrowserPlugin/src/com/android/sampleplugin/SamplePlugin.java
new file mode 100644
index 0000000..9b8ce95
--- /dev/null
+++ b/samples/BrowserPlugin/src/com/android/sampleplugin/SamplePlugin.java
@@ -0,0 +1,15 @@
+package com.android.sampleplugin;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class SamplePlugin extends Service {
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/samples/FixedGridLayout/Android.mk b/samples/FixedGridLayout/Android.mk
new file mode 100644
index 0000000..1562cb9
--- /dev/null
+++ b/samples/FixedGridLayout/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := FixedGridLayout
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/samples/FixedGridLayout/AndroidManifest.xml b/samples/FixedGridLayout/AndroidManifest.xml
new file mode 100644
index 0000000..9acd309
--- /dev/null
+++ b/samples/FixedGridLayout/AndroidManifest.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.fixedgridlayout"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <application android:label="@string/app_name">
+ <activity android:name=".FixedGridLayoutTest"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/samples/FixedGridLayout/res/drawable/bugdroid.png b/samples/FixedGridLayout/res/drawable/bugdroid.png
new file mode 100644
index 0000000..855484a
--- /dev/null
+++ b/samples/FixedGridLayout/res/drawable/bugdroid.png
Binary files differ
diff --git a/samples/FixedGridLayout/res/layout/main.xml b/samples/FixedGridLayout/res/layout/main.xml
new file mode 100644
index 0000000..93e2d5e
--- /dev/null
+++ b/samples/FixedGridLayout/res/layout/main.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.example.android.fixedgridlayout.FixedGridLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res/com.example.android.fixedgridlayout"
+ android:id="@+id/grid"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ app:cellWidth="80dp"
+ app:cellHeight="100dp"
+ >
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/bugdroid"
+ />
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/bugdroid"
+ />
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/bugdroid"
+ />
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/bugdroid"
+ />
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/bugdroid"
+ />
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/bugdroid"
+ />
+</com.example.android.fixedgridlayout.FixedGridLayout>
+
diff --git a/samples/FixedGridLayout/res/values/attrs.xml b/samples/FixedGridLayout/res/values/attrs.xml
new file mode 100644
index 0000000..8287d0e
--- /dev/null
+++ b/samples/FixedGridLayout/res/values/attrs.xml
@@ -0,0 +1,7 @@
+<resources>
+ <declare-styleable name="FixedGridLayout">
+ <attr name="cellWidth" format="dimension" />
+ <attr name="cellHeight" format="dimension" />
+ </declare-styleable>
+</resources>
+
diff --git a/samples/FixedGridLayout/res/values/strings.xml b/samples/FixedGridLayout/res/values/strings.xml
new file mode 100644
index 0000000..14563c7
--- /dev/null
+++ b/samples/FixedGridLayout/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">FixedGridLayoutTest</string>
+</resources>
diff --git a/samples/FixedGridLayout/src/com/example/android/fixedgridlayout/FixedGridLayout.java b/samples/FixedGridLayout/src/com/example/android/fixedgridlayout/FixedGridLayout.java
new file mode 100644
index 0000000..413157f
--- /dev/null
+++ b/samples/FixedGridLayout/src/com/example/android/fixedgridlayout/FixedGridLayout.java
@@ -0,0 +1,119 @@
+/*
+ * 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.example.android.fixedgridlayout;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.SparseIntArray;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewDebug;
+
+/**
+ * A layout that arranges its children in a grid. The size of the
+ * cells is set by the {@link #setCellSize} method and the
+ * android:cell_width and android:cell_height attributes in XML.
+ * The number of rows and columns is determined at runtime. Each
+ * cell contains exactly one view, and they flow in the natural
+ * child order (the order in which they were added, or the index
+ * in {@link #addViewAt}. Views can not span multiple cells.
+ */
+public class FixedGridLayout extends ViewGroup {
+ int mCellWidth;
+ int mCellHeight;
+
+ public FixedGridLayout(Context context) {
+ super(context);
+ }
+
+ public FixedGridLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ // Read the resource attributes.
+ TypedArray a = context.obtainStyledAttributes(
+ attrs, R.styleable.FixedGridLayout);
+ mCellWidth = a.getDimensionPixelSize(
+ R.styleable.FixedGridLayout_cellWidth, -1);
+ mCellHeight = a.getDimensionPixelSize(
+ R.styleable.FixedGridLayout_cellHeight, -1);
+ a.recycle();
+ }
+
+ public void setCellWidth(int px) {
+ mCellWidth = px;
+ requestLayout();
+ }
+
+ public void setCellHeight(int px) {
+ mCellHeight = px;
+ requestLayout();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int cellWidthSpec = MeasureSpec.makeMeasureSpec(mCellWidth,
+ MeasureSpec.AT_MOST);
+ int cellHeightSpec = MeasureSpec.makeMeasureSpec(mCellHeight,
+ MeasureSpec.AT_MOST);
+
+ int count = getChildCount();
+ for (int index=0; index<count; index++) {
+ final View child = getChildAt(index);
+ child.measure(cellWidthSpec, cellHeightSpec);
+ }
+ // Use the size our parents gave us
+ setMeasuredDimension(resolveSize(mCellWidth*count, widthMeasureSpec),
+ resolveSize(mCellHeight*count, heightMeasureSpec));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int cellWidth = mCellWidth;
+ int cellHeight = mCellHeight;
+ int columns = (r - l) / cellWidth;
+ if (columns < 0) {
+ columns = 1;
+ }
+ int x = 0;
+ int y = 0;
+ int i = 0;
+ int count = getChildCount();
+ for (int index=0; index<count; index++) {
+ final View child = getChildAt(index);
+
+ int w = child.getMeasuredWidth();
+ int h = child.getMeasuredHeight();
+
+ int left = x + ((cellWidth-w)/2);
+ int top = y + ((cellHeight-h)/2);
+
+ child.layout(left, top, left+w, top+h);
+ if (i >= (columns-1)) {
+ // advance to next row
+ i = 0;
+ x = 0;
+ y += cellHeight;
+ } else {
+ i++;
+ x += cellWidth;
+ }
+ }
+ }
+}
+
diff --git a/samples/FixedGridLayout/src/com/example/android/fixedgridlayout/FixedGridLayoutTest.java b/samples/FixedGridLayout/src/com/example/android/fixedgridlayout/FixedGridLayoutTest.java
new file mode 100644
index 0000000..19c9da7
--- /dev/null
+++ b/samples/FixedGridLayout/src/com/example/android/fixedgridlayout/FixedGridLayoutTest.java
@@ -0,0 +1,19 @@
+package com.example.android.fixedgridlayout;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class FixedGridLayoutTest extends Activity
+{
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ FixedGridLayout grid = (FixedGridLayout)findViewById(R.id.grid);
+ grid.setCellWidth(80);
+ grid.setCellHeight(80);
+ }
+}
diff --git a/samples/Home/src/com/example/android/home/Home.java b/samples/Home/src/com/example/android/home/Home.java
index c23f7b8..7cae87e 100644
--- a/samples/Home/src/com/example/android/home/Home.java
+++ b/samples/Home/src/com/example/android/home/Home.java
@@ -110,6 +110,9 @@
private boolean mBlockAnimation;
+ private boolean mHomeDown;
+ private boolean mBackDown;
+
private View mShowApplications;
private CheckBox mShowApplicationsCheck;
@@ -396,19 +399,44 @@
}
@Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ if (!hasFocus) {
+ mBackDown = mHomeDown = false;
+ }
+ }
+
+ @Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_BACK:
+ mBackDown = true;
return true;
case KeyEvent.KEYCODE_HOME:
+ mHomeDown = true;
+ return true;
+ }
+ } else if (event.getAction() == KeyEvent.ACTION_UP) {
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_BACK:
+ if (!event.isCanceled()) {
+ // Do BACK behavior.
+ }
+ mBackDown = true;
+ return true;
+ case KeyEvent.KEYCODE_HOME:
+ if (!event.isCanceled()) {
+ // Do HOME behavior.
+ }
+ mHomeDown = true;
return true;
}
}
return super.dispatchKeyEvent(event);
}
-
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
diff --git a/simulator/app/Android.mk b/simulator/app/Android.mk
index c6e1d14..3ce7cd5 100644
--- a/simulator/app/Android.mk
+++ b/simulator/app/Android.mk
@@ -24,12 +24,15 @@
PhoneCollection.cpp \
PhoneData.cpp \
PhoneWindow.cpp \
+ Pipe.cpp \
Preferences.cpp \
PrefsDialog.cpp \
PropertyServer.cpp \
Semaphore.cpp \
Shmem.cpp \
- UserEvent.cpp
+ UserEvent.cpp \
+ executablepath_linux.cpp \
+ ported.cpp
LOCAL_STATIC_LIBRARIES := \
libtinyxml
diff --git a/simulator/app/LoadableImage.cpp b/simulator/app/LoadableImage.cpp
index e5bd0f3..513165b 100644
--- a/simulator/app/LoadableImage.cpp
+++ b/simulator/app/LoadableImage.cpp
@@ -17,7 +17,7 @@
#include "AssetStream.h"
#include "MyApp.h"
-#include <utils.h>
+#include "utils.h"
#include <stdio.h>
diff --git a/simulator/app/LocalBiChannel.h b/simulator/app/LocalBiChannel.h
index ce04bc0..a4f4d62 100644
--- a/simulator/app/LocalBiChannel.h
+++ b/simulator/app/LocalBiChannel.h
@@ -10,7 +10,7 @@
#error DO NOT USE THIS FILE IN THE DEVICE BUILD
#endif
-#include <utils/Pipe.h>
+#include "Pipe.h"
namespace android {
diff --git a/simulator/app/MessageStream.h b/simulator/app/MessageStream.h
index de9c398..82a9b4c 100644
--- a/simulator/app/MessageStream.h
+++ b/simulator/app/MessageStream.h
@@ -17,7 +17,7 @@
#error DO NOT USE THIS FILE IN THE DEVICE BUILD
#endif
-#include <utils/Pipe.h>
+#include "Pipe.h"
#include <stdlib.h>
#include <cutils/uio.h>
diff --git a/simulator/app/MyApp.cpp b/simulator/app/MyApp.cpp
index 313e44d..fd610b1 100644
--- a/simulator/app/MyApp.cpp
+++ b/simulator/app/MyApp.cpp
@@ -16,7 +16,7 @@
#include "MainFrame.h"
#include "MyApp.h"
-#include <utils/executablepath.h>
+#include "executablepath.h"
#include <stdio.h>
#include <unistd.h>
diff --git a/simulator/app/PhoneCollection.cpp b/simulator/app/PhoneCollection.cpp
index 5cddfa8..e1882cc 100644
--- a/simulator/app/PhoneCollection.cpp
+++ b/simulator/app/PhoneCollection.cpp
@@ -18,7 +18,7 @@
#include "PhoneData.h"
#include "MyApp.h"
-#include <utils.h>
+#include "utils.h"
#include <stdlib.h>
#include <unistd.h>
diff --git a/simulator/app/PhoneData.cpp b/simulator/app/PhoneData.cpp
index 48614fd..f15df2b 100644
--- a/simulator/app/PhoneData.cpp
+++ b/simulator/app/PhoneData.cpp
@@ -19,7 +19,7 @@
#include "PhoneCollection.h"
#include "MyApp.h"
-#include <utils.h>
+#include "utils.h"
#include <utils/AssetManager.h>
#include <utils/String8.h>
diff --git a/simulator/app/PhoneData.h b/simulator/app/PhoneData.h
index c7f4732..2d7003a 100644
--- a/simulator/app/PhoneData.h
+++ b/simulator/app/PhoneData.h
@@ -22,7 +22,7 @@
#include "PhoneButton.h"
#include "LoadableImage.h"
#include <ui/PixelFormat.h>
-#include <utils.h>
+#include "utils.h"
/*
diff --git a/simulator/app/Pipe.cpp b/simulator/app/Pipe.cpp
new file mode 100644
index 0000000..05ce790
--- /dev/null
+++ b/simulator/app/Pipe.cpp
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Unidirectional pipe.
+//
+
+#include "Pipe.h"
+#include <utils/Log.h>
+
+#if defined(HAVE_WIN32_IPC)
+# include <windows.h>
+#else
+# include <fcntl.h>
+# include <unistd.h>
+# include <errno.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+using namespace android;
+
+const unsigned long kInvalidHandle = (unsigned long) -1;
+
+
+/*
+ * Constructor. Do little.
+ */
+Pipe::Pipe(void)
+ : mReadNonBlocking(false), mReadHandle(kInvalidHandle),
+ mWriteHandle(kInvalidHandle)
+{
+}
+
+/*
+ * Destructor. Use the system-appropriate close call.
+ */
+Pipe::~Pipe(void)
+{
+#if defined(HAVE_WIN32_IPC)
+ if (mReadHandle != kInvalidHandle) {
+ if (!CloseHandle((HANDLE)mReadHandle))
+ LOG(LOG_WARN, "pipe", "failed closing read handle (%ld)\n",
+ mReadHandle);
+ }
+ if (mWriteHandle != kInvalidHandle) {
+ FlushFileBuffers((HANDLE)mWriteHandle);
+ if (!CloseHandle((HANDLE)mWriteHandle))
+ LOG(LOG_WARN, "pipe", "failed closing write handle (%ld)\n",
+ mWriteHandle);
+ }
+#else
+ if (mReadHandle != kInvalidHandle) {
+ if (close((int) mReadHandle) != 0)
+ LOG(LOG_WARN, "pipe", "failed closing read fd (%d)\n",
+ (int) mReadHandle);
+ }
+ if (mWriteHandle != kInvalidHandle) {
+ if (close((int) mWriteHandle) != 0)
+ LOG(LOG_WARN, "pipe", "failed closing write fd (%d)\n",
+ (int) mWriteHandle);
+ }
+#endif
+}
+
+/*
+ * Create the pipe.
+ *
+ * Use the POSIX stuff for everything but Windows.
+ */
+bool Pipe::create(void)
+{
+ assert(mReadHandle == kInvalidHandle);
+ assert(mWriteHandle == kInvalidHandle);
+
+#if defined(HAVE_WIN32_IPC)
+ /* we use this across processes, so they need to be inheritable */
+ HANDLE handles[2];
+ SECURITY_ATTRIBUTES saAttr;
+
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+
+ if (!CreatePipe(&handles[0], &handles[1], &saAttr, 0)) {
+ LOG(LOG_ERROR, "pipe", "unable to create pipe\n");
+ return false;
+ }
+ mReadHandle = (unsigned long) handles[0];
+ mWriteHandle = (unsigned long) handles[1];
+ return true;
+#else
+ int fds[2];
+
+ if (pipe(fds) != 0) {
+ LOG(LOG_ERROR, "pipe", "unable to create pipe\n");
+ return false;
+ }
+ mReadHandle = fds[0];
+ mWriteHandle = fds[1];
+ return true;
+#endif
+}
+
+/*
+ * Create a "half pipe". Please, no Segway riding.
+ */
+bool Pipe::createReader(unsigned long handle)
+{
+ mReadHandle = handle;
+ assert(mWriteHandle == kInvalidHandle);
+ return true;
+}
+
+/*
+ * Create a "half pipe" for writing.
+ */
+bool Pipe::createWriter(unsigned long handle)
+{
+ mWriteHandle = handle;
+ assert(mReadHandle == kInvalidHandle);
+ return true;
+}
+
+/*
+ * Return "true" if create() has been called successfully.
+ */
+bool Pipe::isCreated(void)
+{
+ // one or the other should be open
+ return (mReadHandle != kInvalidHandle || mWriteHandle != kInvalidHandle);
+}
+
+
+/*
+ * Read data from the pipe.
+ *
+ * For Linux and Darwin, just call read(). For Windows, implement
+ * non-blocking reads by calling PeekNamedPipe first.
+ */
+int Pipe::read(void* buf, int count)
+{
+ assert(mReadHandle != kInvalidHandle);
+
+#if defined(HAVE_WIN32_IPC)
+ DWORD totalBytesAvail = count;
+ DWORD bytesRead;
+
+ if (mReadNonBlocking) {
+ // use PeekNamedPipe to adjust read count expectations
+ if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL,
+ &totalBytesAvail, NULL))
+ {
+ LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n");
+ return -1;
+ }
+
+ if (totalBytesAvail == 0)
+ return 0;
+ }
+
+ if (!ReadFile((HANDLE) mReadHandle, buf, totalBytesAvail, &bytesRead,
+ NULL))
+ {
+ DWORD err = GetLastError();
+ if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE)
+ return 0;
+ LOG(LOG_ERROR, "pipe", "ReadFile failed (err=%ld)\n", err);
+ return -1;
+ }
+
+ return (int) bytesRead;
+#else
+ int cc;
+ cc = ::read(mReadHandle, buf, count);
+ if (cc < 0 && errno == EAGAIN)
+ return 0;
+ return cc;
+#endif
+}
+
+/*
+ * Write data to the pipe.
+ *
+ * POSIX systems are trivial, Windows uses a different call and doesn't
+ * handle non-blocking writes.
+ *
+ * If we add non-blocking support here, we probably want to make it an
+ * all-or-nothing write.
+ *
+ * DO NOT use LOG() here, we could be writing a log message.
+ */
+int Pipe::write(const void* buf, int count)
+{
+ assert(mWriteHandle != kInvalidHandle);
+
+#if defined(HAVE_WIN32_IPC)
+ DWORD bytesWritten;
+
+ if (mWriteNonBlocking) {
+ // BUG: can't use PeekNamedPipe() to get the amount of space
+ // left. Looks like we need to use "overlapped I/O" functions.
+ // I just don't care that much.
+ }
+
+ if (!WriteFile((HANDLE) mWriteHandle, buf, count, &bytesWritten, NULL)) {
+ // can't LOG, use stderr
+ fprintf(stderr, "WriteFile failed (err=%ld)\n", GetLastError());
+ return -1;
+ }
+
+ return (int) bytesWritten;
+#else
+ int cc;
+ cc = ::write(mWriteHandle, buf, count);
+ if (cc < 0 && errno == EAGAIN)
+ return 0;
+ return cc;
+#endif
+}
+
+/*
+ * Figure out if there is data available on the read fd.
+ *
+ * We return "true" on error because we want the caller to try to read
+ * from the pipe. They'll notice the read failure and do something
+ * appropriate.
+ */
+bool Pipe::readReady(void)
+{
+ assert(mReadHandle != kInvalidHandle);
+
+#if defined(HAVE_WIN32_IPC)
+ DWORD totalBytesAvail;
+
+ if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL,
+ &totalBytesAvail, NULL))
+ {
+ LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n");
+ return true;
+ }
+
+ return (totalBytesAvail != 0);
+#else
+ errno = 0;
+ fd_set readfds;
+ struct timeval tv = { 0, 0 };
+ int cc;
+
+ FD_ZERO(&readfds);
+ FD_SET(mReadHandle, &readfds);
+
+ cc = select(mReadHandle+1, &readfds, NULL, NULL, &tv);
+ if (cc < 0) {
+ LOG(LOG_ERROR, "pipe", "select() failed\n");
+ return true;
+ } else if (cc == 0) {
+ /* timed out, nothing available */
+ return false;
+ } else if (cc == 1) {
+ /* our fd is ready */
+ return true;
+ } else {
+ LOG(LOG_ERROR, "pipe", "HUH? select() returned > 1\n");
+ return true;
+ }
+#endif
+}
+
+/*
+ * Enable or disable non-blocking mode for the read descriptor.
+ *
+ * NOTE: the calls succeed under Mac OS X, but the pipe doesn't appear to
+ * actually be in non-blocking mode. If this matters -- i.e. you're not
+ * using a select() call -- put a call to readReady() in front of the
+ * ::read() call, with a PIPE_NONBLOCK_BROKEN #ifdef in the Makefile for
+ * Darwin.
+ */
+bool Pipe::setReadNonBlocking(bool val)
+{
+ assert(mReadHandle != kInvalidHandle);
+
+#if defined(HAVE_WIN32_IPC)
+ // nothing to do
+#else
+ int flags;
+
+ if (fcntl(mReadHandle, F_GETFL, &flags) == -1) {
+ LOG(LOG_ERROR, "pipe", "couldn't get flags for pipe read fd\n");
+ return false;
+ }
+ if (val)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~(O_NONBLOCK);
+ if (fcntl(mReadHandle, F_SETFL, &flags) == -1) {
+ LOG(LOG_ERROR, "pipe", "couldn't set flags for pipe read fd\n");
+ return false;
+ }
+#endif
+
+ mReadNonBlocking = val;
+ return true;
+}
+
+/*
+ * Enable or disable non-blocking mode for the write descriptor.
+ *
+ * As with setReadNonBlocking(), this does not work on the Mac.
+ */
+bool Pipe::setWriteNonBlocking(bool val)
+{
+ assert(mWriteHandle != kInvalidHandle);
+
+#if defined(HAVE_WIN32_IPC)
+ // nothing to do
+#else
+ int flags;
+
+ if (fcntl(mWriteHandle, F_GETFL, &flags) == -1) {
+ LOG(LOG_WARN, "pipe",
+ "Warning: couldn't get flags for pipe write fd (errno=%d)\n",
+ errno);
+ return false;
+ }
+ if (val)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~(O_NONBLOCK);
+ if (fcntl(mWriteHandle, F_SETFL, &flags) == -1) {
+ LOG(LOG_WARN, "pipe",
+ "Warning: couldn't set flags for pipe write fd (errno=%d)\n",
+ errno);
+ return false;
+ }
+#endif
+
+ mWriteNonBlocking = val;
+ return true;
+}
+
+/*
+ * Specify whether a file descriptor can be inherited by a child process.
+ * Under Linux this means setting the close-on-exec flag, under Windows
+ * this is SetHandleInformation(HANDLE_FLAG_INHERIT).
+ */
+bool Pipe::disallowReadInherit(void)
+{
+ if (mReadHandle == kInvalidHandle)
+ return false;
+
+#if defined(HAVE_WIN32_IPC)
+ if (SetHandleInformation((HANDLE) mReadHandle, HANDLE_FLAG_INHERIT, 0) == 0)
+ return false;
+#else
+ if (fcntl((int) mReadHandle, F_SETFD, FD_CLOEXEC) != 0)
+ return false;
+#endif
+ return true;
+}
+bool Pipe::disallowWriteInherit(void)
+{
+ if (mWriteHandle == kInvalidHandle)
+ return false;
+
+#if defined(HAVE_WIN32_IPC)
+ if (SetHandleInformation((HANDLE) mWriteHandle, HANDLE_FLAG_INHERIT, 0) == 0)
+ return false;
+#else
+ if (fcntl((int) mWriteHandle, F_SETFD, FD_CLOEXEC) != 0)
+ return false;
+#endif
+ return true;
+}
+
+/*
+ * Close read descriptor.
+ */
+bool Pipe::closeRead(void)
+{
+ if (mReadHandle == kInvalidHandle)
+ return false;
+
+#if defined(HAVE_WIN32_IPC)
+ if (mReadHandle != kInvalidHandle) {
+ if (!CloseHandle((HANDLE)mReadHandle)) {
+ LOG(LOG_WARN, "pipe", "failed closing read handle\n");
+ return false;
+ }
+ }
+#else
+ if (mReadHandle != kInvalidHandle) {
+ if (close((int) mReadHandle) != 0) {
+ LOG(LOG_WARN, "pipe", "failed closing read fd\n");
+ return false;
+ }
+ }
+#endif
+ mReadHandle = kInvalidHandle;
+ return true;
+}
+
+/*
+ * Close write descriptor.
+ */
+bool Pipe::closeWrite(void)
+{
+ if (mWriteHandle == kInvalidHandle)
+ return false;
+
+#if defined(HAVE_WIN32_IPC)
+ if (mWriteHandle != kInvalidHandle) {
+ if (!CloseHandle((HANDLE)mWriteHandle)) {
+ LOG(LOG_WARN, "pipe", "failed closing write handle\n");
+ return false;
+ }
+ }
+#else
+ if (mWriteHandle != kInvalidHandle) {
+ if (close((int) mWriteHandle) != 0) {
+ LOG(LOG_WARN, "pipe", "failed closing write fd\n");
+ return false;
+ }
+ }
+#endif
+ mWriteHandle = kInvalidHandle;
+ return true;
+}
+
+/*
+ * Get the read handle.
+ */
+unsigned long Pipe::getReadHandle(void)
+{
+ assert(mReadHandle != kInvalidHandle);
+
+ return mReadHandle;
+}
+
+/*
+ * Get the write handle.
+ */
+unsigned long Pipe::getWriteHandle(void)
+{
+ assert(mWriteHandle != kInvalidHandle);
+
+ return mWriteHandle;
+}
+
diff --git a/simulator/app/Pipe.h b/simulator/app/Pipe.h
new file mode 100644
index 0000000..6404168
--- /dev/null
+++ b/simulator/app/Pipe.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// FIFO I/O.
+//
+#ifndef _LIBS_UTILS_PIPE_H
+#define _LIBS_UTILS_PIPE_H
+
+#ifdef HAVE_ANDROID_OS
+#error DO NOT USE THIS FILE IN THE DEVICE BUILD
+#endif
+
+namespace android {
+
+/*
+ * Simple anonymous unidirectional pipe.
+ *
+ * The primary goal is to create an implementation with minimal overhead
+ * under Linux. Making Windows, Mac OS X, and Linux all work the same way
+ * is a secondary goal. Part of this goal is to have something that can
+ * be fed to a select() call, so that the application can sleep in the
+ * kernel until something interesting happens.
+ */
+class Pipe {
+public:
+ Pipe(void);
+ virtual ~Pipe(void);
+
+ /* Create the pipe */
+ bool create(void);
+
+ /* Create a read-only pipe, using the supplied handle as read handle */
+ bool createReader(unsigned long handle);
+ /* Create a write-only pipe, using the supplied handle as write handle */
+ bool createWriter(unsigned long handle);
+
+ /* Is this object ready to go? */
+ bool isCreated(void);
+
+ /*
+ * Read "count" bytes from the pipe. Returns the amount of data read,
+ * or 0 if no data available and we're non-blocking.
+ * Returns -1 on error.
+ */
+ int read(void* buf, int count);
+
+ /*
+ * Write "count" bytes into the pipe. Returns number of bytes written,
+ * or 0 if there's no room for more data and we're non-blocking.
+ * Returns -1 on error.
+ */
+ int write(const void* buf, int count);
+
+ /* Returns "true" if data is available to read */
+ bool readReady(void);
+
+ /* Enable or disable non-blocking I/O for reads */
+ bool setReadNonBlocking(bool val);
+ /* Enable or disable non-blocking I/O for writes. Only works on Linux. */
+ bool setWriteNonBlocking(bool val);
+
+ /*
+ * Get the handle. Only useful in some platform-specific situations.
+ */
+ unsigned long getReadHandle(void);
+ unsigned long getWriteHandle(void);
+
+ /*
+ * Modify inheritance, i.e. whether or not a child process will get
+ * copies of the descriptors. Systems with fork+exec allow us to close
+ * the descriptors before launching the child process, but Win32
+ * doesn't allow it.
+ */
+ bool disallowReadInherit(void);
+ bool disallowWriteInherit(void);
+
+ /*
+ * Close one side or the other. Useful in the parent after launching
+ * a child process.
+ */
+ bool closeRead(void);
+ bool closeWrite(void);
+
+private:
+ bool mReadNonBlocking;
+ bool mWriteNonBlocking;
+
+ unsigned long mReadHandle;
+ unsigned long mWriteHandle;
+};
+
+}; // android
+
+#endif // _LIBS_UTILS_PIPE_H
diff --git a/simulator/app/UserEventMessage.h b/simulator/app/UserEventMessage.h
index 4a66cc2..9b94ea7 100644
--- a/simulator/app/UserEventMessage.h
+++ b/simulator/app/UserEventMessage.h
@@ -6,7 +6,7 @@
#ifndef _SIM_USER_EVENT_MESSAGE_H
#define _SIM_USER_EVENT_MESSAGE_H
-#include <utils.h>
+#include "utils.h"
#include "LogMessage.h"
/*
diff --git a/simulator/app/executablepath.h b/simulator/app/executablepath.h
new file mode 100644
index 0000000..889982d
--- /dev/null
+++ b/simulator/app/executablepath.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _SIM_EXECUTABLEPATH_H
+#define _SIM_EXECUTABLEPATH_H
+
+#include <limits.h>
+
+// returns the path to this executable
+#if __cplusplus
+extern "C"
+#endif
+void executablepath(char s[PATH_MAX]);
+
+#endif // _SIM_EXECUTABLEPATH_H
diff --git a/simulator/app/executablepath_darwin.cpp b/simulator/app/executablepath_darwin.cpp
new file mode 100644
index 0000000..9ec1c18
--- /dev/null
+++ b/simulator/app/executablepath_darwin.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#include "executablepath.h"
+#import <Carbon/Carbon.h>
+#include <unistd.h>
+
+void executablepath(char s[PATH_MAX])
+{
+ ProcessSerialNumber psn;
+ GetCurrentProcess(&psn);
+ CFDictionaryRef dict;
+ dict = ProcessInformationCopyDictionary(&psn, 0xffffffff);
+ CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict,
+ CFSTR("CFBundleExecutable"));
+ CFStringGetCString(value, s, PATH_MAX+1, kCFStringEncodingUTF8);
+}
+
diff --git a/simulator/app/executablepath_linux.cpp b/simulator/app/executablepath_linux.cpp
new file mode 100644
index 0000000..e4a3a2b
--- /dev/null
+++ b/simulator/app/executablepath_linux.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#include "executablepath.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdio.h>
+
+void executablepath(char exe[PATH_MAX])
+{
+ char proc[100];
+ sprintf(proc, "/proc/%d/exe", getpid());
+
+ int err = readlink(proc, exe, PATH_MAX);
+}
+
diff --git a/simulator/app/ported.cpp b/simulator/app/ported.cpp
new file mode 100644
index 0000000..232b302
--- /dev/null
+++ b/simulator/app/ported.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Ports of standard functions that don't exist on a specific platform.
+//
+// Note these are NOT in the "android" namespace.
+//
+#include "ported.h"
+
+#if defined(NEED_GETTIMEOFDAY) || defined(NEED_USLEEP)
+# include <sys/time.h>
+# include <windows.h>
+#endif
+
+
+#if defined(NEED_GETTIMEOFDAY)
+/*
+ * Replacement gettimeofday() for Windows environments (primarily MinGW).
+ *
+ * Ignores "tz".
+ */
+int gettimeofday(struct timeval* ptv, struct timezone* tz)
+{
+ long long nsTime; // time in 100ns units since Jan 1 1601
+ FILETIME ft;
+
+ if (tz != NULL) {
+ // oh well
+ }
+
+ ::GetSystemTimeAsFileTime(&ft);
+ nsTime = (long long) ft.dwHighDateTime << 32 |
+ (long long) ft.dwLowDateTime;
+ // convert to time in usec since Jan 1 1970
+ ptv->tv_usec = (long) ((nsTime / 10LL) % 1000000LL);
+ ptv->tv_sec = (long) ((nsTime - 116444736000000000LL) / 10000000LL);
+
+ return 0;
+}
+#endif
+
+#if defined(NEED_USLEEP)
+//
+// Replacement usleep for Windows environments (primarily MinGW).
+//
+void usleep(unsigned long usec)
+{
+ // Win32 API function Sleep() takes milliseconds
+ ::Sleep((usec + 500) / 1000);
+}
+#endif
+
+#if 0 //defined(NEED_PIPE)
+//
+// Replacement pipe() command for MinGW
+//
+// The _O_NOINHERIT flag sets bInheritHandle to FALSE in the
+// SecurityAttributes argument to CreatePipe(). This means the handles
+// aren't inherited when a new process is created. The examples I've seen
+// use it, possibly because there's a lot of junk going on behind the
+// scenes. (I'm assuming "process" and "thread" are different here, so
+// we should be okay spinning up a thread.) The recommended practice is
+// to dup() the descriptor you want the child to have.
+//
+// It appears that unnamed pipes can't do non-blocking ("overlapped") I/O.
+// You can't use select() either, since that only works on sockets. The
+// Windows API calls that are useful here all operate on a HANDLE, not
+// an integer file descriptor, and I don't think you can get there from
+// here. The "named pipe" stuff is insane.
+//
+int pipe(int filedes[2])
+{
+ return _pipe(filedes, 0, _O_BINARY | _O_NOINHERIT);
+}
+#endif
+
+#if defined(NEED_SETENV)
+/*
+ * MinGW lacks these. For now, just stub them out so the code compiles.
+ */
+int setenv(const char* name, const char* value, int overwrite)
+{
+ return 0;
+}
+void unsetenv(const char* name)
+{
+}
+char* getenv(const char* name)
+{
+ return NULL;
+}
+#endif
diff --git a/simulator/app/ported.h b/simulator/app/ported.h
new file mode 100644
index 0000000..eb3be01
--- /dev/null
+++ b/simulator/app/ported.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Standard functions ported to the current platform. Note these are NOT
+// in the "android" namespace.
+//
+#ifndef _LIBS_UTILS_PORTED_H
+#define _LIBS_UTILS_PORTED_H
+
+#include <sys/time.h> // for timeval
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* library replacement functions */
+#if defined(NEED_GETTIMEOFDAY)
+int gettimeofday(struct timeval* tv, struct timezone* tz);
+#endif
+#if defined(NEED_USLEEP)
+void usleep(unsigned long usec);
+#endif
+#if defined(NEED_PIPE)
+int pipe(int filedes[2]);
+#endif
+#if defined(NEED_SETENV)
+int setenv(const char* name, const char* value, int overwrite);
+void unsetenv(const char* name);
+char* getenv(const char* name);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LIBS_UTILS_PORTED_H
diff --git a/simulator/app/utils.h b/simulator/app/utils.h
new file mode 100644
index 0000000..b74845f
--- /dev/null
+++ b/simulator/app/utils.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Handy utility functions and portability code. This file includes all
+// of the generally-useful headers in the "utils" directory.
+//
+#ifndef _SIM_UTILS_H
+#define _SIM_UTILS_H
+
+#include "ported.h"
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/Timers.h>
+#include <utils/List.h>
+#include <utils/StringArray.h>
+#include <utils/misc.h>
+#include <utils/Errors.h>
+
+#endif // _SIM_UTILS_H
diff --git a/simulator/wrapsim/Android.mk b/simulator/wrapsim/Android.mk
index 0b7890d..f9a2414 100644
--- a/simulator/wrapsim/Android.mk
+++ b/simulator/wrapsim/Android.mk
@@ -3,7 +3,6 @@
#
# Build instructions for simulator LD_PRELOAD wrapper.
#
-ifneq ($(TARGET_ARCH),arm)
ifeq ($(TARGET_SIMULATOR),true)
LOCAL_PATH:= $(call my-dir)
@@ -23,7 +22,8 @@
Intercept.c \
Log.c \
SimMgr.c \
- SysPower.c
+ SysPower.c \
+ Util.c
LOCAL_C_INCLUDES += prebuilt/common/esd
@@ -55,5 +55,3 @@
include $(BUILD_EXECUTABLE)
endif # ifeq ($(TARGET_SIMULATOR),true)
-endif
-# ifneq ($(TARGET_ARCH),arm)
diff --git a/simulator/wrapsim/Common.h b/simulator/wrapsim/Common.h
index a9c3bb8..463262f 100644
--- a/simulator/wrapsim/Common.h
+++ b/simulator/wrapsim/Common.h
@@ -14,5 +14,6 @@
#include "Log.h"
#include "SimMgr.h"
#include "Globals.h"
+#include "Util.h"
#endif /*_WRAPSIM_COMMON_H*/
diff --git a/simulator/wrapsim/DevEvent.c b/simulator/wrapsim/DevEvent.c
index 692856e..60060f4 100644
--- a/simulator/wrapsim/DevEvent.c
+++ b/simulator/wrapsim/DevEvent.c
@@ -31,14 +31,34 @@
* (For now, just pretend to be a "goldfish" like the emulator.)
*/
static const unsigned char gKeyBitMask[64] = {
+ // These bits indicate which keys the device has
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ // These bits indicate other capabilities, such
+ // as whether it's a trackball or a touchscreen
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // touchscreen
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+/*
+ * Abs bit mask, for EVIOCGBIT(EV_ABS).
+ *
+ * Pretend to be a normal single touch panel
+ */
+static const unsigned char gAbsBitMask[64] = {
+ // these bits indicate the capabilities of the touch screen
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ABS_X, ABS_Y
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
/*
@@ -193,7 +213,9 @@
} else if (!getenv("NOTOUCH") && _IOC_NR(urequest) == _IOC_NR(EVIOCGBIT(EV_ABS,0))) {
// absolute controllers (touch screen)
int maxLen = _IOC_SIZE(urequest);
- memset(argp, 0xff, maxLen);
+ if (maxLen > (int) sizeof(gAbsBitMask))
+ maxLen = sizeof(gAbsBitMask);
+ memcpy(argp, gAbsBitMask, maxLen);
} else if (_IOC_NR(urequest) >= _IOC_NR(EVIOCGABS(ABS_X)) &&
_IOC_NR(urequest) <= _IOC_NR(EVIOCGABS(ABS_MAX)))
diff --git a/simulator/wrapsim/DevFb.c b/simulator/wrapsim/DevFb.c
index c54403e..bfdbb22 100644
--- a/simulator/wrapsim/DevFb.c
+++ b/simulator/wrapsim/DevFb.c
@@ -14,6 +14,10 @@
#include <linux/fb.h>
typedef struct FbState {
+
+ /* refcount for dup() */
+ int refCount;
+
/* index into gWrapSim.display[] */
int displayIdx;
@@ -77,7 +81,13 @@
*/
static void freeState(FbState* fbState)
{
- free(fbState);
+ int oldcount;
+
+ oldcount = wsAtomicAdd(&fbState->refCount, -1);
+
+ if (oldcount == 0) {
+ free(fbState);
+ }
}
/*
@@ -242,6 +252,28 @@
}
/*
+ * dup() an existing fake descriptor
+ */
+static FakeDev* dupFb(FakeDev* dev, int fd)
+{
+ FakeDev* newDev = wsCreateFakeDev(dev->debugName);
+ if (newDev != NULL) {
+ newDev->mmap = mmapFb;
+ newDev->ioctl = ioctlFb;
+ newDev->close = closeFb;
+ newDev->dup = dupFb;
+
+ /* use state from existing FakeDev */
+ FbState* fbState = dev->state;
+ wsAtomicAdd(&fbState->refCount, 1);
+
+ newDev->state = fbState;
+ }
+
+ return newDev;
+}
+
+/*
* Open the console TTY device, which responds to a collection of ioctl()s.
*/
FakeDev* wsOpenDevFb(const char* pathName, int flags)
@@ -251,6 +283,7 @@
newDev->mmap = mmapFb;
newDev->ioctl = ioctlFb;
newDev->close = closeFb;
+ newDev->dup = dupFb;
FbState* fbState = calloc(1, sizeof(FbState));
diff --git a/simulator/wrapsim/FakeDev.c b/simulator/wrapsim/FakeDev.c
index 7d2494e..f03dd29 100644
--- a/simulator/wrapsim/FakeDev.c
+++ b/simulator/wrapsim/FakeDev.c
@@ -99,6 +99,11 @@
{
return 0;
}
+static FakeDev* noDup(FakeDev* dev, ...)
+{
+ notImplemented(dev, "dup");
+ return NULL;
+}
static int noRead(FakeDev* dev, ...)
{
return notImplemented(dev, "read");
@@ -146,6 +151,7 @@
newDev->state = NULL;
newDev->close = (Fake_close) noClose;
+ newDev->dup = (Fake_dup) noDup;
newDev->read = (Fake_read) noRead;
newDev->readv = (Fake_readv) noReadv;
newDev->write = (Fake_write) noWrite;
diff --git a/simulator/wrapsim/FakeDev.h b/simulator/wrapsim/FakeDev.h
index 4781cfc..65f47ae 100644
--- a/simulator/wrapsim/FakeDev.h
+++ b/simulator/wrapsim/FakeDev.h
@@ -12,13 +12,14 @@
typedef struct FakeDev FakeDev;
-typedef int (*Fake_close)(FakeDev* dev, int);
-typedef ssize_t (*Fake_read)(FakeDev* dev, int, void*, size_t);
-typedef ssize_t (*Fake_readv)(FakeDev* dev, int, const struct iovec*, int);
-typedef ssize_t (*Fake_write)(FakeDev* dev, int, const void*, size_t);
-typedef ssize_t (*Fake_writev)(FakeDev* dev, int, const struct iovec*, int);
-typedef void* (*Fake_mmap)(FakeDev* dev, void*, size_t, int, int, int, __off_t);
-typedef int (*Fake_ioctl)(FakeDev* dev, int, int, void*);
+typedef int (*Fake_close)(FakeDev* dev, int);
+typedef FakeDev* (*Fake_dup)(FakeDev* dev, int);
+typedef ssize_t (*Fake_read)(FakeDev* dev, int, void*, size_t);
+typedef ssize_t (*Fake_readv)(FakeDev* dev, int, const struct iovec*, int);
+typedef ssize_t (*Fake_write)(FakeDev* dev, int, const void*, size_t);
+typedef ssize_t (*Fake_writev)(FakeDev* dev, int, const struct iovec*, int);
+typedef void* (*Fake_mmap)(FakeDev* dev, void*, size_t, int, int, int, __off_t);
+typedef int (*Fake_ioctl)(FakeDev* dev, int, int, void*);
/*
* An open fake device entry.
@@ -42,6 +43,7 @@
* All other file descriptor operations should fail, usually with EBADF.
*/
Fake_close close;
+ Fake_dup dup;
Fake_read read;
Fake_readv readv;
Fake_write write;
diff --git a/simulator/wrapsim/Globals.h b/simulator/wrapsim/Globals.h
index 75c98d8..a8d834c 100644
--- a/simulator/wrapsim/Globals.h
+++ b/simulator/wrapsim/Globals.h
@@ -29,6 +29,7 @@
typedef int (*Func_open64)(const char*, int, mode_t);
typedef int (*Func_close)(int);
+typedef int (*Func_dup)(int);
typedef ssize_t (*Func_read)(int, void*, size_t);
typedef ssize_t (*Func_readv)(int, const struct iovec*, int);
typedef ssize_t (*Func_write)(int, const void*, size_t);
@@ -95,6 +96,7 @@
EXTERN_FUNC Func_open64 _ws_open64;
EXTERN_FUNC Func_close _ws_close;
+EXTERN_FUNC Func_dup _ws_dup;
EXTERN_FUNC Func_read _ws_read;
EXTERN_FUNC Func_readv _ws_readv;
EXTERN_FUNC Func_write _ws_write;
@@ -201,6 +203,9 @@
pthread_mutex_t fakeFdLock;
BitVector* fakeFdMap;
FakeDev* fakeFdList[kMaxFakeFdCount];
+
+ /* used for wsAtomicAdd */
+ pthread_mutex_t atomicLock;
};
extern struct WrapSimGlobals gWrapSim;
diff --git a/simulator/wrapsim/Init.c b/simulator/wrapsim/Init.c
index eed650b..3df0efe 100644
--- a/simulator/wrapsim/Init.c
+++ b/simulator/wrapsim/Init.c
@@ -40,6 +40,7 @@
_ws_open64 = dlsym(RTLD_NEXT, "open64");
_ws_close = dlsym(RTLD_NEXT, "close");
+ _ws_dup = dlsym(RTLD_NEXT, "dup");
_ws_read = dlsym(RTLD_NEXT, "read");
_ws_readv = dlsym(RTLD_NEXT, "readv");
_ws_write = dlsym(RTLD_NEXT, "write");
@@ -108,6 +109,8 @@
gWrapSim.fakeFdMap = wsAllocBitVector(kMaxFakeFdCount, 0);
memset(gWrapSim.fakeFdList, 0, sizeof(gWrapSim.fakeFdList));
+ pthread_mutex_init(&gWrapSim.atomicLock, NULL);
+
gWrapSim.numDisplays = 0;
gWrapSim.keyInputDevice = NULL;
diff --git a/simulator/wrapsim/Intercept.c b/simulator/wrapsim/Intercept.c
index 49d77ee..3d4edb2 100644
--- a/simulator/wrapsim/Intercept.c
+++ b/simulator/wrapsim/Intercept.c
@@ -160,7 +160,7 @@
_rtype _fname( __VA_ARGS__ )
#define PASS_THROUGH_BODY(_fname, _patharg, ...) \
{ \
- CALLTRACEV("%s\n", __FUNCTION__); \
+ CALLTRACEV("%s(%s)\n", __FUNCTION__, _patharg); \
char pathBuf[PATH_MAX]; \
return _ws_##_fname(rewritePath(#_fname, pathBuf, _patharg), \
##__VA_ARGS__); \
@@ -631,6 +631,30 @@
}
+int dup(int fd)
+{
+ CALLTRACEV("%s(%d)\n", __FUNCTION__, fd);
+
+ FakeDev* dev = wsFakeDevFromFd(fd);
+ if (dev != NULL) {
+ FakeDev* newDev = dev->dup(dev, fd);
+ if (newDev != NULL) {
+ /*
+ * Now that the device entry is ready, add it to the list.
+ */
+ wsLog("## dup'ed fake dev %d: '%s' %p\n",
+ newDev->fd, newDev->debugName, newDev->state);
+ gWrapSim.fakeFdList[newDev->fd - kFakeFdBase] = newDev;
+ return newDev->fd;
+ }
+ return -1;
+ } else {
+ CALLTRACE("dup(%d)\n", fd);
+ return _ws_dup(fd);
+ }
+}
+
+
/*
* Close a file descriptor.
*/
@@ -799,6 +823,16 @@
return 0;
}
+/*
+ * Pretend to be running as root, so the Android framework
+ * doesn't complain about permission problems all over the
+ * place.
+ */
+uid_t getuid(void)
+{
+ return 0;
+}
+
#if 0
/*
* Create a pipe. (Only needed for debugging an fd leak.)
diff --git a/simulator/wrapsim/Util.c b/simulator/wrapsim/Util.c
new file mode 100644
index 0000000..33d903b
--- /dev/null
+++ b/simulator/wrapsim/Util.c
@@ -0,0 +1,13 @@
+
+#include "Common.h"
+
+int wsAtomicAdd(int *var, int val)
+{
+ int cc;
+ int ret;
+ cc = pthread_mutex_lock(&gWrapSim.atomicLock);
+ ret = *var;
+ *var = *var + val;
+ cc = pthread_mutex_unlock(&gWrapSim.atomicLock);
+ return ret;
+}
diff --git a/simulator/wrapsim/Util.h b/simulator/wrapsim/Util.h
new file mode 100644
index 0000000..e470802
--- /dev/null
+++ b/simulator/wrapsim/Util.h
@@ -0,0 +1,4 @@
+
+
+
+int wsAtomicAdd(int *var, int val);
diff --git a/testrunner/am_instrument_parser.py b/testrunner/am_instrument_parser.py
index cad87c0..4554c4d 100755
--- a/testrunner/am_instrument_parser.py
+++ b/testrunner/am_instrument_parser.py
@@ -80,7 +80,7 @@
code.
"""
- re_result = re.compile(r'INSTRUMENTATION_RESULT: ([^=]+)=(.+)$')
+ re_result = re.compile(r'INSTRUMENTATION_RESULT: ([^=]+)=(.*)$')
re_code = re.compile(r'INSTRUMENTATION_CODE: (\-?\d)$')
result_dict = {}
key = ''
@@ -135,38 +135,26 @@
self._test_name = None
self._status_code = None
self._failure_reason = None
+ self._fields_map = {}
- re_start_block = re.compile(
- r'\s*INSTRUMENTATION_STATUS: stream=(?P<stream>.*)'
- 'INSTRUMENTATION_STATUS: test=(?P<test>\w+)\s+'
- 'INSTRUMENTATION_STATUS: class=(?P<class>[\w\.]+)\s+'
- 'INSTRUMENTATION_STATUS: current=(?P<current>\d+)\s+'
- 'INSTRUMENTATION_STATUS: numtests=(?P<numtests>\d+)\s+'
- 'INSTRUMENTATION_STATUS: id=.*\s+'
- 'INSTRUMENTATION_STATUS_CODE: 1\s*', re.DOTALL)
+ re_status_code = re.search(r'INSTRUMENTATION_STATUS_CODE: '
+ '(?P<status_code>1|0|-1|-2)', result_block_string)
+ re_fields = re.compile(r'INSTRUMENTATION_STATUS: '
+ '(?P<key>[\w.]+)=(?P<value>.*?)(?=\nINSTRUMENTATION_STATUS)', re.DOTALL)
- re_end_block = re.compile(
- r'\s*INSTRUMENTATION_STATUS: stream=(?P<stream>.*)'
- 'INSTRUMENTATION_STATUS: test=(?P<test>\w+)\s+'
- '(INSTRUMENTATION_STATUS: stack=(?P<stack>.*))?'
- 'INSTRUMENTATION_STATUS: class=(?P<class>[\w\.]+)\s+'
- 'INSTRUMENTATION_STATUS: current=(?P<current>\d+)\s+'
- 'INSTRUMENTATION_STATUS: numtests=(?P<numtests>\d+)\s+'
- 'INSTRUMENTATION_STATUS: id=.*\s+'
- 'INSTRUMENTATION_STATUS_CODE: (?P<status_code>0|-1|-2)\s*', re.DOTALL)
+ for field in re_fields.finditer(result_block_string):
+ key, value = (field.group('key').strip(), field.group('value').strip())
+ if key.startswith('performance.'):
+ key = key[len('performance.'):]
+ self._fields_map[key] = value
+ self._fields_map.setdefault('class')
+ self._fields_map.setdefault('test')
- start_block_match = re_start_block.match(result_block_string)
- end_block_match = re_end_block.match(result_block_string)
-
- if start_block_match:
- self._test_name = "%s:%s" % (start_block_match.group('class'),
- start_block_match.group('test'))
- self._status_code = 1
- elif end_block_match:
- self._test_name = "%s:%s" % (end_block_match.group('class'),
- end_block_match.group('test'))
- self._status_code = int(end_block_match.group('status_code'))
- self._failure_reason = end_block_match.group('stack')
+ self._test_name = '%s:%s' % (self._fields_map['class'],
+ self._fields_map['test'])
+ self._status_code = int(re_status_code.group('status_code'))
+ if 'stack' in self._fields_map:
+ self._failure_reason = self._fields_map['stack']
def GetTestName(self):
return self._test_name
@@ -176,3 +164,6 @@
def GetFailureReason(self):
return self._failure_reason
+
+ def GetResultFields(self):
+ return self._fields_map
diff --git a/testrunner/run_command.py b/testrunner/run_command.py
index 7d1f547..79c7ea5 100755
--- a/testrunner/run_command.py
+++ b/testrunner/run_command.py
@@ -144,7 +144,7 @@
else:
# Need the full path to valgrind to avoid other versions on the system.
subproc = subprocess.Popen(["/usr/bin/valgrind", "--tool=memcheck",
- "--leak-check=yes", "-q", full_path],
+ "--leak-check=yes", "-q", binary],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# Cannot rely on the retcode of valgrind. Instead look for an empty output.
valgrind_out = subproc.communicate()[0].strip()
diff --git a/testrunner/test_defs.xml b/testrunner/test_defs.xml
index 6f073bb..d37a3df 100644
--- a/testrunner/test_defs.xml
+++ b/testrunner/test_defs.xml
@@ -44,6 +44,12 @@
coverage_target="framework"
continuous="true" />
+<test name="account"
+ build_path="frameworks/base/tests/AndroidTests"
+ package="com.android.unit_tests"
+ class="com.android.unit_tests.accounts.AccountManagerServiceTest"
+ coverage_target="framework" />
+
<test name="smoke"
build_path="frameworks/base/tests/SmokeTest"
package="com.android.smoketest.tests"
@@ -103,6 +109,12 @@
-->
+<test name="contentprovideroperation"
+ build_path="frameworks/base/tests/FrameworkTest"
+ package="com.android.frameworktest.tests"
+ class="android.content.ContentProviderOperationTest"
+ coverage_target="framework" />
+
<test name="tablemerger"
build_path="frameworks/base/tests/FrameworkTest"
package="com.android.frameworktest.tests"
@@ -441,7 +453,6 @@
<test name="mms"
build_path="packages/apps/Mms"
package="com.android.mms.tests"
- runner="com.android.mms.ui.MMSInstrumentationTestRunner"
coverage_target="Mms" />
<test name="mmslaunch"
@@ -467,6 +478,10 @@
description="Bionic libstdc++."
extra_build_args="BIONIC_TESTS=1" />
+<test-native name="libskia"
+ build_path="external/skia/tests"
+ description="Skia tests." />
+
<!-- Android STL tests -->
<test-native name="astl"
build_path="external/astl/tests"
diff --git a/testrunner/tests/am_instrument_parser_tests.py b/testrunner/tests/am_instrument_parser_tests.py
new file mode 100755
index 0000000..55356eb
--- /dev/null
+++ b/testrunner/tests/am_instrument_parser_tests.py
@@ -0,0 +1,201 @@
+#!/usr/bin/python2.4
+#
+#
+# Copyright 2009, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import sys
+import unittest
+sys.path.append('../..')
+
+from testrunner import am_instrument_parser
+
+
+class AmParserTest(unittest.TestCase):
+
+ def testParseAmInstResult(self):
+ result="""INSTRUMENTATION_RESULT: performance.java_size=4871
+INSTRUMENTATION_RESULT: stream=
+Error: Failed to generate emma coverage.
+INSTRUMENTATION_RESULT: performance.cpu_time=33846
+INSTRUMENTATION_CODE: -1
+"""
+ bundle_dict = \
+ am_instrument_parser._ParseInstrumentationFinishedBundle(result)
+ self.assertEquals(4871, bundle_dict['java_size'])
+ self.assertEquals(33846, bundle_dict['cpu_time'])
+ self.assertEquals("\nError: Failed to generate emma coverage.",
+ bundle_dict['stream'])
+
+ def testParseAmInstStatus(self):
+ # numtests before id
+ segment1 = """INSTRUMENTATION_STATUS: stream=
+INSTRUMENTATION_STATUS: test=testLaunchComplexActivity
+INSTRUMENTATION_STATUS: class=LaunchPerformanceTest
+INSTRUMENTATION_STATUS: current=1
+INSTRUMENTATION_STATUS: numtests=2
+INSTRUMENTATION_STATUS: id=InstrumentationTestRunner
+INSTRUMENTATION_STATUS_CODE: 1"""
+ segment2 = """INSTRUMENTATION_STATUS: stream=.
+INSTRUMENTATION_STATUS: test=testLaunchComplexActivity
+INSTRUMENTATION_STATUS: performance.cpu_time=866
+INSTRUMENTATION_STATUS: performance.execution_time=1242
+INSTRUMENTATION_STATUS: class=LaunchPerformanceTest
+INSTRUMENTATION_STATUS: current=1
+INSTRUMENTATION_STATUS: numtests=2
+INSTRUMENTATION_STATUS: id=InstrumentationTestRunner
+INSTRUMENTATION_STATUS_CODE: 0"""
+ # numtests after id
+ segment3 = """INSTRUMENTATION_STATUS: stream=
+INSTRUMENTATION_STATUS: test=testLaunchSimpleActivity
+INSTRUMENTATION_STATUS: class=LaunchPerformanceTest
+INSTRUMENTATION_STATUS: current=2
+INSTRUMENTATION_STATUS: id=InstrumentationTestRunner
+INSTRUMENTATION_STATUS: numtests=8
+INSTRUMENTATION_STATUS_CODE: 1"""
+ segment4 = """INSTRUMENTATION_STATUS: stream=.
+INSTRUMENTATION_STATUS: test=testLaunchSimpleActivity
+INSTRUMENTATION_STATUS: performance.cpu_time=590
+INSTRUMENTATION_STATUS: performance.execution_time=1122
+INSTRUMENTATION_STATUS: class=LaunchPerformanceTest
+INSTRUMENTATION_STATUS: current=2
+INSTRUMENTATION_STATUS: id=InstrumentationTestRunner
+INSTRUMENTATION_STATUS: numtests=8
+INSTRUMENTATION_STATUS_CODE: 0"""
+
+ result = am_instrument_parser.TestResult(segment1)
+ map = result.GetResultFields()
+ self.assertEquals('testLaunchComplexActivity', map['test'])
+ self.assertEquals('LaunchPerformanceTest', map['class'])
+ self.assertEquals('1', map['current'])
+ self.assertEquals('2', map['numtests'])
+ self.assertEquals('InstrumentationTestRunner', map['id'])
+ self.assertEquals(1, result.GetStatusCode())
+
+ result = am_instrument_parser.TestResult(segment2)
+ map = result.GetResultFields()
+ self.assertEquals('testLaunchComplexActivity', map['test'])
+ self.assertEquals('866', map['cpu_time'])
+ self.assertEquals('1242', map['execution_time'])
+ self.assertEquals('LaunchPerformanceTest', map['class'])
+ self.assertEquals('1', map['current'])
+ self.assertEquals('2', map['numtests'])
+ self.assertEquals('InstrumentationTestRunner', map['id'])
+ self.assertEquals(0, result.GetStatusCode())
+
+ result = am_instrument_parser.TestResult(segment3)
+ map = result.GetResultFields()
+ self.assertEquals('testLaunchSimpleActivity', map['test'])
+ self.assertEquals('LaunchPerformanceTest', map['class'])
+ self.assertEquals('2', map['current'])
+ self.assertEquals('8', map['numtests'])
+ self.assertEquals('InstrumentationTestRunner', map['id'])
+ self.assertEquals(1, result.GetStatusCode())
+
+ result = am_instrument_parser.TestResult(segment4)
+ map = result.GetResultFields()
+ self.assertEquals('testLaunchSimpleActivity', map['test'])
+ self.assertEquals('590', map['cpu_time'])
+ self.assertEquals('1122', map['execution_time'])
+ self.assertEquals('LaunchPerformanceTest', map['class'])
+ self.assertEquals('2', map['current'])
+ self.assertEquals('8', map['numtests'])
+ self.assertEquals('InstrumentationTestRunner', map['id'])
+ self.assertEquals(0, result.GetStatusCode())
+
+ def testParseAmInstOutput(self):
+ result = """INSTRUMENTATION_STATUS: class=LaunchPerformanceTestCase
+INSTRUMENTATION_STATUS: current=1
+INSTRUMENTATION_STATUS: id=InstrumentationTestRunner
+INSTRUMENTATION_STATUS: numtests=2
+INSTRUMENTATION_STATUS: stream=
+LaunchPerformanceTestCase:
+INSTRUMENTATION_STATUS: test=testLaunchComplexActivity
+INSTRUMENTATION_STATUS_CODE: 1
+INSTRUMENTATION_STATUS: class=LaunchPerformanceTestCase
+INSTRUMENTATION_STATUS: current=1
+INSTRUMENTATION_STATUS: id=InstrumentationTestRunner
+INSTRUMENTATION_STATUS: numtests=2
+INSTRUMENTATION_STATUS: performance.cpu_time=866
+INSTRUMENTATION_STATUS: performance.execution_time=1242
+INSTRUMENTATION_STATUS: stream=.
+INSTRUMENTATION_STATUS: test=testLaunchComplexActivity
+INSTRUMENTATION_STATUS_CODE: 0
+INSTRUMENTATION_STATUS: class=LaunchPerformanceTestCase
+INSTRUMENTATION_STATUS: current=2
+INSTRUMENTATION_STATUS: id=InstrumentationTestRunner
+INSTRUMENTATION_STATUS: numtests=2
+INSTRUMENTATION_STATUS: stream=
+INSTRUMENTATION_STATUS: test=testLaunchSimpleActivity
+INSTRUMENTATION_STATUS_CODE: 1
+INSTRUMENTATION_STATUS: class=LaunchPerformanceTestCase
+INSTRUMENTATION_STATUS: current=2
+INSTRUMENTATION_STATUS: id=InstrumentationTestRunner
+INSTRUMENTATION_STATUS: numtests=2
+INSTRUMENTATION_STATUS: performance.cpu_time=590
+INSTRUMENTATION_STATUS: performance.execution_time=1122
+INSTRUMENTATION_STATUS: stream=.
+INSTRUMENTATION_STATUS: test=testLaunchSimpleActivity
+INSTRUMENTATION_STATUS_CODE: 0
+INSTRUMENTATION_RESULT: performance.cpu_time=829
+INSTRUMENTATION_RESULT: performance.execution_time=1708
+INSTRUMENTATION_RESULT: performance.gc_invocation_count=0
+INSTRUMENTATION_RESULT: performance.global_alloc_count=2848
+INSTRUMENTATION_RESULT: performance.global_alloc_size=193079
+INSTRUMENTATION_RESULT: performance.global_freed_count=1207
+INSTRUMENTATION_RESULT: performance.global_freed_size=93040
+INSTRUMENTATION_RESULT: performance.java_allocated=2175
+INSTRUMENTATION_RESULT: performance.java_free=580
+INSTRUMENTATION_RESULT: performance.java_private_dirty=740
+INSTRUMENTATION_RESULT: performance.java_pss=1609
+INSTRUMENTATION_RESULT: performance.java_shared_dirty=3860
+INSTRUMENTATION_RESULT: performance.java_size=2755
+INSTRUMENTATION_RESULT: performance.native_allocated=2585
+INSTRUMENTATION_RESULT: performance.native_free=34
+INSTRUMENTATION_RESULT: performance.native_private_dirty=632
+INSTRUMENTATION_RESULT: performance.native_pss=701
+INSTRUMENTATION_RESULT: performance.native_shared_dirty=1164
+INSTRUMENTATION_RESULT: performance.native_size=2620
+INSTRUMENTATION_RESULT: performance.other_private_dirty=896
+INSTRUMENTATION_RESULT: performance.other_pss=1226
+INSTRUMENTATION_RESULT: performance.other_shared_dirty=804
+INSTRUMENTATION_RESULT: performance.pre_received_transactions=-1
+INSTRUMENTATION_RESULT: performance.pre_sent_transactions=-1
+INSTRUMENTATION_RESULT: performance.received_transactions=-1
+INSTRUMENTATION_RESULT: performance.sent_transactions=-1
+INSTRUMENTATION_RESULT: stream=
+Test results for InstrumentationTestRunner=..
+Time: 2.413
+
+OK (2 tests)
+
+
+INSTRUMENTATION_CODE: -1
+"""
+ (results_list, perf_dict) = \
+ am_instrument_parser.ParseAmInstrumentOutput(result)
+ self.assertEquals(829, perf_dict['cpu_time'])
+ self.assertEquals(2848, perf_dict['global_alloc_count'])
+ self.assertEquals(93040, perf_dict['global_freed_size'])
+ self.assertEquals(740, perf_dict['java_private_dirty'])
+ self.assertEquals(2755, perf_dict['java_size'])
+ self.assertEquals(632, perf_dict['native_private_dirty'])
+ self.assertEquals(2620, perf_dict['native_size'])
+ self.assertEquals(804, perf_dict['other_shared_dirty'])
+ self.assertEquals(-1, perf_dict['received_transactions'])
+ self.assertTrue(len(perf_dict['stream']) > 50)
+ self.assertEquals('-1', perf_dict['code'])
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java b/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java
index ef74fe7..47b8f48 100644
--- a/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java
+++ b/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java
@@ -17,6 +17,7 @@
package com.android.ant;
import com.android.sdklib.internal.project.ApkConfigurationHelper;
+import com.android.sdklib.internal.project.ApkSettings;
import com.android.sdklib.internal.project.ProjectProperties;
import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
@@ -28,7 +29,6 @@
import java.io.File;
import java.util.Map;
-import java.util.Set;
import java.util.Map.Entry;
/**
@@ -38,7 +38,7 @@
*
*/
public final class AaptExecLoopTask extends Task {
-
+
private String mExecutable;
private String mCommand;
private String mManifest;
@@ -55,7 +55,7 @@
public void setExecutable(String executable) {
mExecutable = executable;
}
-
+
/**
* Sets the value of the "command" attribute.
* @param command the value.
@@ -63,7 +63,7 @@
public void setCommand(String command) {
mCommand = command;
}
-
+
/**
* Sets the value of the "manifest" attribute.
* @param manifest the value.
@@ -71,7 +71,7 @@
public void setManifest(Path manifest) {
mManifest = manifest.toString();
}
-
+
/**
* Sets the value of the "resources" attribute.
* @param resources the value.
@@ -79,7 +79,7 @@
public void setResources(Path resources) {
mResources = resources.toString();
}
-
+
/**
* Sets the value of the "assets" attribute.
* @param assets the value.
@@ -87,7 +87,7 @@
public void setAssets(Path assets) {
mAssets = assets.toString();
}
-
+
/**
* Sets the value of the "androidjar" attribute.
* @param androidJar the value.
@@ -95,7 +95,7 @@
public void setAndroidjar(Path androidJar) {
mAndroidJar = androidJar.toString();
}
-
+
/**
* Sets the value of the "outfolder" attribute.
* @param outFolder the value.
@@ -103,7 +103,7 @@
public void setOutfolder(Path outFolder) {
mOutFolder = outFolder.toString();
}
-
+
/**
* Sets the value of the "basename" attribute.
* @param baseName the value.
@@ -111,19 +111,19 @@
public void setBasename(String baseName) {
mBaseName = baseName;
}
-
+
/*
* (non-Javadoc)
- *
+ *
* Executes the loop. Based on the values inside default.properties, this will
* create alternate temporary ap_ files.
- *
+ *
* @see org.apache.tools.ant.Task#execute()
*/
@Override
public void execute() throws BuildException {
Project taskProject = getProject();
-
+
// first do a full resource package
createPackage(null /*configName*/, null /*resourceFilter*/);
@@ -132,12 +132,15 @@
File baseDir = taskProject.getBaseDir();
ProjectProperties properties = ProjectProperties.load(baseDir.getAbsolutePath(),
PropertyType.DEFAULT);
-
- Map<String, String> apkConfigs = ApkConfigurationHelper.getConfigs(properties);
- if (apkConfigs.size() > 0) {
- Set<Entry<String, String>> entrySet = apkConfigs.entrySet();
- for (Entry<String, String> entry : entrySet) {
- createPackage(entry.getKey(), entry.getValue());
+
+
+ ApkSettings apkSettings = ApkConfigurationHelper.getSettings(properties);
+ if (apkSettings != null) {
+ Map<String, String> apkFilters = apkSettings.getResourceFilters();
+ if (apkFilters.size() > 0) {
+ for (Entry<String, String> entry : apkFilters.entrySet()) {
+ createPackage(entry.getKey(), entry.getValue());
+ }
}
}
}
@@ -164,19 +167,19 @@
ExecTask task = new ExecTask();
task.setExecutable(mExecutable);
task.setFailonerror(true);
-
+
// aapt command. Only "package" is supported at this time really.
task.createArg().setValue(mCommand);
-
+
// filters if needed
if (configName != null && resourceFilter != null) {
task.createArg().setValue("-c");
task.createArg().setValue(resourceFilter);
}
-
+
// force flag
task.createArg().setValue("-f");
-
+
// manifest location
task.createArg().setValue("-M");
task.createArg().setValue(mManifest);
@@ -187,18 +190,18 @@
task.createArg().setValue("-S");
task.createArg().setValue(mResources);
}
-
+
// assets location. This may not exists, and aapt doesn't like it, so we check first.
File assets = new File(mAssets);
if (assets.isDirectory()) {
task.createArg().setValue("-A");
task.createArg().setValue(mAssets);
}
-
+
// android.jar
task.createArg().setValue("-I");
task.createArg().setValue(mAndroidJar);
-
+
// out file. This is based on the outFolder, baseName, and the configName (if applicable)
String filename;
if (configName != null && resourceFilter != null) {
@@ -206,15 +209,15 @@
} else {
filename = mBaseName + ".ap_";
}
-
+
File file = new File(mOutFolder, filename);
task.createArg().setValue("-F");
task.createArg().setValue(file.getAbsolutePath());
-
+
// final setup of the task
task.setProject(taskProject);
task.setOwningTarget(getOwningTarget());
-
+
// execute it.
task.execute();
}
diff --git a/tools/anttasks/src/com/android/ant/ApkBuilderTask.java b/tools/anttasks/src/com/android/ant/ApkBuilderTask.java
index 9231dc9..e062fed 100644
--- a/tools/anttasks/src/com/android/ant/ApkBuilderTask.java
+++ b/tools/anttasks/src/com/android/ant/ApkBuilderTask.java
@@ -20,6 +20,7 @@
import com.android.apkbuilder.internal.ApkBuilderImpl;
import com.android.apkbuilder.internal.ApkBuilderImpl.ApkFile;
import com.android.sdklib.internal.project.ApkConfigurationHelper;
+import com.android.sdklib.internal.project.ApkSettings;
import com.android.sdklib.internal.project.ProjectProperties;
import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
@@ -35,7 +36,6 @@
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Map;
-import java.util.Set;
import java.util.Map.Entry;
public class ApkBuilderTask extends Task {
@@ -232,12 +232,14 @@
ProjectProperties properties = ProjectProperties.load(baseDir.getAbsolutePath(),
PropertyType.DEFAULT);
- Map<String, String> apkConfigs = ApkConfigurationHelper.getConfigs(properties);
- if (apkConfigs.size() > 0) {
- Set<Entry<String, String>> entrySet = apkConfigs.entrySet();
- for (Entry<String, String> entry : entrySet) {
- createApk(apkBuilder, entry.getKey(), entry.getValue(), path,
- debugPackageSuffix);
+ ApkSettings apkSettings = ApkConfigurationHelper.getSettings(properties);
+ if (apkSettings != null) {
+ Map<String, String> apkFilters = apkSettings.getResourceFilters();
+ if (apkFilters.size() > 0) {
+ for (Entry<String, String> entry : apkFilters.entrySet()) {
+ createApk(apkBuilder, entry.getKey(), entry.getValue(), path,
+ debugPackageSuffix);
+ }
}
}
diff --git a/tools/anttasks/src/com/android/ant/SetupTask.java b/tools/anttasks/src/com/android/ant/SetupTask.java
index 04afba7..ffe8314 100644
--- a/tools/anttasks/src/com/android/ant/SetupTask.java
+++ b/tools/anttasks/src/com/android/ant/SetupTask.java
@@ -60,9 +60,13 @@
private final static String ANDROID_RULES = "android_rules.xml";
// ant property with the path to the android.jar
- private final static String PROPERTY_ANDROID_JAR = "android-jar";
+ private final static String PROPERTY_ANDROID_JAR = "android.jar";
+ // LEGACY - compatibility with 1.6 and before
+ private final static String PROPERTY_ANDROID_JAR_LEGACY = "android-jar";
// ant property with the path to the framework.jar
- private final static String PROPERTY_ANDROID_AIDL = "android-aidl";
+ private final static String PROPERTY_ANDROID_AIDL = "android.aidl";
+ // LEGACY - compatibility with 1.6 and before
+ private final static String PROPERTY_ANDROID_AIDL_LEGACY = "android-aidl";
// ant property with the path to the aapt tool
private final static String PROPERTY_AAPT = "aapt";
// ant property with the path to the aidl tool
@@ -83,7 +87,13 @@
// check if it's valid and exists
if (sdkLocation == null || sdkLocation.length() == 0) {
- throw new BuildException("SDK Location is not set.");
+ // LEGACY support: project created with 1.6 or before may be using a different
+ // property to declare the location of the SDK. At this point, we cannot
+ // yet check which target is running so we check both always.
+ sdkLocation = antProject.getProperty(ProjectProperties.PROPERTY_SDK_LEGACY);
+ if (sdkLocation == null || sdkLocation.length() == 0) {
+ throw new BuildException("SDK Location is not set.");
+ }
}
File sdk = new File(sdkLocation);
@@ -152,8 +162,9 @@
String androidJar = androidTarget.getPath(IAndroidTarget.ANDROID_JAR);
antProject.setProperty(PROPERTY_ANDROID_JAR, androidJar);
- antProject.setProperty(PROPERTY_ANDROID_AIDL,
- androidTarget.getPath(IAndroidTarget.ANDROID_AIDL));
+ String androidAidl = androidTarget.getPath(IAndroidTarget.ANDROID_AIDL);
+ antProject.setProperty(PROPERTY_ANDROID_AIDL, androidAidl);
+
antProject.setProperty(PROPERTY_AAPT, androidTarget.getPath(IAndroidTarget.AAPT));
antProject.setProperty(PROPERTY_AIDL, androidTarget.getPath(IAndroidTarget.AIDL));
antProject.setProperty(PROPERTY_DX, androidTarget.getPath(IAndroidTarget.DX));
@@ -188,6 +199,18 @@
// find the file to import, and import it.
String templateFolder = androidTarget.getPath(IAndroidTarget.TEMPLATES);
+ // LEGACY support. android_rules.xml in older platforms expects properties with
+ // older names. This sets those properties to make sure the rules will work.
+ if (androidTarget.getVersion().getApiLevel() <= 4) { // 1.6 and earlier
+ antProject.setProperty(PROPERTY_ANDROID_JAR_LEGACY, androidJar);
+ antProject.setProperty(PROPERTY_ANDROID_AIDL_LEGACY, androidAidl);
+ antProject.setProperty(ProjectProperties.PROPERTY_SDK_LEGACY, sdkLocation);
+ String appPackage = antProject.getProperty(ProjectProperties.PROPERTY_APP_PACKAGE);
+ if (appPackage != null && appPackage.length() > 0) {
+ antProject.setProperty(ProjectProperties.PROPERTY_APP_PACKAGE_LEGACY, appPackage);
+ }
+ }
+
// Now the import section. This is only executed if the task actually has to import a file.
if (mDoImport) {
// make sure the file exists.
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java
index e8e8103..0789655 100644
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java
@@ -73,11 +73,14 @@
/**
* Send a MPRS (Method PRofiling Start) request to the client.
*
+ * The arguments to this method will eventually be passed to
+ * android.os.Debug.startMethodTracing() on the device.
+ *
* @param fileName is the name of the file to which profiling data
* will be written (on the device); it will have ".trace"
* appended if necessary
* @param bufferSize is the desired buffer size in bytes (8MB is good)
- * @param flags should be zero
+ * @param flags see startMethodTracing() docs; use 0 for default behavior
*/
public static void sendMPRS(Client client, String fileName, int bufferSize,
int flags) throws IOException {
diff --git a/tools/eclipse/changes.txt b/tools/eclipse/changes.txt
index cd2736a..5805681 100644
--- a/tools/eclipse/changes.txt
+++ b/tools/eclipse/changes.txt
@@ -1,7 +1,8 @@
-0.9.2:
+0.9.3:
- New wizard to create Android JUnit Test Projects.
- New AVD wizard.
- SDK Updater
+- zipalign support
0.9.1:
- Added an AVD creation wizard to ADT. It is automatically displayed during a launch if no compatible AVDs are found.
diff --git a/tools/eclipse/features/com.android.ide.eclipse.adt/feature.xml b/tools/eclipse/features/com.android.ide.eclipse.adt/feature.xml
index 6dd3ee7..e4cc9f2 100644
--- a/tools/eclipse/features/com.android.ide.eclipse.adt/feature.xml
+++ b/tools/eclipse/features/com.android.ide.eclipse.adt/feature.xml
@@ -2,7 +2,7 @@
<feature
id="com.android.ide.eclipse.adt"
label="Android Development Tools"
- version="0.9.3.qualifier"
+ version="0.9.4.qualifier"
provider-name="The Android Open Source Project"
plugin="com.android.ide.eclipse.adt">
diff --git a/tools/eclipse/features/com.android.ide.eclipse.ddms/feature.xml b/tools/eclipse/features/com.android.ide.eclipse.ddms/feature.xml
index e67f960..c0cb12d 100644
--- a/tools/eclipse/features/com.android.ide.eclipse.ddms/feature.xml
+++ b/tools/eclipse/features/com.android.ide.eclipse.ddms/feature.xml
@@ -2,7 +2,7 @@
<feature
id="com.android.ide.eclipse.ddms"
label="Android DDMS"
- version="0.9.3.qualifier"
+ version="0.9.4.qualifier"
provider-name="The Android Open Source Project">
<description>
diff --git a/tools/eclipse/features/com.android.ide.eclipse.tests/feature.xml b/tools/eclipse/features/com.android.ide.eclipse.tests/feature.xml
index cc34045..679da31 100644
--- a/tools/eclipse/features/com.android.ide.eclipse.tests/feature.xml
+++ b/tools/eclipse/features/com.android.ide.eclipse.tests/feature.xml
@@ -2,7 +2,7 @@
<feature
id="com.android.ide.eclipse.tests"
label="ADT Tests"
- version="0.9.3.qualifier"
+ version="0.9.4.qualifier"
provider-name="The Android Open Source Project">
<copyright>
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/.gitignore b/tools/eclipse/plugins/com.android.ide.eclipse.adt/.gitignore
new file mode 100644
index 0000000..d392f0e
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/.gitignore
@@ -0,0 +1 @@
+*.jar
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
index 32c939b..0c78390 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
Bundle-ManifestVersion: 2
Bundle-Name: Android Development Toolkit
Bundle-SymbolicName: com.android.ide.eclipse.adt;singleton:=true
-Bundle-Version: 0.9.3.qualifier
+Bundle-Version: 0.9.4.qualifier
Bundle-ClassPath: .,
jarutils.jar,
androidprefs.jar,
@@ -54,6 +54,7 @@
com.android.ide.eclipse.adt.internal.editors;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.editors.descriptors;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.editors.layout;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.eclipse.adt.internal.editors.layout.configuration,
com.android.ide.eclipse.adt.internal.editors.layout.descriptors;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.editors.layout.parts;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.editors.layout.uimodel;x-friends:="com.android.ide.eclipse.tests",
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java
index b49ee6e..1ed5123 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java
@@ -34,6 +34,7 @@
import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
+import com.android.sdklib.internal.project.ApkSettings;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
@@ -303,11 +304,15 @@
return referencedProjects;
}
- // get the extra configs for the project.
- // The map contains (name, filter) where 'name' is a name to be used in the apk filename,
- // and filter is the resource filter to be used in the aapt -c parameters to restrict
- // which resource configurations to package in the apk.
- Map<String, String> configs = Sdk.getCurrent().getProjectApkConfigs(project);
+ // get the APK configs for the project.
+ ApkSettings apkSettings = Sdk.getCurrent().getApkSettings(project);
+ Set<Entry<String, String>> apkfilters = null;
+ if (apkSettings != null) {
+ Map<String, String> filterMap = apkSettings.getResourceFilters();
+ if (filterMap != null && filterMap.size() > 0) {
+ apkfilters = filterMap.entrySet();
+ }
+ }
// do some extra check, in case the output files are not present. This
// will force to recreate them.
@@ -320,19 +325,20 @@
mPackageResources = true;
mBuildFinalPackage = true;
} else {
- // if the full package is present, we check the filtered resource packages as well
- if (configs != null) {
- Set<Entry<String, String>> entrySet = configs.entrySet();
-
- for (Entry<String, String> entry : entrySet) {
+ // if the full package is present, we check the filtered resource packages
+ // as well
+ if (apkfilters != null) {
+ for (Entry<String, String> entry : apkfilters) {
String filename = String.format(AndroidConstants.FN_RESOURCES_S_AP_,
entry.getKey());
tmp = outputFolder.findMember(filename);
if (tmp == null || (tmp instanceof IFile &&
tmp.exists() == false)) {
- String msg = String.format(Messages.s_Missing_Repackaging, filename);
- AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
+ String msg = String.format(Messages.s_Missing_Repackaging,
+ filename);
+ AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE,
+ project, msg);
mPackageResources = true;
mBuildFinalPackage = true;
break;
@@ -360,11 +366,9 @@
String msg = String.format(Messages.s_Missing_Repackaging, finalPackageName);
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
mBuildFinalPackage = true;
- } else if (configs != null) {
+ } else if (apkfilters != null) {
// if the full apk is present, we check the filtered apk as well
- Set<Entry<String, String>> entrySet = configs.entrySet();
-
- for (Entry<String, String> entry : entrySet) {
+ for (Entry<String, String> entry : apkfilters) {
String filename = ProjectHelper.getApkFilename(project, entry.getKey());
tmp = outputFolder.findMember(filename);
@@ -411,9 +415,8 @@
// notified.
finalPackage.delete();
- if (configs != null) {
- Set<Entry<String, String>> entrySet = configs.entrySet();
- for (Entry<String, String> entry : entrySet) {
+ if (apkfilters != null) {
+ for (Entry<String, String> entry : apkfilters) {
String packageFilepath = osBinPath + File.separator +
ProjectHelper.getApkFilename(project, entry.getKey());
@@ -477,9 +480,8 @@
}
// now do the same thing for all the configured resource packages.
- if (configs != null) {
- Set<Entry<String, String>> entrySet = configs.entrySet();
- for (Entry<String, String> entry : entrySet) {
+ if (apkfilters != null) {
+ for (Entry<String, String> entry : apkfilters) {
String outPathFormat = osBinPath + File.separator +
AndroidConstants.FN_RESOURCES_S_AP_;
String outPath = String.format(outPathFormat, entry.getKey());
@@ -527,12 +529,11 @@
}
// now do the same thing for all the configured resource packages.
- if (configs != null) {
+ if (apkfilters != null) {
String resPathFormat = osBinPath + File.separator +
AndroidConstants.FN_RESOURCES_S_AP_;
- Set<Entry<String, String>> entrySet = configs.entrySet();
- for (Entry<String, String> entry : entrySet) {
+ for (Entry<String, String> entry : apkfilters) {
// make the filename for the resource package.
String resPath = String.format(resPathFormat, entry.getKey());
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GLE2.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GLE2.java
new file mode 100755
index 0000000..e905990
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GLE2.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.ide.eclipse.adt.internal.editors.layout;
+
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.gef.ui.parts.SelectionSynchronizer;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.part.EditorPart;
+
+/**
+ * Graphical layout editor part, version 2.
+ *
+ * @since GLE2
+ */
+public class GLE2 extends EditorPart implements IGraphicalLayoutEditor {
+
+ /*
+ * Useful notes:
+ * To understand Drag'n'drop:
+ * http://www.eclipse.org/articles/Article-Workbench-DND/drag_drop.html
+ */
+
+ /** Reference to the layout editor */
+ private final LayoutEditor mLayoutEditor;
+
+ public GLE2(LayoutEditor layoutEditor) {
+ mLayoutEditor = layoutEditor;
+ setPartName("Graphical Layout");
+ }
+
+ // ------------------------------------
+ // Methods overridden from base classes
+ //------------------------------------
+
+ /**
+ * Initializes the editor part with a site and input.
+ * {@inheritDoc}
+ */
+ @Override
+ public void init(IEditorSite site, IEditorInput input) throws PartInitException {
+ setSite(site);
+ setInput(input);
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ }
+
+ @Override
+ public void doSave(IProgressMonitor monitor) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void doSaveAs() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public boolean isDirty() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isSaveAsAllowed() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public void createPartControl(Composite parent) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void setFocus() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void activated() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void deactivated() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void editNewFile(FolderConfiguration configuration) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Clipboard getClipboard() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public LayoutEditor getLayoutEditor() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public UiDocumentNode getModel() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public SelectionSynchronizer getSelectionSynchronizer() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void onXmlModelChanged() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void recomputeLayout() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void reloadEditor() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void reloadPalette() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void selectModel(UiElementNode uiNodeModel) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void reloadLayout(boolean codeChange, boolean rChange,
+ boolean resChange) {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalLayoutEditor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalLayoutEditor.java
index 225d5bd..45810af 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalLayoutEditor.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalLayoutEditor.java
@@ -19,7 +19,8 @@
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor.UiEditorActions;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutReloadMonitor.ILayoutReloadListener;
+import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite;
+import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite.IConfigListener;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.parts.ElementCreateCommand;
import com.android.ide.eclipse.adt.internal.editors.layout.parts.UiElementEditPart;
@@ -28,25 +29,9 @@
import com.android.ide.eclipse.adt.internal.editors.ui.tree.PasteAction;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
-import com.android.ide.eclipse.adt.internal.resources.ResourceType;
-import com.android.ide.eclipse.adt.internal.resources.configurations.CountryCodeQualifier;
import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.KeyboardStateQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.LanguageQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationMethodQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.NetworkCodeQualifier;
import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.RegionQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenDimensionQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.TextInputMethodQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.TouchScreenQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.KeyboardStateQualifier.KeyboardState;
-import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationMethodQualifier.NavigationMethod;
import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier.Density;
-import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier.ScreenOrientation;
-import com.android.ide.eclipse.adt.internal.resources.configurations.TextInputMethodQualifier.TextInputMethod;
-import com.android.ide.eclipse.adt.internal.resources.configurations.TouchScreenQualifier.TouchScreenType;
import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
@@ -56,14 +41,11 @@
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData.LayoutBridge;
import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener;
-import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.DimensionVerifier;
-import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.LanguageRegionVerifier;
-import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.MobileCodeVerifier;
+import com.android.layoutlib.api.ILayoutBridge;
import com.android.layoutlib.api.ILayoutLog;
import com.android.layoutlib.api.ILayoutResult;
import com.android.layoutlib.api.IProjectCallback;
import com.android.layoutlib.api.IResourceValue;
-import com.android.layoutlib.api.IStyleResourceValue;
import com.android.layoutlib.api.IXmlPullParser;
import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo;
import com.android.sdklib.IAndroidTarget;
@@ -88,6 +70,8 @@
import org.eclipse.gef.editparts.ScalableFreeformRootEditPart;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gef.requests.CreationFactory;
+import org.eclipse.gef.ui.parts.GraphicalEditorWithPalette;
+import org.eclipse.gef.ui.parts.SelectionSynchronizer;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
@@ -97,22 +81,12 @@
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.ide.IDE;
@@ -127,12 +101,9 @@
import java.io.InputStream;
import java.io.PrintStream;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* Graphical layout editor, based on GEF.
@@ -140,11 +111,12 @@
* To understand GEF: http://www.ibm.com/developerworks/opensource/library/os-gef/
* <p/>
* To understand Drag'n'drop: http://www.eclipse.org/articles/Article-Workbench-DND/drag_drop.html
+ *
+ * @since GLE1
*/
-public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
- implements ILayoutReloadListener {
+public class GraphicalLayoutEditor extends GraphicalEditorWithPalette
+ implements IGraphicalLayoutEditor, IConfigListener {
- private final static String THEME_SEPARATOR = "----------"; //$NON-NLS-1$
/** Reference to the layout editor */
private final LayoutEditor mLayoutEditor;
@@ -154,43 +126,10 @@
private Clipboard mClipboard;
private Composite mParent;
+ private ConfigurationComposite mConfigComposite;
+
private PaletteRoot mPaletteRoot;
- private Text mCountry;
- private Text mNetwork;
- private Combo mLanguage;
- private Combo mRegion;
- private Combo mOrientation;
- private Combo mDensity;
- private Combo mTouch;
- private Combo mKeyboard;
- private Combo mTextInput;
- private Combo mNavigation;
- private Text mSize1;
- private Text mSize2;
- private Combo mThemeCombo;
- private Button mCreateButton;
-
- private Label mCountryIcon;
- private Label mNetworkIcon;
- private Label mLanguageIcon;
- private Label mRegionIcon;
- private Label mOrientationIcon;
- private Label mDensityIcon;
- private Label mTouchIcon;
- private Label mKeyboardIcon;
- private Label mTextInputIcon;
- private Label mNavigationIcon;
- private Label mSizeIcon;
-
- private Label mCurrentLayoutLabel;
-
- private Image mWarningImage;
- private Image mMatchImage;
- private Image mErrorImage;
-
- /** The {@link FolderConfiguration} representing the state of the UI controls */
- private FolderConfiguration mCurrentConfig = new FolderConfiguration();
/** The {@link FolderConfiguration} being edited. */
private FolderConfiguration mEditedConfig;
@@ -201,8 +140,6 @@
private boolean mNeedsXmlReload = false;
private boolean mNeedsRecompute = false;
- private int mPlatformThemeCount = 0;
- private boolean mDisableUpdates = false;
/** Listener to update the root node if the target of the file is changed because of a
* SDK location change or a project target change */
@@ -217,9 +154,7 @@
// because the SDK changed we must reset the configured framework resource.
mConfiguredFrameworkRes = null;
- updateUIFromResources();
-
- mThemeCombo.getParent().layout();
+ mConfigComposite.updateUIFromResources();
// updateUiFromFramework will reset language/region combo, so we must call
// setConfiguration after, or the settext on language/region will be lost.
@@ -247,21 +182,16 @@
private final Runnable mUiUpdateFromResourcesRunnable = new Runnable() {
public void run() {
- updateUIFromResources();
- mThemeCombo.getParent().layout();
+ mConfigComposite.updateUIFromResources();
}
};
+
public GraphicalLayoutEditor(LayoutEditor layoutEditor) {
mLayoutEditor = layoutEditor;
setEditDomain(new DefaultEditDomain(this));
setPartName("Layout");
- IconFactory factory = IconFactory.getInstance();
- mWarningImage = factory.getIcon("warning"); //$NON-NLS-1$
- mMatchImage = factory.getIcon("match"); //$NON-NLS-1$
- mErrorImage = factory.getIcon("error"); //$NON-NLS-1$
-
AdtPlugin.getDefault().addTargetListener(mTargetListener);
}
@@ -273,7 +203,6 @@
public void createPartControl(Composite parent) {
mParent = parent;
GridLayout gl;
- GridData gd;
mClipboard = new Clipboard(parent.getDisplay());
@@ -281,286 +210,7 @@
gl.marginHeight = gl.marginWidth = 0;
// create the top part for the configuration control
- int cols = 10;
-
- Composite topParent = new Composite(parent, SWT.NONE);
- topParent.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- topParent.setLayout(gl = new GridLayout(cols, false));
-
- new Label(topParent, SWT.NONE).setText("MCC");
- mCountryIcon = createControlComposite(topParent, true /* grab_horizontal */);
- mCountry = new Text(mCountryIcon.getParent(), SWT.BORDER);
- mCountry.setLayoutData(new GridData(
- GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
- mCountry.addVerifyListener(new MobileCodeVerifier());
- mCountry.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetDefaultSelected(SelectionEvent e) {
- onCountryCodeChange();
- }
- });
- mCountry.addModifyListener(new ModifyListener() {
- public void modifyText(ModifyEvent e) {
- onCountryCodeChange();
- }
- });
-
- new Label(topParent, SWT.NONE).setText("MNC");
- mNetworkIcon = createControlComposite(topParent, true /* grab_horizontal */);
- mNetwork = new Text(mNetworkIcon.getParent(), SWT.BORDER);
- mNetwork.setLayoutData(new GridData(
- GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
- mNetwork.addVerifyListener(new MobileCodeVerifier());
- mNetwork.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetDefaultSelected(SelectionEvent e) {
- onNetworkCodeChange();
- }
- });
- mNetwork.addModifyListener(new ModifyListener() {
- public void modifyText(ModifyEvent e) {
- onNetworkCodeChange();
- }
- });
-
- new Label(topParent, SWT.NONE).setText("Lang");
- mLanguageIcon = createControlComposite(topParent, true /* grab_horizontal */);
- mLanguage = new Combo(mLanguageIcon.getParent(), SWT.DROP_DOWN);
- mLanguage.setLayoutData(new GridData(
- GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
- mLanguage.addVerifyListener(new LanguageRegionVerifier());
- mLanguage.addSelectionListener(new SelectionListener() {
- public void widgetDefaultSelected(SelectionEvent e) {
- onLanguageChange();
- }
- public void widgetSelected(SelectionEvent e) {
- onLanguageChange();
- }
- });
- mLanguage.addModifyListener(new ModifyListener() {
- public void modifyText(ModifyEvent e) {
- onLanguageChange();
- }
- });
-
- new Label(topParent, SWT.NONE).setText("Region");
- mRegionIcon = createControlComposite(topParent, true /* grab_horizontal */);
- mRegion = new Combo(mRegionIcon.getParent(), SWT.DROP_DOWN);
- mRegion.setLayoutData(new GridData(
- GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
- mRegion.addVerifyListener(new LanguageRegionVerifier());
- mRegion.addSelectionListener(new SelectionListener() {
- public void widgetDefaultSelected(SelectionEvent e) {
- onRegionChange();
- }
- public void widgetSelected(SelectionEvent e) {
- onRegionChange();
- }
- });
- mRegion.addModifyListener(new ModifyListener() {
- public void modifyText(ModifyEvent e) {
- onRegionChange();
- }
- });
-
- new Label(topParent, SWT.NONE).setText("Orient");
- mOrientationIcon = createControlComposite(topParent, true /* grab_horizontal */);
- mOrientation = new Combo(mOrientationIcon.getParent(), SWT.DROP_DOWN | SWT.READ_ONLY);
- ScreenOrientation[] soValues = ScreenOrientation.values();
- mOrientation.add("(Default)");
- for (ScreenOrientation value : soValues) {
- mOrientation.add(value.getDisplayValue());
- }
- mOrientation.select(0);
- mOrientation.setLayoutData(new GridData(
- GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
- mOrientation.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onOrientationChange();
- }
- });
-
- new Label(topParent, SWT.NONE).setText("Density");
- mDensityIcon = createControlComposite(topParent, true /* grab_horizontal */);
- mDensity = new Combo(mDensityIcon.getParent(), SWT.DROP_DOWN | SWT.READ_ONLY);
- Density[] dValues = Density.values();
- mDensity.add("(Default)");
- for (Density value : dValues) {
- mDensity.add(value.getDisplayValue());
- }
- mDensity.select(0);
- mDensity.setLayoutData(new GridData(
- GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
- mDensity.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onDensityChange();
- }
- });
-
- new Label(topParent, SWT.NONE).setText("Touch");
- mTouchIcon = createControlComposite(topParent, true /* grab_horizontal */);
- mTouch = new Combo(mTouchIcon.getParent(), SWT.DROP_DOWN | SWT.READ_ONLY);
- TouchScreenType[] tstValues = TouchScreenType.values();
- mTouch.add("(Default)");
- for (TouchScreenType value : tstValues) {
- mTouch.add(value.getDisplayValue());
- }
- mTouch.select(0);
- mTouch.setLayoutData(new GridData(
- GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
- mTouch.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onTouchChange();
- }
- });
-
- new Label(topParent, SWT.NONE).setText("Keybrd");
- mKeyboardIcon = createControlComposite(topParent, true /* grab_horizontal */);
- mKeyboard = new Combo(mKeyboardIcon.getParent(), SWT.DROP_DOWN | SWT.READ_ONLY);
- KeyboardState[] ksValues = KeyboardState.values();
- mKeyboard.add("(Default)");
- for (KeyboardState value : ksValues) {
- mKeyboard.add(value.getDisplayValue());
- }
- mKeyboard.select(0);
- mKeyboard.setLayoutData(new GridData(
- GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
- mKeyboard.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onKeyboardChange();
- }
- });
-
- new Label(topParent, SWT.NONE).setText("Input");
- mTextInputIcon = createControlComposite(topParent, true /* grab_horizontal */);
- mTextInput = new Combo(mTextInputIcon.getParent(), SWT.DROP_DOWN | SWT.READ_ONLY);
- TextInputMethod[] timValues = TextInputMethod.values();
- mTextInput.add("(Default)");
- for (TextInputMethod value : timValues) {
- mTextInput.add(value.getDisplayValue());
- }
- mTextInput.select(0);
- mTextInput.setLayoutData(new GridData(
- GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
- mTextInput.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onTextInputChange();
- }
- });
-
- new Label(topParent, SWT.NONE).setText("Nav");
- mNavigationIcon = createControlComposite(topParent, true /* grab_horizontal */);
- mNavigation = new Combo(mNavigationIcon.getParent(), SWT.DROP_DOWN | SWT.READ_ONLY);
- NavigationMethod[] nValues = NavigationMethod.values();
- mNavigation.add("(Default)");
- for (NavigationMethod value : nValues) {
- mNavigation.add(value.getDisplayValue());
- }
- mNavigation.select(0);
- mNavigation.setLayoutData(new GridData(
- GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
- mNavigation.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onNavigationChange();
- }
- });
-
- Composite labelParent = new Composite(topParent, SWT.NONE);
- labelParent.setLayout(gl = new GridLayout(8, false));
- gl.marginWidth = gl.marginHeight = 0;
- labelParent.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
- gd.horizontalSpan = cols;
-
- new Label(labelParent, SWT.NONE).setText("Editing config:");
- mCurrentLayoutLabel = new Label(labelParent, SWT.NONE);
- mCurrentLayoutLabel.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
- gd.widthHint = 50;
-
- new Label(labelParent, SWT.NONE).setText("Size");
- mSizeIcon = createControlComposite(labelParent, false);
- Composite sizeParent = new Composite(mSizeIcon.getParent(), SWT.NONE);
- sizeParent.setLayout(gl = new GridLayout(3, false));
- gl.marginWidth = gl.marginHeight = 0;
- gl.horizontalSpacing = 0;
-
- mSize1 = new Text(sizeParent, SWT.BORDER);
- mSize1.setLayoutData(gd = new GridData());
- gd.widthHint = 30;
- new Label(sizeParent, SWT.NONE).setText("x");
- mSize2 = new Text(sizeParent, SWT.BORDER);
- mSize2.setLayoutData(gd = new GridData());
- gd.widthHint = 30;
-
- DimensionVerifier verifier = new DimensionVerifier();
- mSize1.addVerifyListener(verifier);
- mSize2.addVerifyListener(verifier);
-
- SelectionListener sl = new SelectionListener() {
- public void widgetDefaultSelected(SelectionEvent e) {
- onSizeChange();
- }
- public void widgetSelected(SelectionEvent e) {
- onSizeChange();
- }
- };
-
- mSize1.addSelectionListener(sl);
- mSize2.addSelectionListener(sl);
-
- ModifyListener sizeModifyListener = new ModifyListener() {
- public void modifyText(ModifyEvent e) {
- onSizeChange();
- }
- };
-
- mSize1.addModifyListener(sizeModifyListener);
- mSize2.addModifyListener(sizeModifyListener);
-
- // first separator
- Label separator = new Label(labelParent, SWT.SEPARATOR | SWT.VERTICAL);
- separator.setLayoutData(gd = new GridData(
- GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL));
- gd.heightHint = 0;
-
- mThemeCombo = new Combo(labelParent, SWT.READ_ONLY | SWT.DROP_DOWN);
- mThemeCombo.setEnabled(false);
- updateUIFromResources();
- mThemeCombo.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onThemeChange();
- }
- });
-
- // second separator
- separator = new Label(labelParent, SWT.SEPARATOR | SWT.VERTICAL);
- separator.setLayoutData(gd = new GridData(
- GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL));
- gd.heightHint = 0;
-
- mCreateButton = new Button(labelParent, SWT.PUSH | SWT.FLAT);
- mCreateButton.setText("Create...");
- mCreateButton.setEnabled(false);
- mCreateButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- LayoutCreatorDialog dialog = new LayoutCreatorDialog(mCreateButton.getShell(),
- mEditedFile.getName(),
- Sdk.getCurrent().getTarget(mEditedFile.getProject()), mCurrentConfig);
- if (dialog.open() == Dialog.OK) {
- final FolderConfiguration config = new FolderConfiguration();
- dialog.getConfiguration(config);
-
- createAlternateLayout(config);
- }
- }
- });
+ mConfigComposite = new ConfigurationComposite(this, parent, SWT.NONE);
// create a new composite that will contain the standard editor controls.
Composite editorParent = new Composite(parent, SWT.NONE);
@@ -586,6 +236,31 @@
super.dispose();
}
+ /**
+ * Returns the selection synchronizer object.
+ * The synchronizer can be used to sync the selection of 2 or more EditPartViewers.
+ * <p/>
+ * This is changed from protected to public so that the outline can use it.
+ *
+ * @return the synchronizer
+ */
+ @Override
+ public SelectionSynchronizer getSelectionSynchronizer() {
+ return super.getSelectionSynchronizer();
+ }
+
+ /**
+ * Returns the edit domain.
+ * <p/>
+ * This is changed from protected to public so that the outline can use it.
+ *
+ * @return the edit domain
+ */
+ @Override
+ public DefaultEditDomain getEditDomain() {
+ return super.getEditDomain();
+ }
+
/* (non-Javadoc)
* Creates the palette root.
*/
@@ -596,7 +271,6 @@
return mPaletteRoot;
}
- @Override
public Clipboard getClipboard() {
return mClipboard;
}
@@ -689,7 +363,7 @@
FileEditorInput fileInput = (FileEditorInput)input;
mEditedFile = fileInput.getFile();
- updateUIFromResources();
+ mConfigComposite.updateUIFromResources();
LayoutReloadMonitor.getMonitor().addListener(mEditedFile.getProject(), this);
} else {
@@ -718,8 +392,7 @@
*
* @param uiNodeModel The {@link UiElementNode} to select.
*/
- @Override
- void selectModel(UiElementNode uiNodeModel) {
+ public void selectModel(UiElementNode uiNodeModel) {
GraphicalViewer viewer = getGraphicalViewer();
// Give focus to the graphical viewer (in case the outline has it)
@@ -737,7 +410,6 @@
// Local methods
//--------------
- @Override
public LayoutEditor getLayoutEditor() {
return mLayoutEditor;
}
@@ -867,55 +539,17 @@
* Sets the UI for the edition of a new file.
* @param configuration the configuration of the new file.
*/
- @Override
- void editNewFile(FolderConfiguration configuration) {
+ public void editNewFile(FolderConfiguration configuration) {
// update the configuration UI
setConfiguration(configuration, true /*force*/);
// enable the create button if the current and edited config are not equals
- mCreateButton.setEnabled(mEditedConfig.equals(mCurrentConfig) == false);
+ mConfigComposite.setEnabledCreate(
+ mEditedConfig.equals(mConfigComposite.getCurrentConfig()) == false);
}
public Rectangle getBounds() {
- ScreenOrientation orientation = null;
- if (mOrientation.getSelectionIndex() == 0) {
- orientation = ScreenOrientation.PORTRAIT;
- } else {
- orientation = ScreenOrientation.getByIndex(
- mOrientation.getSelectionIndex() - 1);
- }
-
- int s1, s2;
-
- // get the size from the UI controls. If it fails, revert to default values.
- try {
- s1 = Integer.parseInt(mSize1.getText().trim());
- } catch (NumberFormatException e) {
- s1 = 480;
- }
-
- try {
- s2 = Integer.parseInt(mSize2.getText().trim());
- } catch (NumberFormatException e) {
- s2 = 320;
- }
-
- // make sure s1 is bigger than s2
- if (s1 < s2) {
- int tmp = s1;
- s1 = s2;
- s2 = tmp;
- }
-
- switch (orientation) {
- default:
- case PORTRAIT:
- return new Rectangle(0, 0, s2, s1);
- case LANDSCAPE:
- return new Rectangle(0, 0, s1, s2);
- case SQUARE:
- return new Rectangle(0, 0, s1, s1);
- }
+ return mConfigComposite.getScreenBounds();
}
/**
@@ -960,7 +594,8 @@
projectRes.loadAll();
// get the project resource values based on the current config
- mConfiguredProjectRes = projectRes.getConfiguredResources(mCurrentConfig);
+ mConfiguredProjectRes = projectRes.getConfiguredResources(
+ mConfigComposite.getCurrentConfig());
}
configuredProjectResources = mConfiguredProjectRes;
@@ -975,17 +610,15 @@
if (configuredProjectResources != null && frameworkResources != null) {
// get the selected theme
- int themeIndex = mThemeCombo.getSelectionIndex();
- if (themeIndex != -1) {
- String theme = mThemeCombo.getItem(themeIndex);
-
+ String theme = mConfigComposite.getTheme();
+ if (theme != null) {
// Render a single object as described by the ViewElementDescriptor.
WidgetPullParser parser = new WidgetPullParser(descriptor);
ILayoutResult result = computeLayout(bridge, parser,
null /* projectKey */,
300 /* width */, 300 /* height */, 160 /*density*/,
160.f /*xdpi*/, 160.f /*ydpi*/, theme,
- themeIndex >= mPlatformThemeCount /*isProjectTheme*/,
+ mConfigComposite.isProjectTheme(),
configuredProjectResources, frameworkResources, projectCallback,
null /* logger */);
@@ -1016,8 +649,7 @@
/**
* Reloads this editor, by getting the new model from the {@link LayoutEditor}.
*/
- @Override
- void reloadEditor() {
+ public void reloadEditor() {
GraphicalViewer viewer = getGraphicalViewer();
viewer.setContents(getModel());
@@ -1038,8 +670,7 @@
/**
* Callback for XML model changed. Only update/recompute the layout if the editor is visible
*/
- @Override
- void onXmlModelChanged() {
+ public void onXmlModelChanged() {
if (mLayoutEditor.isGraphicalEditorActive()) {
doXmlReload(true /* force */);
recomputeLayout();
@@ -1080,511 +711,29 @@
* @param force Whether the UI should be changed to exactly match the received configuration.
*/
void setConfiguration(FolderConfiguration config, boolean force) {
- mDisableUpdates = true; // we do not want to trigger onXXXChange when setting new values in the widgets.
-
mEditedConfig = config;
mConfiguredFrameworkRes = mConfiguredProjectRes = null;
- mCountryIcon.setImage(mMatchImage);
- CountryCodeQualifier countryQualifier = config.getCountryCodeQualifier();
- if (countryQualifier != null) {
- mCountry.setText(String.format("%1$d", countryQualifier.getCode()));
- mCurrentConfig.setCountryCodeQualifier(countryQualifier);
- } else if (force) {
- mCountry.setText(""); //$NON-NLS-1$
- mCurrentConfig.setCountryCodeQualifier(null);
- } else if (mCountry.getText().length() > 0) {
- mCountryIcon.setImage(mWarningImage);
- }
+ mConfigComposite.setConfiguration(config, force);
- mNetworkIcon.setImage(mMatchImage);
- NetworkCodeQualifier networkQualifier = config.getNetworkCodeQualifier();
- if (networkQualifier != null) {
- mNetwork.setText(String.format("%1$d", networkQualifier.getCode()));
- mCurrentConfig.setNetworkCodeQualifier(networkQualifier);
- } else if (force) {
- mNetwork.setText(""); //$NON-NLS-1$
- mCurrentConfig.setNetworkCodeQualifier(null);
- } else if (mNetwork.getText().length() > 0) {
- mNetworkIcon.setImage(mWarningImage);
- }
-
- mLanguageIcon.setImage(mMatchImage);
- LanguageQualifier languageQualifier = config.getLanguageQualifier();
- if (languageQualifier != null) {
- mLanguage.setText(languageQualifier.getValue());
- mCurrentConfig.setLanguageQualifier(languageQualifier);
- } else if (force) {
- mLanguage.setText(""); //$NON-NLS-1$
- mCurrentConfig.setLanguageQualifier(null);
- } else if (mLanguage.getText().length() > 0) {
- mLanguageIcon.setImage(mWarningImage);
- }
-
- mRegionIcon.setImage(mMatchImage);
- RegionQualifier regionQualifier = config.getRegionQualifier();
- if (regionQualifier != null) {
- mRegion.setText(regionQualifier.getValue());
- mCurrentConfig.setRegionQualifier(regionQualifier);
- } else if (force) {
- mRegion.setText(""); //$NON-NLS-1$
- mCurrentConfig.setRegionQualifier(null);
- } else if (mRegion.getText().length() > 0) {
- mRegionIcon.setImage(mWarningImage);
- }
-
- mOrientationIcon.setImage(mMatchImage);
- ScreenOrientationQualifier orientationQualifier = config.getScreenOrientationQualifier();
- if (orientationQualifier != null) {
- mOrientation.select(
- ScreenOrientation.getIndex(orientationQualifier.getValue()) + 1);
- mCurrentConfig.setScreenOrientationQualifier(orientationQualifier);
- } else if (force) {
- mOrientation.select(0);
- mCurrentConfig.setScreenOrientationQualifier(null);
- } else if (mOrientation.getSelectionIndex() != 0) {
- mOrientationIcon.setImage(mWarningImage);
- }
-
- mDensityIcon.setImage(mMatchImage);
- PixelDensityQualifier densityQualifier = config.getPixelDensityQualifier();
- if (densityQualifier != null) {
- mDensity.select(
- Density.getIndex(densityQualifier.getValue()) + 1);
- mCurrentConfig.setPixelDensityQualifier(densityQualifier);
- } else if (force) {
- mOrientation.select(0);
- mCurrentConfig.setPixelDensityQualifier(null);
- } else if (mDensity.getSelectionIndex() != 0) {
- mDensityIcon.setImage(mWarningImage);
- }
-
- mTouchIcon.setImage(mMatchImage);
- TouchScreenQualifier touchQualifier = config.getTouchTypeQualifier();
- if (touchQualifier != null) {
- mTouch.select(TouchScreenType.getIndex(touchQualifier.getValue()) + 1);
- mCurrentConfig.setTouchTypeQualifier(touchQualifier);
- } else if (force) {
- mTouch.select(0);
- mCurrentConfig.setTouchTypeQualifier(null);
- } else if (mTouch.getSelectionIndex() != 0) {
- mTouchIcon.setImage(mWarningImage);
- }
-
- mKeyboardIcon.setImage(mMatchImage);
- KeyboardStateQualifier keyboardQualifier = config.getKeyboardStateQualifier();
- if (keyboardQualifier != null) {
- mKeyboard.select(KeyboardState.getIndex(keyboardQualifier.getValue()) + 1);
- mCurrentConfig.setKeyboardStateQualifier(keyboardQualifier);
- } else if (force) {
- mKeyboard.select(0);
- mCurrentConfig.setKeyboardStateQualifier(null);
- } else if (mKeyboard.getSelectionIndex() != 0) {
- mKeyboardIcon.setImage(mWarningImage);
- }
-
- mTextInputIcon.setImage(mMatchImage);
- TextInputMethodQualifier inputQualifier = config.getTextInputMethodQualifier();
- if (inputQualifier != null) {
- mTextInput.select(TextInputMethod.getIndex(inputQualifier.getValue()) + 1);
- mCurrentConfig.setTextInputMethodQualifier(inputQualifier);
- } else if (force) {
- mTextInput.select(0);
- mCurrentConfig.setTextInputMethodQualifier(null);
- } else if (mTextInput.getSelectionIndex() != 0) {
- mTextInputIcon.setImage(mWarningImage);
- }
-
- mNavigationIcon.setImage(mMatchImage);
- NavigationMethodQualifier navigationQualifiter = config.getNavigationMethodQualifier();
- if (navigationQualifiter != null) {
- mNavigation.select(
- NavigationMethod.getIndex(navigationQualifiter.getValue()) + 1);
- mCurrentConfig.setNavigationMethodQualifier(navigationQualifiter);
- } else if (force) {
- mNavigation.select(0);
- mCurrentConfig.setNavigationMethodQualifier(null);
- } else if (mNavigation.getSelectionIndex() != 0) {
- mNavigationIcon.setImage(mWarningImage);
- }
-
- mSizeIcon.setImage(mMatchImage);
- ScreenDimensionQualifier sizeQualifier = config.getScreenDimensionQualifier();
- if (sizeQualifier != null) {
- mSize1.setText(String.format("%1$d", sizeQualifier.getValue1()));
- mSize2.setText(String.format("%1$d", sizeQualifier.getValue2()));
- mCurrentConfig.setScreenDimensionQualifier(sizeQualifier);
- } else if (force) {
- mSize1.setText(""); //$NON-NLS-1$
- mSize2.setText(""); //$NON-NLS-1$
- mCurrentConfig.setScreenDimensionQualifier(null);
- } else if (mSize1.getText().length() > 0 && mSize2.getText().length() > 0) {
- mSizeIcon.setImage(mWarningImage);
- }
-
- // update the string showing the folder name
- String current = config.toDisplayString();
- mCurrentLayoutLabel.setText(current != null ? current : "(Default)");
-
- mDisableUpdates = false;
}
- /**
- * Displays an error icon in front of all the non-null qualifiers.
- */
- void displayConfigError() {
- mCountryIcon.setImage(mMatchImage);
- CountryCodeQualifier countryQualifier = mCurrentConfig.getCountryCodeQualifier();
- if (countryQualifier != null) {
- mCountryIcon.setImage(mErrorImage);
- }
- mNetworkIcon.setImage(mMatchImage);
- NetworkCodeQualifier networkQualifier = mCurrentConfig.getNetworkCodeQualifier();
- if (networkQualifier != null) {
- mNetworkIcon.setImage(mErrorImage);
- }
-
- mLanguageIcon.setImage(mMatchImage);
- LanguageQualifier languageQualifier = mCurrentConfig.getLanguageQualifier();
- if (languageQualifier != null) {
- mLanguageIcon.setImage(mErrorImage);
- }
-
- mRegionIcon.setImage(mMatchImage);
- RegionQualifier regionQualifier = mCurrentConfig.getRegionQualifier();
- if (regionQualifier != null) {
- mRegionIcon.setImage(mErrorImage);
- }
-
- mOrientationIcon.setImage(mMatchImage);
- ScreenOrientationQualifier orientationQualifier =
- mCurrentConfig.getScreenOrientationQualifier();
- if (orientationQualifier != null) {
- mOrientationIcon.setImage(mErrorImage);
- }
-
- mDensityIcon.setImage(mMatchImage);
- PixelDensityQualifier densityQualifier = mCurrentConfig.getPixelDensityQualifier();
- if (densityQualifier != null) {
- mDensityIcon.setImage(mErrorImage);
- }
-
- mTouchIcon.setImage(mMatchImage);
- TouchScreenQualifier touchQualifier = mCurrentConfig.getTouchTypeQualifier();
- if (touchQualifier != null) {
- mTouchIcon.setImage(mErrorImage);
- }
-
- mKeyboardIcon.setImage(mMatchImage);
- KeyboardStateQualifier keyboardQualifier = mCurrentConfig.getKeyboardStateQualifier();
- if (keyboardQualifier != null) {
- mKeyboardIcon.setImage(mErrorImage);
- }
-
- mTextInputIcon.setImage(mMatchImage);
- TextInputMethodQualifier inputQualifier = mCurrentConfig.getTextInputMethodQualifier();
- if (inputQualifier != null) {
- mTextInputIcon.setImage(mErrorImage);
- }
-
- mNavigationIcon.setImage(mMatchImage);
- NavigationMethodQualifier navigationQualifiter =
- mCurrentConfig.getNavigationMethodQualifier();
- if (navigationQualifiter != null) {
- mNavigationIcon.setImage(mErrorImage);
- }
-
- mSizeIcon.setImage(mMatchImage);
- ScreenDimensionQualifier sizeQualifier = mCurrentConfig.getScreenDimensionQualifier();
- if (sizeQualifier != null) {
- mSizeIcon.setImage(mErrorImage);
- }
-
- // update the string showing the folder name
- String current = mCurrentConfig.toDisplayString();
- mCurrentLayoutLabel.setText(current != null ? current : "(Default)");
- }
-
- @Override
- UiDocumentNode getModel() {
+ public UiDocumentNode getModel() {
return mLayoutEditor.getUiRootNode();
}
- @Override
- void reloadPalette() {
+ public void reloadPalette() {
PaletteFactory.createPaletteRoot(mPaletteRoot, mLayoutEditor.getTargetData());
}
- private void onCountryCodeChange() {
- // because mCountry triggers onCountryCodeChange at each modification, calling setText()
- // will trigger notifications, and we don't want that.
- if (mDisableUpdates == true) {
- return;
- }
-
- // update the current config
- String value = mCountry.getText();
-
- // empty string, means no qualifier.
- if (value.length() == 0) {
- mCurrentConfig.setCountryCodeQualifier(null);
- } else {
- try {
- CountryCodeQualifier qualifier = CountryCodeQualifier.getQualifier(
- CountryCodeQualifier.getFolderSegment(Integer.parseInt(value)));
- if (qualifier != null) {
- mCurrentConfig.setCountryCodeQualifier(qualifier);
- } else {
- // Failure! Looks like the value is wrong (for instance a one letter string).
- // We do nothing in this case.
- mCountryIcon.setImage(mErrorImage);
- return;
- }
- } catch (NumberFormatException e) {
- // Looks like the code is not a number. This should not happen since the text
- // field has a VerifyListener that prevents it.
- mCurrentConfig.setCountryCodeQualifier(null);
- mCountryIcon.setImage(mErrorImage);
- }
- }
-
- // look for a file to open/create
- onConfigurationChange();
- }
-
- private void onNetworkCodeChange() {
- // because mNetwork triggers onNetworkCodeChange at each modification, calling setText()
- // will trigger notifications, and we don't want that.
- if (mDisableUpdates == true) {
- return;
- }
-
- // update the current config
- String value = mNetwork.getText();
-
- // empty string, means no qualifier.
- if (value.length() == 0) {
- mCurrentConfig.setNetworkCodeQualifier(null);
- } else {
- try {
- NetworkCodeQualifier qualifier = NetworkCodeQualifier.getQualifier(
- NetworkCodeQualifier.getFolderSegment(Integer.parseInt(value)));
- if (qualifier != null) {
- mCurrentConfig.setNetworkCodeQualifier(qualifier);
- } else {
- // Failure! Looks like the value is wrong (for instance a one letter string).
- // We do nothing in this case.
- mNetworkIcon.setImage(mErrorImage);
- return;
- }
- } catch (NumberFormatException e) {
- // Looks like the code is not a number. This should not happen since the text
- // field has a VerifyListener that prevents it.
- mCurrentConfig.setNetworkCodeQualifier(null);
- mNetworkIcon.setImage(mErrorImage);
- }
- }
-
- // look for a file to open/create
- onConfigurationChange();
- }
-
- /**
- * Call back for language combo selection
- */
- private void onLanguageChange() {
- // because mLanguage triggers onLanguageChange at each modification, the filling
- // of the combo with data will trigger notifications, and we don't want that.
- if (mDisableUpdates == true) {
- return;
- }
-
- // update the current config
- String value = mLanguage.getText();
-
- updateRegionUi(null /* projectResources */, null /* frameworkResources */);
-
- // empty string, means no qualifier.
- if (value.length() == 0) {
- mCurrentConfig.setLanguageQualifier(null);
- } else {
- LanguageQualifier qualifier = null;
- String segment = LanguageQualifier.getFolderSegment(value);
- if (segment != null) {
- qualifier = LanguageQualifier.getQualifier(segment);
- }
-
- if (qualifier != null) {
- mCurrentConfig.setLanguageQualifier(qualifier);
- } else {
- // Failure! Looks like the value is wrong (for instance a one letter string).
- mCurrentConfig.setLanguageQualifier(null);
- mLanguageIcon.setImage(mErrorImage);
- }
- }
-
- // look for a file to open/create
- onConfigurationChange();
- }
-
- private void onRegionChange() {
- // because mRegion triggers onRegionChange at each modification, the filling
- // of the combo with data will trigger notifications, and we don't want that.
- if (mDisableUpdates == true) {
- return;
- }
-
- // update the current config
- String value = mRegion.getText();
-
- // empty string, means no qualifier.
- if (value.length() == 0) {
- mCurrentConfig.setRegionQualifier(null);
- } else {
- RegionQualifier qualifier = null;
- String segment = RegionQualifier.getFolderSegment(value);
- if (segment != null) {
- qualifier = RegionQualifier.getQualifier(segment);
- }
-
- if (qualifier != null) {
- mCurrentConfig.setRegionQualifier(qualifier);
- } else {
- // Failure! Looks like the value is wrong (for instance a one letter string).
- mCurrentConfig.setRegionQualifier(null);
- mRegionIcon.setImage(mErrorImage);
- }
- }
-
- // look for a file to open/create
- onConfigurationChange();
- }
-
- private void onOrientationChange() {
- // update the current config
- int index = mOrientation.getSelectionIndex();
- if (index != 0) {
- mCurrentConfig.setScreenOrientationQualifier(new ScreenOrientationQualifier(
- ScreenOrientation.getByIndex(index-1)));
- } else {
- mCurrentConfig.setScreenOrientationQualifier(null);
- }
-
- // look for a file to open/create
- onConfigurationChange();
- }
-
- private void onDensityChange() {
- int index = mDensity.getSelectionIndex();
- if (index != 0) {
- mCurrentConfig.setPixelDensityQualifier((new PixelDensityQualifier(
- Density.getByIndex(index-1))));
- } else {
- mCurrentConfig.setPixelDensityQualifier(null);
- }
-
- // look for a file to open/create
- onConfigurationChange();
- }
-
- private void onTouchChange() {
- // update the current config
- int index = mTouch.getSelectionIndex();
- if (index != 0) {
- mCurrentConfig.setTouchTypeQualifier(new TouchScreenQualifier(
- TouchScreenType.getByIndex(index-1)));
- } else {
- mCurrentConfig.setTouchTypeQualifier(null);
- }
-
- // look for a file to open/create
- onConfigurationChange();
- }
-
- private void onKeyboardChange() {
- // update the current config
- int index = mKeyboard.getSelectionIndex();
- if (index != 0) {
- mCurrentConfig.setKeyboardStateQualifier(new KeyboardStateQualifier(
- KeyboardState.getByIndex(index-1)));
- } else {
- mCurrentConfig.setKeyboardStateQualifier(null);
- }
-
- // look for a file to open/create
- onConfigurationChange();
- }
-
- private void onTextInputChange() {
- // update the current config
- int index = mTextInput.getSelectionIndex();
- if (index != 0) {
- mCurrentConfig.setTextInputMethodQualifier(new TextInputMethodQualifier(
- TextInputMethod.getByIndex(index-1)));
- } else {
- mCurrentConfig.setTextInputMethodQualifier(null);
- }
-
- // look for a file to open/create
- onConfigurationChange();
- }
-
- private void onNavigationChange() {
- // update the current config
- int index = mNavigation.getSelectionIndex();
- if (index != 0) {
- mCurrentConfig.setNavigationMethodQualifier(new NavigationMethodQualifier(
- NavigationMethod.getByIndex(index-1)));
- } else {
- mCurrentConfig.setNavigationMethodQualifier(null);
- }
-
- // look for a file to open/create
- onConfigurationChange();
- }
-
- private void onSizeChange() {
- // because mSize1 and mSize2 trigger onSizeChange at each modification, calling setText()
- // will trigger notifications, and we don't want that.
- if (mDisableUpdates == true) {
- return;
- }
-
- // update the current config
- String size1 = mSize1.getText();
- String size2 = mSize2.getText();
-
- // if only one of the strings is empty, do nothing
- if ((size1.length() == 0) ^ (size2.length() == 0)) {
- mSizeIcon.setImage(mErrorImage);
- return;
- } else if (size1.length() == 0 && size2.length() == 0) {
- // both sizes are empty: remove the qualifier.
- mCurrentConfig.setScreenDimensionQualifier(null);
- } else {
- ScreenDimensionQualifier qualifier = ScreenDimensionQualifier.getQualifier(size1,
- size2);
-
- if (qualifier != null) {
- mCurrentConfig.setScreenDimensionQualifier(qualifier);
- } else {
- // Failure! Looks like the value is wrong.
- // we do nothing in this case.
- return;
- }
- }
-
- // look for a file to open/create
- onConfigurationChange();
- }
/**
* Looks for a file matching the new {@link FolderConfiguration} and attempts to open it.
* <p/>If there is no match, notify the user.
*/
- private void onConfigurationChange() {
+ public void onConfigurationChange() {
mConfiguredFrameworkRes = mConfiguredProjectRes = null;
if (mEditedFile == null || mEditedConfig == null) {
@@ -1600,7 +749,7 @@
if (resources != null) {
match = resources.getMatchingFile(mEditedFile.getName(),
ResourceFolderType.LAYOUT,
- mCurrentConfig);
+ mConfigComposite.getCurrentConfig());
}
if (match != null) {
@@ -1623,73 +772,50 @@
setConfiguration(mEditedConfig, false /*force*/);
// enable the create button if the current and edited config are not equals
- mCreateButton.setEnabled(mEditedConfig.equals(mCurrentConfig) == false);
+ mConfigComposite.setEnabledCreate(
+ mEditedConfig.equals(mConfigComposite.getCurrentConfig()) == false);
// Even though the layout doesn't change, the config changed, and referenced
// resources need to be updated.
recomputeLayout();
} else {
- // update the configuration icons with the new edited config.
- displayConfigError();
-
// enable the Create button
- mCreateButton.setEnabled(true);
+ mConfigComposite.setEnabledCreate(true);
// display the error.
+ FolderConfiguration currentConfig = mConfigComposite.getCurrentConfig();
String message = String.format(
"No resources match the configuration\n \n\t%1$s\n \nChange the configuration or create:\n \n\tres/%2$s/%3$s\n \nYou can also click the 'Create' button above.",
- mCurrentConfig.toDisplayString(),
- mCurrentConfig.getFolderName(ResourceFolderType.LAYOUT,
+ currentConfig.toDisplayString(),
+ currentConfig.getFolderName(ResourceFolderType.LAYOUT,
Sdk.getCurrent().getTarget(mEditedFile.getProject())),
mEditedFile.getName());
showErrorInEditor(message);
}
}
- private void onThemeChange() {
- int themeIndex = mThemeCombo.getSelectionIndex();
- if (themeIndex != -1) {
- String theme = mThemeCombo.getItem(themeIndex);
-
- if (theme.equals(THEME_SEPARATOR)) {
- mThemeCombo.select(0);
- }
-
- recomputeLayout();
- }
+ public void onThemeChange() {
+ recomputeLayout();
}
- /**
- * Creates a composite with no margin/spacing, and puts a {@link Label} in it with the matching
- * icon.
- * @param parent the parent to receive the composite
- * @return the created {@link Label} object.
- */
- private Label createControlComposite(Composite parent, boolean grab) {
- GridLayout gl;
+ public void onCreate() {
+ LayoutCreatorDialog dialog = new LayoutCreatorDialog(mParent.getShell(),
+ mEditedFile.getName(),
+ Sdk.getCurrent().getTarget(mEditedFile.getProject()),
+ mConfigComposite.getCurrentConfig());
+ if (dialog.open() == Dialog.OK) {
+ final FolderConfiguration config = new FolderConfiguration();
+ dialog.getConfiguration(config);
- Composite composite = new Composite(parent, SWT.NONE);
- composite.setLayout(gl = new GridLayout(2, false));
- gl.marginHeight = gl.marginWidth = 0;
- gl.horizontalSpacing = 0;
- if (grab) {
- composite.setLayoutData(
- new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
+ createAlternateLayout(config);
}
-
- // create the label
- Label icon = new Label(composite, SWT.NONE);
- icon.setImage(mMatchImage);
-
- return icon;
}
/**
* Recomputes the layout with the help of layoutlib.
*/
- @Override
@SuppressWarnings("deprecation")
- void recomputeLayout() {
+ public void recomputeLayout() {
doXmlReload(false /* force */);
try {
// check that the resource exists. If the file is opened but the project is closed
@@ -1760,19 +886,14 @@
}
// get the resources of the file's project.
- if (mConfiguredProjectRes == null) {
- // make sure they are loaded
- projectRes.loadAll();
-
- // get the project resource values based on the current config
- mConfiguredProjectRes = projectRes.getConfiguredResources(mCurrentConfig);
- }
+ Map<String, Map<String, IResourceValue>> configuredProjectRes =
+ getConfiguredProjectResources();
// get the framework resources
Map<String, Map<String, IResourceValue>> frameworkResources =
getConfiguredFrameworkResources();
- if (mConfiguredProjectRes != null && frameworkResources != null) {
+ if (configuredProjectRes != null && frameworkResources != null) {
if (mProjectCallback == null) {
mProjectCallback = new ProjectCallback(
bridge.classLoader, projectRes, iProject);
@@ -1801,19 +922,19 @@
}
// get the selected theme
- int themeIndex = mThemeCombo.getSelectionIndex();
- if (themeIndex != -1) {
- String theme = mThemeCombo.getItem(themeIndex);
+ String theme = mConfigComposite.getTheme();
+ if (theme != null) {
// Compute the layout
UiElementPullParser parser = new UiElementPullParser(getModel());
Rectangle rect = getBounds();
- boolean isProjectTheme = themeIndex >= mPlatformThemeCount;
+ boolean isProjectTheme = mConfigComposite.isProjectTheme();
// FIXME pass the density/dpi from somewhere (resource config or skin).
// For now, get it from the config
int density = Density.MEDIUM.getDpiValue();
- PixelDensityQualifier qual = mCurrentConfig.getPixelDensityQualifier();
+ PixelDensityQualifier qual =
+ mConfigComposite.getCurrentConfig().getPixelDensityQualifier();
if (qual != null) {
int d = qual.getValue().getDpiValue();
if (d > 0) {
@@ -1825,7 +946,7 @@
iProject /* projectKey */,
rect.width, rect.height, density, density, density,
theme, isProjectTheme,
- mConfiguredProjectRes, frameworkResources, mProjectCallback,
+ configuredProjectRes, frameworkResources, mProjectCallback,
mLogger);
// update the UiElementNode with the layout info.
@@ -1970,8 +1091,7 @@
/**
* Responds to a page change that made the Graphical editor page the activated page.
*/
- @Override
- void activated() {
+ public void activated() {
if (mNeedsRecompute || mNeedsXmlReload) {
recomputeLayout();
}
@@ -1980,265 +1100,75 @@
/**
* Responds to a page change that made the Graphical editor page the deactivated page
*/
- @Override
- void deactivated() {
+ public void deactivated() {
// nothing to be done here for now.
}
- /**
- * Updates the UI from values in the resources, such as languages, regions, themes, etc...
- * This must be called from the UI thread.
- */
- private void updateUIFromResources() {
-
- ResourceManager manager = ResourceManager.getInstance();
-
- ProjectResources frameworkProject = getFrameworkResources();
-
- mDisableUpdates = true;
-
- // Reset stuff
- int selection = mThemeCombo.getSelectionIndex();
- mThemeCombo.removeAll();
- mPlatformThemeCount = 0;
- mLanguage.removeAll();
-
- Set<String> languages = new HashSet<String>();
- ArrayList<String> themes = new ArrayList<String>();
-
- // get the themes, and languages from the Framework.
- if (frameworkProject != null) {
- // get the configured resources for the framework
- Map<String, Map<String, IResourceValue>> frameworResources =
- getConfiguredFrameworkResources();
-
- if (frameworResources != null) {
- // get the styles.
- Map<String, IResourceValue> styles = frameworResources.get(
- ResourceType.STYLE.getName());
-
-
- // collect the themes out of all the styles.
- for (IResourceValue value : styles.values()) {
- String name = value.getName();
- if (name.startsWith("Theme.") || name.equals("Theme")) {
- themes.add(value.getName());
- mPlatformThemeCount++;
- }
- }
-
- // sort them and add them to the combo
- Collections.sort(themes);
-
- for (String theme : themes) {
- mThemeCombo.add(theme);
- }
-
- mPlatformThemeCount = themes.size();
- themes.clear();
- }
- // now get the languages from the framework.
- Set<String> frameworkLanguages = frameworkProject.getLanguages();
- if (frameworkLanguages != null) {
- languages.addAll(frameworkLanguages);
- }
- }
-
- // now get the themes and languages from the project.
- ProjectResources project = null;
- if (mEditedFile != null) {
- project = manager.getProjectResources(mEditedFile.getProject());
-
- // in cases where the opened file is not linked to a project, this could be null.
- if (project != null) {
- // get the configured resources for the project
- if (mConfiguredProjectRes == null) {
- // make sure they are loaded
- project.loadAll();
-
- // get the project resource values based on the current config
- mConfiguredProjectRes = project.getConfiguredResources(mCurrentConfig);
- }
-
- if (mConfiguredProjectRes != null) {
- // get the styles.
- Map<String, IResourceValue> styleMap = mConfiguredProjectRes.get(
- ResourceType.STYLE.getName());
-
- if (styleMap != null) {
- // collect the themes out of all the styles, ie styles that extend,
- // directly or indirectly a platform theme.
- for (IResourceValue value : styleMap.values()) {
- if (isTheme(value, styleMap)) {
- themes.add(value.getName());
- }
- }
-
- // sort them and add them the to the combo.
- if (mPlatformThemeCount > 0 && themes.size() > 0) {
- mThemeCombo.add(THEME_SEPARATOR);
- }
-
- Collections.sort(themes);
-
- for (String theme : themes) {
- mThemeCombo.add(theme);
- }
- }
- }
-
- // now get the languages from the project.
- Set<String> projectLanguages = project.getLanguages();
- if (projectLanguages != null) {
- languages.addAll(projectLanguages);
- }
- }
- }
-
- // add the languages to the Combo
- for (String language : languages) {
- mLanguage.add(language);
- }
-
- mDisableUpdates = false;
-
- // and update the Region UI based on the current language
- updateRegionUi(project, frameworkProject);
-
- // handle default selection of themes
- if (mThemeCombo.getItemCount() > 0) {
- mThemeCombo.setEnabled(true);
- if (selection == -1) {
- selection = 0;
- }
-
- if (mThemeCombo.getItemCount() <= selection) {
- mThemeCombo.select(0);
- } else {
- mThemeCombo.select(selection);
- }
- } else {
- mThemeCombo.setEnabled(false);
- }
- }
-
- /**
- * Returns whether the given <var>style</var> is a theme.
- * This is done by making sure the parent is a theme.
- * @param value the style to check
- * @param styleMap the map of styles for the current project. Key is the style name.
- * @return True if the given <var>style</var> is a theme.
- */
- private boolean isTheme(IResourceValue value, Map<String, IResourceValue> styleMap) {
- if (value instanceof IStyleResourceValue) {
- IStyleResourceValue style = (IStyleResourceValue)value;
-
- boolean frameworkStyle = false;
- String parentStyle = style.getParentStyle();
- if (parentStyle == null) {
- // if there is no specified parent style we look an implied one.
- // For instance 'Theme.light' is implied child style of 'Theme',
- // and 'Theme.light.fullscreen' is implied child style of 'Theme.light'
- String name = style.getName();
- int index = name.lastIndexOf('.');
- if (index != -1) {
- parentStyle = name.substring(0, index);
- }
- } else {
- // remove the useless @ if it's there
- if (parentStyle.startsWith("@")) {
- parentStyle = parentStyle.substring(1);
- }
-
- // check for framework identifier.
- if (parentStyle.startsWith("android:")) {
- frameworkStyle = true;
- parentStyle = parentStyle.substring("android:".length());
- }
-
- // at this point we could have the format style/<name>. we want only the name
- if (parentStyle.startsWith("style/")) {
- parentStyle = parentStyle.substring("style/".length());
- }
- }
-
- if (frameworkStyle) {
- // if the parent is a framework style, it has to be 'Theme' or 'Theme.*'
- return parentStyle.equals("Theme") || parentStyle.startsWith("Theme.");
- } else {
- // if it's a project style, we check this is a theme.
- value = styleMap.get(parentStyle);
- if (value != null) {
- return isTheme(value, styleMap);
- }
- }
- }
-
- return false;
- }
-
- /**
- * Update the Region UI widget based on the current language selection
- * @param projectResources the project resources or {@code null}.
- * @param frameworkResources the framework resource or {@code null}
- */
- private void updateRegionUi(ProjectResources projectResources,
- ProjectResources frameworkResources) {
- if (projectResources == null && mEditedFile != null) {
- projectResources = ResourceManager.getInstance().getProjectResources(
- mEditedFile.getProject());
- }
-
- if (frameworkResources == null) {
- frameworkResources = getFrameworkResources();
- }
-
- String currentLanguage = mLanguage.getText();
-
- Set<String> set = null;
-
- if (projectResources != null) {
- set = projectResources.getRegions(currentLanguage);
- }
-
- if (frameworkResources != null) {
- if (set != null) {
- Set<String> set2 = frameworkResources.getRegions(currentLanguage);
- set.addAll(set2);
- } else {
- set = frameworkResources.getRegions(currentLanguage);
- }
- }
-
- if (set != null) {
- mDisableUpdates = true;
-
- mRegion.removeAll();
- for (String region : set) {
- mRegion.add(region);
- }
-
- mDisableUpdates = false;
- }
- }
-
- private Map<String, Map<String, IResourceValue>> getConfiguredFrameworkResources() {
+ public Map<String, Map<String, IResourceValue>> getConfiguredFrameworkResources() {
if (mConfiguredFrameworkRes == null) {
ProjectResources frameworkRes = getFrameworkResources();
if (frameworkRes == null) {
AdtPlugin.log(IStatus.ERROR, "Failed to get ProjectResource for the framework");
+ } else {
+ // get the framework resource values based on the current config
+ mConfiguredFrameworkRes = frameworkRes.getConfiguredResources(
+ mConfigComposite.getCurrentConfig());
}
-
- // get the framework resource values based on the current config
- mConfiguredFrameworkRes = frameworkRes.getConfiguredResources(mCurrentConfig);
}
return mConfiguredFrameworkRes;
}
+ public Map<String, Map<String, IResourceValue>> getConfiguredProjectResources() {
+ if (mConfiguredProjectRes == null) {
+ ProjectResources project = getProjectResources();
+
+ // make sure they are loaded
+ project.loadAll();
+
+ // get the project resource values based on the current config
+ mConfiguredProjectRes = project.getConfiguredResources(
+ mConfigComposite.getCurrentConfig());
+ }
+
+ return mConfiguredProjectRes;
+ }
+
/**
- * Creates a new layout file from the specificed {@link FolderConfiguration}.
+ * Returns a {@link ProjectResources} for the framework resources.
+ * @return the framework resources or null if not found.
+ */
+ public ProjectResources getFrameworkResources() {
+ if (mEditedFile != null) {
+ Sdk currentSdk = Sdk.getCurrent();
+ if (currentSdk != null) {
+ IAndroidTarget target = currentSdk.getTarget(mEditedFile.getProject());
+
+ if (target != null) {
+ AndroidTargetData data = currentSdk.getTargetData(target);
+
+ if (data != null) {
+ return data.getFrameworkResources();
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public ProjectResources getProjectResources() {
+ if (mEditedFile != null) {
+ ResourceManager manager = ResourceManager.getInstance();
+ return manager.getProjectResources(mEditedFile.getProject());
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates a new layout file from the specified {@link FolderConfiguration}.
*/
private void createAlternateLayout(final FolderConfiguration config) {
new Job("Create Alternate Resource") {
@@ -2295,7 +1225,7 @@
// to trigger the edit of the new file.
res.refreshLocal(IResource.DEPTH_INFINITE, new IProgressMonitor() {
public void done() {
- mCurrentConfig.set(config);
+ mConfigComposite.setConfig(config);
mParent.getDisplay().asyncExec(new Runnable() {
public void run() {
onConfigurationChange();
@@ -2358,34 +1288,11 @@
}
/**
- * Returns a {@link ProjectResources} for the framework resources.
- * @return the framework resources or null if not found.
- */
- private ProjectResources getFrameworkResources() {
- if (mEditedFile != null) {
- Sdk currentSdk = Sdk.getCurrent();
- if (currentSdk != null) {
- IAndroidTarget target = currentSdk.getTarget(mEditedFile.getProject());
-
- if (target != null) {
- AndroidTargetData data = currentSdk.getTargetData(target);
-
- if (data != null) {
- return data.getFrameworkResources();
- }
- }
- }
- }
-
- return null;
- }
-
- /**
* Computes a layout by calling the correct computeLayout method of ILayoutBridge based on
* the implementation API level.
*/
@SuppressWarnings("deprecation")
- private ILayoutResult computeLayout(LayoutBridge bridge,
+ private static ILayoutResult computeLayout(LayoutBridge bridge,
IXmlPullParser layoutDescription, Object projectKey,
int screenWidth, int screenHeight, int density, float xdpi, float ydpi,
String themeName, boolean isProjectTheme,
@@ -2393,9 +1300,17 @@
Map<String, Map<String, IResourceValue>> frameworkResources,
IProjectCallback projectCallback, ILayoutLog logger) {
- if (bridge.apiLevel >= 3) {
- // newer api with boolean for separation of project/framework theme,
- // and density support.
+ if (bridge.apiLevel >= ILayoutBridge.API_CURRENT) {
+ // newest API with support for "render full height"
+ // TODO: link boolean to UI.
+ return bridge.bridge.computeLayout(layoutDescription,
+ projectKey, screenWidth, screenHeight, false /* renderFullHeight */,
+ density, xdpi, ydpi,
+ themeName, isProjectTheme,
+ projectResources, frameworkResources, projectCallback,
+ logger);
+ } else if (bridge.apiLevel == 3) {
+ // newer api with density support.
return bridge.bridge.computeLayout(layoutDescription,
projectKey, screenWidth, screenHeight, density, xdpi, ydpi,
themeName, isProjectTheme,
@@ -2405,8 +1320,8 @@
// api with boolean for separation of project/framework theme
return bridge.bridge.computeLayout(layoutDescription,
projectKey, screenWidth, screenHeight, themeName, isProjectTheme,
- mConfiguredProjectRes, frameworkResources, mProjectCallback,
- mLogger);
+ projectResources, frameworkResources, projectCallback,
+ logger);
} else {
// oldest api with no density/dpi, and project theme boolean mixed
// into the theme name.
@@ -2419,8 +1334,9 @@
return bridge.bridge.computeLayout(layoutDescription,
projectKey, screenWidth, screenHeight, themeName,
- mConfiguredProjectRes, frameworkResources, mProjectCallback,
- mLogger);
+ projectResources, frameworkResources, projectCallback,
+ logger);
}
}
+
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/AbstractGraphicalLayoutEditor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/IGraphicalLayoutEditor.java
old mode 100644
new mode 100755
similarity index 75%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/AbstractGraphicalLayoutEditor.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/IGraphicalLayoutEditor.java
index e8ccdab..6414b20
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/AbstractGraphicalLayoutEditor.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/IGraphicalLayoutEditor.java
@@ -4,7 +4,7 @@
* Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
*
* Unless required by applicable law or agreed to in writing, software
@@ -22,17 +22,16 @@
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import org.eclipse.gef.DefaultEditDomain;
-import org.eclipse.gef.ui.parts.GraphicalEditorWithPalette;
import org.eclipse.gef.ui.parts.SelectionSynchronizer;
import org.eclipse.swt.dnd.Clipboard;
-import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IEditorPart;
/**
- * Abstract GraphicalLayoutEditor.
+ * Interface defining what {@link LayoutEditor} expects from a GraphicalLayoutEditor part.
+ *
+ * @since GLE2
*/
-/*package*/ abstract class AbstractGraphicalLayoutEditor extends GraphicalEditorWithPalette
- implements IWorkbenchPart, ILayoutReloadListener {
+/*package*/ interface IGraphicalLayoutEditor extends IEditorPart, ILayoutReloadListener {
/**
* Sets the UI for the edition of a new file.
@@ -63,7 +62,7 @@
/**
* Used by LayoutEditor.UiEditorActions.selectUiNode to select a new UI Node
* created by {@link ElementCreateCommand#execute()}.
- *
+ *
* @param uiNodeModel The {@link UiElementNode} to select.
*/
abstract void selectModel(UiElementNode uiNodeModel);
@@ -71,27 +70,8 @@
/**
* Returns the selection synchronizer object.
* The synchronizer can be used to sync the selection of 2 or more EditPartViewers.
- * <p/>
- * This is changed from protected to public so that the outline can use it.
- *
- * @return the synchronizer
*/
- @Override
- public SelectionSynchronizer getSelectionSynchronizer() {
- return super.getSelectionSynchronizer();
- }
-
- /**
- * Returns the edit domain.
- * <p/>
- * This is changed from protected to public so that the outline can use it.
- *
- * @return the edit domain
- */
- @Override
- public DefaultEditDomain getEditDomain() {
- return super.getEditDomain();
- }
+ abstract public SelectionSynchronizer getSelectionSynchronizer();
abstract void reloadPalette();
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java
index 6226b7b..1176253 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java
@@ -46,7 +46,7 @@
import org.w3c.dom.Document;
/**
- * Multi-page form editor for /res/layout XML files.
+ * Multi-page form editor for /res/layout XML files.
*/
public class LayoutEditor extends AndroidEditor implements IShowEditorInput, IPartListener {
@@ -54,8 +54,8 @@
/** Root node of the UI element hierarchy */
private UiDocumentNode mUiRootNode;
-
- private AbstractGraphicalLayoutEditor mGraphicalEditor;
+
+ private IGraphicalLayoutEditor mGraphicalEditor;
private int mGraphicalEditorIndex;
/** Implementation of the {@link IContentOutlinePage} for this editor */
private UiContentOutlinePage mOutline;
@@ -63,7 +63,7 @@
private UiPropertySheetPage mPropertyPage;
private UiEditorActions mUiEditorActions;
-
+
/**
* Creates the form editor for resources XML files.
*/
@@ -87,7 +87,7 @@
super.dispose();
}
-
+
/**
* Save the XML.
* <p/>
@@ -105,12 +105,12 @@
mGraphicalEditor.doSave(monitor);
}
}
-
+
/**
* Returns whether the "save as" operation is supported by this editor.
* <p/>
* Save-As is a valid operation for the ManifestEditor since it acts on a
- * single source file.
+ * single source file.
*
* @see IEditorPart
*/
@@ -128,9 +128,15 @@
// The graphical layout editor is now enabled by default.
// In case there's an issue we provide a way to disable it using an
// env variable.
- if (System.getenv("ANDROID_DISABLE_LAYOUT") == null) {
+ if (System.getenv("ANDROID_DISABLE_LAYOUT") == null) { //$NON-NLS-1$
if (mGraphicalEditor == null) {
- mGraphicalEditor = new GraphicalLayoutEditor(this);
+
+ if (System.getenv("USE_GLE2") != null) { //$NON-NLS-1$ //$NON-NLS-2$
+ mGraphicalEditor = new GLE2(this);
+ } else {
+ mGraphicalEditor = new GraphicalLayoutEditor(this);
+ }
+
mGraphicalEditorIndex = addPage(mGraphicalEditor, getEditorInput());
setPageText(mGraphicalEditorIndex, mGraphicalEditor.getTitle());
} else {
@@ -174,7 +180,7 @@
super.setInputWithNotify(input);
handleNewInput(input);
}
-
+
/**
* Called to replace the current {@link IEditorInput} with another one.
* <p/>This is used when {@link MatchingStrategy} returned <code>true</code> which means we're
@@ -183,10 +189,10 @@
public void showEditorInput(IEditorInput editorInput) {
// save the current editor input.
doSave(new NullProgressMonitor());
-
+
// get the current page
int currentPage = getActivePage();
-
+
// remove the pages, except for the graphical editor, which will be dynamically adapted
// to the new model.
// page after the graphical editor:
@@ -198,10 +204,10 @@
for (int i = mGraphicalEditorIndex - 1 ; i >= 0 ; i--) {
removePage(i);
}
-
+
// set the current input.
setInputWithNotify(editorInput);
-
+
// re-create or reload the pages with the default page shown as the previous active page.
createAndroidPages();
selectDefaultPage(Integer.toString(currentPage));
@@ -211,10 +217,10 @@
mOutline.reloadModel();
}
}
-
+
/**
* Processes the new XML Model, which XML root node is given.
- *
+ *
* @param xml_doc The XML document, if available, or null if none exists.
*/
@Override
@@ -226,16 +232,16 @@
// update the model first, since it is used by the viewers.
super.xmlModelChanged(xml_doc);
-
+
if (mGraphicalEditor != null) {
mGraphicalEditor.onXmlModelChanged();
}
-
+
if (mOutline != null) {
mOutline.reloadModel();
}
}
-
+
/* (non-java doc)
* Returns the IContentOutlinePage when asked for it.
*/
@@ -246,29 +252,33 @@
// This fixes the case where a layout file is opened in XML view first and the outline
// gets stuck in the XML outline.
if (IContentOutlinePage.class == adapter && mGraphicalEditor != null) {
- if (mOutline == null) {
- mOutline = new UiContentOutlinePage(mGraphicalEditor, new TreeViewer());
+
+ if (mOutline == null && mGraphicalEditor instanceof GraphicalLayoutEditor) {
+ // TODO add support for GLE2
+ mOutline = new UiContentOutlinePage(
+ (GraphicalLayoutEditor) mGraphicalEditor,
+ new TreeViewer());
}
-
+
return mOutline;
}
-
+
if (IPropertySheetPage.class == adapter && mGraphicalEditor != null) {
if (mPropertyPage == null) {
mPropertyPage = new UiPropertySheetPage();
}
-
+
return mPropertyPage;
}
// return default
return super.getAdapter(adapter);
}
-
+
@Override
protected void pageChange(int newPageIndex) {
super.pageChange(newPageIndex);
-
+
if (mGraphicalEditor != null) {
if (newPageIndex == mGraphicalEditorIndex) {
mGraphicalEditor.activated();
@@ -277,9 +287,9 @@
}
}
}
-
+
// ----- IPartListener Methods ----
-
+
public void partActivated(IWorkbenchPart part) {
if (part == this) {
if (mGraphicalEditor != null) {
@@ -312,7 +322,7 @@
EclipseUiHelper.showView(EclipseUiHelper.CONTENT_OUTLINE_VIEW_ID, false /* activate */);
EclipseUiHelper.showView(EclipseUiHelper.PROPERTY_SHEET_VIEW_ID, false /* activate */);
}
-
+
public class UiEditorActions extends UiActions {
@Override
@@ -331,16 +341,16 @@
// Pass. There is nothing to commit before the XML is changed here.
}
}
-
+
public UiEditorActions getUiEditorActions() {
if (mUiEditorActions == null) {
mUiEditorActions = new UiEditorActions();
}
return mUiEditorActions;
}
-
+
// ---- Local Methods ----
-
+
/**
* Returns true if the Graphics editor page is visible. This <b>must</b> be
* called from the UI thread.
@@ -357,20 +367,20 @@
}
return false;
- }
-
+ }
+
@Override
protected void initUiRootNode(boolean force) {
// The root UI node is always created, even if there's no corresponding XML node.
if (mUiRootNode == null || force) {
// get the target data from the opened file (and its project)
AndroidTargetData data = getTargetData();
-
+
Document doc = null;
if (mUiRootNode != null) {
doc = mUiRootNode.getXmlDocument();
}
-
+
DocumentDescriptor desc;
if (data == null) {
desc = new DocumentDescriptor("temp", null /*children*/);
@@ -385,18 +395,18 @@
onDescriptorsChanged(doc);
}
}
-
+
private void onDescriptorsChanged(Document document) {
if (document != null) {
mUiRootNode.loadFromXmlNode(document);
} else {
mUiRootNode.reloadFromXmlNode(mUiRootNode.getXmlDocument());
}
-
+
if (mOutline != null) {
mOutline.reloadModel();
}
-
+
if (mGraphicalEditor != null) {
mGraphicalEditor.reloadEditor();
mGraphicalEditor.reloadPalette();
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/PaletteFactory.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/PaletteFactory.java
index 950be13..7bf6acf 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/PaletteFactory.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/PaletteFactory.java
@@ -28,6 +28,8 @@
/**
* Factory that creates the palette for the {@link GraphicalLayoutEditor}.
+ *
+ * @since GLE1
*/
public class PaletteFactory {
@@ -37,7 +39,7 @@
public static PaletteRoot createPaletteRoot(PaletteRoot currentPalette,
AndroidTargetData targetData) {
-
+
if (currentPalette == null) {
currentPalette = new PaletteRoot();
}
@@ -45,7 +47,7 @@
for (int n = currentPalette.getChildren().size() - 1; n >= 0; n--) {
currentPalette.getChildren().remove(n);
}
-
+
if (targetData != null) {
addTools(currentPalette);
addViews(currentPalette, "Layouts",
@@ -59,7 +61,7 @@
private static void addTools(PaletteRoot paletteRoot) {
PaletteGroup group = new PaletteGroup("Tools");
-
+
// Default tools: selection.
// Do not use the MarqueeToolEntry since we don't support multiple selection.
/* -- Do not put the selection tool. It's the unique tool so it looks useless.
@@ -75,7 +77,7 @@
private static void addViews(PaletteRoot paletteRoot, String groupName,
List<ElementDescriptor> descriptors) {
PaletteDrawer group = new PaletteDrawer(groupName);
-
+
for (ElementDescriptor desc : descriptors) {
PaletteTemplateEntry entry = new PaletteTemplateEntry(
desc.getUiName(), // label
@@ -84,10 +86,10 @@
desc.getImageDescriptor(), // small icon
desc.getImageDescriptor() // large icon
);
-
+
group.add(entry);
}
-
+
paletteRoot.add(group);
}
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiContentOutlinePage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiContentOutlinePage.java
index 62abd5f..8e15681 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiContentOutlinePage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiContentOutlinePage.java
@@ -67,29 +67,31 @@
/**
* Implementation of the {@link ContentOutlinePage} to display {@link UiElementNode}.
+ *
+ * @since GLE1
*/
class UiContentOutlinePage extends ContentOutlinePage {
- private AbstractGraphicalLayoutEditor mEditor;
-
+ private GraphicalLayoutEditor mEditor;
+
private Action mAddAction;
private Action mDeleteAction;
private Action mUpAction;
private Action mDownAction;
-
+
private UiOutlineActions mUiActions = new UiOutlineActions();
- public UiContentOutlinePage(AbstractGraphicalLayoutEditor editor, final EditPartViewer viewer) {
+ public UiContentOutlinePage(GraphicalLayoutEditor editor, final EditPartViewer viewer) {
super(viewer);
mEditor = editor;
IconFactory factory = IconFactory.getInstance();
-
+
mAddAction = new Action("Add...") {
@Override
public void run() {
List<UiElementNode> nodes = getModelSelections();
UiElementNode node = nodes != null && nodes.size() > 0 ? nodes.get(0) : null;
-
+
mUiActions.doAdd(node, viewer.getControl().getShell());
}
};
@@ -100,7 +102,7 @@
@Override
public void run() {
List<UiElementNode> nodes = getModelSelections();
-
+
mUiActions.doRemove(nodes, viewer.getControl().getShell());
}
};
@@ -111,7 +113,7 @@
@Override
public void run() {
List<UiElementNode> nodes = getModelSelections();
-
+
mUiActions.doUp(nodes);
}
};
@@ -122,7 +124,7 @@
@Override
public void run() {
List<UiElementNode> nodes = getModelSelections();
-
+
mUiActions.doDown(nodes);
}
};
@@ -138,7 +140,7 @@
addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
ISelection selection = event.getSelection();
-
+
// the selection is never empty. The least it'll contain is the
// UiDocumentTreeEditPart object.
if (selection instanceof StructuredSelection) {
@@ -162,7 +164,7 @@
}
});
}
-
+
/* (non-Javadoc)
* @see org.eclipse.ui.part.IPage#createControl(org.eclipse.swt.widgets.Composite)
@@ -184,7 +186,7 @@
/*
* (non-Javadoc)
* @see org.eclipse.ui.part.Page#setActionBars(org.eclipse.ui.IActionBars)
- *
+ *
* Called automatically after createControl
*/
@Override
@@ -195,7 +197,7 @@
toolBarManager.add(new Separator());
toolBarManager.add(mUpAction);
toolBarManager.add(mDownAction);
-
+
IMenuManager menuManager = actionBars.getMenuManager();
menuManager.add(mAddAction);
menuManager.add(mDeleteAction);
@@ -222,18 +224,19 @@
public Control getControl() {
return getViewer().getControl();
}
-
+
void setNewEditor(GraphicalLayoutEditor editor) {
mEditor = editor;
setupOutline();
}
-
+
void breakConnectionWithEditor() {
// unhook outline viewer
mEditor.getSelectionSynchronizer().removeViewer(getViewer());
}
-
+
private void setupOutline() {
+
getViewer().setEditDomain(mEditor.getEditDomain());
// hook outline viewer
@@ -255,13 +258,13 @@
*/
public void menuAboutToShow(IMenuManager manager) {
List<UiElementNode> selected = getModelSelections();
-
+
if (selected != null) {
doCreateMenuAction(manager, selected);
return;
}
doCreateMenuAction(manager, null /* ui_node */);
- }
+ }
});
Control control = getControl();
Menu contextMenu = menuManager.createContextMenu(control);
@@ -271,13 +274,13 @@
/**
* Adds the menu actions to the context menu when the given UI node is selected in
* the tree view.
- *
+ *
* @param manager The context menu manager
* @param selected The UI node selected in the tree. Can be null, in which case the root
* is to be modified.
*/
private void doCreateMenuAction(IMenuManager manager, List<UiElementNode> selected) {
-
+
if (selected != null) {
boolean hasXml = false;
for (UiElementNode uiNode : selected) {
@@ -323,14 +326,14 @@
if (selected != null) {
manager.add(mDeleteAction);
manager.add(new Separator());
-
+
manager.add(mUpAction);
manager.add(mDownAction);
}
if (selected != null && selected.size() == 1) {
manager.add(new Separator());
-
+
Action propertiesAction = new Action("Properties") {
@Override
public void run() {
@@ -344,7 +347,7 @@
}
/**
- * Updates the outline view with the model of the {@link GraphicalLayoutEditor}.
+ * Updates the outline view with the model of the {@link IGraphicalLayoutEditor}.
* <p/>
* This attemps to preserve the selection, if any.
*/
@@ -380,22 +383,22 @@
ISelection selection = getSelection();
if (selection instanceof StructuredSelection) {
StructuredSelection structuredSelection = (StructuredSelection)selection;
-
+
if (structuredSelection.size() > 0) {
ArrayList<UiElementTreeEditPart> selected = new ArrayList<UiElementTreeEditPart>();
-
+
for (Iterator it = structuredSelection.iterator(); it.hasNext(); ) {
Object selectedObj = it.next();
-
+
if (selectedObj instanceof UiElementTreeEditPart) {
selected.add((UiElementTreeEditPart) selectedObj);
}
}
-
+
return selected.size() > 0 ? selected : null;
}
}
-
+
return null;
}
@@ -412,16 +415,16 @@
if (parts != null) {
ArrayList<UiElementNode> selected = new ArrayList<UiElementNode>();
-
+
for (UiElementTreeEditPart part : parts) {
if (part instanceof UiViewTreeEditPart || part instanceof UiLayoutTreeEditPart) {
selected.add((UiElementNode) part.getModel());
}
}
-
+
return selected.size() > 0 ? selected : null;
}
-
+
return null;
}
@@ -440,17 +443,17 @@
}
}
- /**
+ /**
* Selects the corresponding model element in the tree viewer.
*/
private void setModelSelection(UiElementNode uiNodeToSelect) {
if (uiNodeToSelect != null) {
-
+
// find an edit part that has the requested model element
UiElementTreeEditPart part = findPartForModel(
(UiElementTreeEditPart) getViewer().getContents(),
uiNodeToSelect);
-
+
// if we found a part, select it and reveal it
if (part != null) {
setViewerSelection(part);
@@ -461,7 +464,7 @@
/**
* Utility method that tries to find an edit part that matches a given model UI node.
- *
+ *
* @param rootPart The root of the viewer edit parts
* @param uiNode The UI node model to find
* @return The part that matches the model or null if it's not in the sub tree.
@@ -471,7 +474,7 @@
if (rootPart.getModel() == uiNode) {
return rootPart;
}
-
+
for (Object part : rootPart.getChildren()) {
if (part instanceof UiElementTreeEditPart) {
UiElementTreeEditPart found = findPartForModel(
@@ -492,16 +495,16 @@
*/
private void setupTooltip() {
final Tree tree = (Tree) getControl();
-
+
/*
- * Reference:
+ * Reference:
* http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet125.java?view=markup
*/
-
+
final Listener listener = new Listener() {
Shell tip = null;
Label label = null;
-
+
public void handleEvent(Event event) {
switch(event.type) {
case SWT.Dispose:
@@ -523,7 +526,7 @@
}
String tooltip = null;
-
+
TreeItem item = tree.getItem(new Point(event.x, event.y));
if (item != null) {
Object data = item.getData();
@@ -540,12 +543,12 @@
tooltip = item.getText() + ":\r" + tooltip;
}
}
-
-
+
+
if (tooltip != null) {
Shell shell = tree.getShell();
Display display = tree.getDisplay();
-
+
tip = new Shell(shell, SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);
tip.setBackground(display .getSystemColor(SWT.COLOR_INFO_BACKGROUND));
FillLayout layout = new FillLayout();
@@ -567,7 +570,7 @@
}
}
};
-
+
tree.addListener(SWT.Dispose, listener);
tree.addListener(SWT.KeyDown, listener);
tree.addListener(SWT.MouseMove, listener);
@@ -592,7 +595,7 @@
}
// ---------------
-
+
private class UiOutlineActions extends UiActions {
@Override
@@ -610,6 +613,6 @@
public void commitPendingXmlChanges() {
// Pass. There is nothing to commit before the XML is changed here.
}
-
+
}
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiPropertySheetPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiPropertySheetPage.java
index 33d2ed4..43b73b0 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiPropertySheetPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiPropertySheetPage.java
@@ -35,10 +35,12 @@
* A customized property sheet page for the graphical layout editor.
* <p/>
* Currently it just provides a custom tooltip to display attributes javadocs.
+ *
+ * @since GLE1
*/
public class UiPropertySheetPage extends PropertySheetPage {
-
+
public UiPropertySheetPage() {
super();
}
@@ -46,7 +48,7 @@
@Override
public void createControl(Composite parent) {
super.createControl(parent);
-
+
setupTooltip();
}
@@ -59,14 +61,14 @@
final Tree tree = (Tree) getControl();
/*
- * Reference:
+ * Reference:
* http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet125.java?view=markup
*/
final Listener listener = new Listener() {
Shell tip = null;
Label label = null;
-
+
public void handleEvent(Event event) {
switch(event.type) {
case SWT.Dispose:
@@ -88,7 +90,7 @@
}
String tooltip = null;
-
+
TreeItem item = tree.getItem(new Point(event.x, event.y));
if (item != null) {
Object data = item.getData();
@@ -102,11 +104,11 @@
tooltip = item.getText() + ":\r" + tooltip;
}
}
-
+
if (tooltip != null) {
Shell shell = tree.getShell();
Display display = tree.getDisplay();
-
+
tip = new Shell(shell, SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);
tip.setBackground(display .getSystemColor(SWT.COLOR_INFO_BACKGROUND));
FillLayout layout = new FillLayout();
@@ -128,7 +130,7 @@
}
}
};
-
+
tree.addListener(SWT.Dispose, listener);
tree.addListener(SWT.KeyDown, listener);
tree.addListener(SWT.MouseMove, listener);
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java
new file mode 100644
index 0000000..4ed96ae
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java
@@ -0,0 +1,729 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.ide.eclipse.adt.internal.editors.layout.configuration;
+
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.resources.ResourceType;
+import com.android.ide.eclipse.adt.internal.resources.configurations.CountryCodeQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
+import com.android.ide.eclipse.adt.internal.resources.configurations.LanguageQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.NetworkCodeQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.RegionQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenDimensionQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.VersionQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier.ScreenOrientation;
+import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
+import com.android.ide.eclipse.adt.internal.sdk.DeviceConfiguration;
+import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.LanguageRegionVerifier;
+import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.MobileCodeVerifier;
+import com.android.layoutlib.api.IResourceValue;
+import com.android.layoutlib.api.IStyleResourceValue;
+
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+
+/**
+ * A composite that displays the current configuration displayed in a Graphical Layout Editor.
+ */
+public class ConfigurationComposite extends Composite {
+
+ private final static String THEME_SEPARATOR = "----------"; //$NON-NLS-1$
+
+ private Text mCountry;
+ private Text mNetwork;
+ private Combo mLocale;
+ private Combo mDeviceList;
+ private Combo mDeviceConfigs;
+ private Combo mThemeCombo;
+ private Button mCreateButton;
+
+ private Label mCountryIcon;
+ private Label mNetworkIcon;
+
+ private Label mCurrentLayoutLabel;
+
+ private Image mMatchImage;
+ private Image mErrorImage;
+
+ private int mPlatformThemeCount = 0;
+ private boolean mDisableUpdates = false;
+
+ /** The {@link FolderConfiguration} representing the state of the UI controls */
+ private final FolderConfiguration mCurrentConfig = new FolderConfiguration();
+
+ private DeviceConfiguration[] mDevices;
+
+ private final ArrayList<ResourceQualifier[] > mLocaleList =
+ new ArrayList<ResourceQualifier[]>();
+
+ private final IConfigListener mListener;
+
+ /**
+ * Interface implemented by the part which owns a {@link ConfigurationComposite}.
+ * This notifies the owners when the configuration change.
+ * The owner must also provide methods to provide the configuration that will
+ * be displayed.
+ */
+ public interface IConfigListener {
+ void onConfigurationChange();
+ void onThemeChange();
+ void onCreate();
+
+ ProjectResources getProjectResources();
+ ProjectResources getFrameworkResources();
+ Map<String, Map<String, IResourceValue>> getConfiguredProjectResources();
+ Map<String, Map<String, IResourceValue>> getConfiguredFrameworkResources();
+ }
+
+ public ConfigurationComposite(IConfigListener listener, Composite parent, int style) {
+ super(parent, style);
+ mListener = listener;
+ mDevices = DeviceConfiguration.getDevices();
+
+ IconFactory factory = IconFactory.getInstance();
+ mMatchImage = factory.getIcon("match"); //$NON-NLS-1$
+ mErrorImage = factory.getIcon("error"); //$NON-NLS-1$
+
+ GridLayout gl;
+ GridData gd;
+ int cols = 10;
+
+ setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ setLayout(gl = new GridLayout(cols, false));
+
+ new Label(this, SWT.NONE).setText("MCC");
+ mCountryIcon = createControlComposite(this, true /* grab_horizontal */);
+ mCountry = new Text(mCountryIcon.getParent(), SWT.BORDER);
+ mCountry.setLayoutData(new GridData(
+ GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
+ mCountry.addVerifyListener(new MobileCodeVerifier());
+ mCountry.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onCountryCodeChange();
+ }
+ });
+ mCountry.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ onCountryCodeChange();
+ }
+ });
+
+ new Label(this, SWT.NONE).setText("MNC");
+ mNetworkIcon = createControlComposite(this, true /* grab_horizontal */);
+ mNetwork = new Text(mNetworkIcon.getParent(), SWT.BORDER);
+ mNetwork.setLayoutData(new GridData(
+ GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
+ mNetwork.addVerifyListener(new MobileCodeVerifier());
+ mNetwork.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onNetworkCodeChange();
+ }
+ });
+ mNetwork.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ onNetworkCodeChange();
+ }
+ });
+
+ new Label(this, SWT.NONE).setText("Locale");
+ mLocale = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ mLocale.setLayoutData(new GridData(
+ GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
+ mLocale.addVerifyListener(new LanguageRegionVerifier());
+ mLocale.addSelectionListener(new SelectionListener() {
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onLocaleChange();
+ }
+ public void widgetSelected(SelectionEvent e) {
+ onLocaleChange();
+ }
+ });
+
+ new Label(this, SWT.NONE).setText("Devices");
+ mDeviceList = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ // fill with the devices
+ for (DeviceConfiguration device : mDevices) {
+ mDeviceList.add(device.getName());
+ }
+
+ mDeviceList.select(0);
+ mDeviceList.setLayoutData(new GridData(
+ GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
+ mDeviceList.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onDeviceChange();
+ }
+ });
+
+ new Label(this, SWT.NONE).setText("Config");
+ mDeviceConfigs = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ Map<String, FolderConfiguration> configs = mDevices[0].getConfigs();
+ Set<String> configNames = configs.keySet();
+ for (String name : configNames) {
+ mDeviceConfigs.add(name);
+ }
+ mDeviceConfigs.select(0);
+ if (configNames.size() == 1) {
+ mDeviceConfigs.setEnabled(false);
+ }
+ mDeviceConfigs.setLayoutData(new GridData(
+ GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
+ mDeviceConfigs.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onDeviceConfigChange();
+ }
+ });
+
+ Composite labelParent = new Composite(this, SWT.NONE);
+ labelParent.setLayout(gl = new GridLayout(8, false));
+ gl.marginWidth = gl.marginHeight = 0;
+ labelParent.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
+ gd.horizontalSpan = cols;
+
+ new Label(labelParent, SWT.NONE).setText("Editing config:");
+ mCurrentLayoutLabel = new Label(labelParent, SWT.NONE);
+ mCurrentLayoutLabel.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
+ gd.widthHint = 50;
+
+ // first separator
+ Label separator = new Label(labelParent, SWT.SEPARATOR | SWT.VERTICAL);
+ separator.setLayoutData(gd = new GridData(
+ GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL));
+ gd.heightHint = 0;
+
+ mThemeCombo = new Combo(labelParent, SWT.READ_ONLY | SWT.DROP_DOWN);
+ mThemeCombo.setEnabled(false);
+ updateUIFromResources();
+ mThemeCombo.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onThemeChange();
+ }
+ });
+
+ // second separator
+ separator = new Label(labelParent, SWT.SEPARATOR | SWT.VERTICAL);
+ separator.setLayoutData(gd = new GridData(
+ GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL));
+ gd.heightHint = 0;
+
+ mCreateButton = new Button(labelParent, SWT.PUSH | SWT.FLAT);
+ mCreateButton.setText("Create...");
+ mCreateButton.setEnabled(false);
+ mCreateButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if (mListener != null) {
+ mListener.onCreate();
+ }
+ }
+ });
+
+ onDeviceConfigChange();
+ }
+
+ public void setConfig(FolderConfiguration config) {
+// mCurrentConfig.set(config);
+ throw new UnsupportedOperationException("setConfig");
+ }
+
+ public FolderConfiguration getCurrentConfig() {
+ return mCurrentConfig;
+ }
+
+ public void getCurrentConfig(FolderConfiguration config) {
+ config.set(mCurrentConfig);
+ }
+
+ public Rectangle getScreenBounds() {
+ // get the orientation from the current device config
+ ScreenOrientationQualifier qual = mCurrentConfig.getScreenOrientationQualifier();
+ ScreenOrientation orientation = qual.getValue();
+
+ // get the device screen dimension
+ ScreenDimensionQualifier qual2 = mCurrentConfig.getScreenDimensionQualifier();
+ int s1 = qual2.getValue1();
+ int s2 = qual2.getValue2();
+
+ switch (orientation) {
+ default:
+ case PORTRAIT:
+ return new Rectangle(0, 0, s2, s1);
+ case LANDSCAPE:
+ return new Rectangle(0, 0, s1, s2);
+ case SQUARE:
+ return new Rectangle(0, 0, s1, s1);
+ }
+ }
+
+ /**
+ * Updates the UI from values in the resources, such as languages, regions, themes, etc...
+ * This must be called from the UI thread.
+ */
+ public void updateUIFromResources() {
+ if (mListener == null) {
+ return; // can't do anything w/o it.
+ }
+
+ ProjectResources frameworkProject = mListener.getFrameworkResources();
+
+ mDisableUpdates = true;
+
+ // Reset stuff
+ int selection = mThemeCombo.getSelectionIndex();
+ mThemeCombo.removeAll();
+ mPlatformThemeCount = 0;
+
+ mLocale.removeAll();
+ mLocaleList.clear();
+
+ SortedSet<String> languages = null;
+ ArrayList<String> themes = new ArrayList<String>();
+
+ // get the themes, and languages from the Framework.
+ if (frameworkProject != null) {
+ // get the configured resources for the framework
+ Map<String, Map<String, IResourceValue>> frameworResources =
+ mListener.getConfiguredFrameworkResources();
+
+ if (frameworResources != null) {
+ // get the styles.
+ Map<String, IResourceValue> styles = frameworResources.get(
+ ResourceType.STYLE.getName());
+
+
+ // collect the themes out of all the styles.
+ for (IResourceValue value : styles.values()) {
+ String name = value.getName();
+ if (name.startsWith("Theme.") || name.equals("Theme")) {
+ themes.add(value.getName());
+ mPlatformThemeCount++;
+ }
+ }
+
+ // sort them and add them to the combo
+ Collections.sort(themes);
+
+ for (String theme : themes) {
+ mThemeCombo.add(theme);
+ }
+
+ mPlatformThemeCount = themes.size();
+ themes.clear();
+ }
+ }
+
+ // now get the themes and languages from the project.
+ ProjectResources project = mListener.getProjectResources();
+ // in cases where the opened file is not linked to a project, this could be null.
+ if (project != null) {
+ // get the configured resources for the project
+ Map<String, Map<String, IResourceValue>> configuredProjectRes =
+ mListener.getConfiguredProjectResources();
+
+ if (configuredProjectRes != null) {
+ // get the styles.
+ Map<String, IResourceValue> styleMap = configuredProjectRes.get(
+ ResourceType.STYLE.getName());
+
+ if (styleMap != null) {
+ // collect the themes out of all the styles, ie styles that extend,
+ // directly or indirectly a platform theme.
+ for (IResourceValue value : styleMap.values()) {
+ if (isTheme(value, styleMap)) {
+ themes.add(value.getName());
+ }
+ }
+
+ // sort them and add them the to the combo.
+ if (mPlatformThemeCount > 0 && themes.size() > 0) {
+ mThemeCombo.add(THEME_SEPARATOR);
+ }
+
+ Collections.sort(themes);
+
+ for (String theme : themes) {
+ mThemeCombo.add(theme);
+ }
+ }
+ }
+
+ // now get the languages from the project.
+ languages = project.getLanguages();
+ }
+
+ // add the languages to the Combo
+ mLocale.add("Default");
+ mLocaleList.add(new ResourceQualifier[] { null, null });
+
+ if (project != null && languages != null && languages.size() > 0) {
+ for (String language : languages) {
+ // first the language alone
+ mLocale.add(language);
+ LanguageQualifier qual = new LanguageQualifier(language);
+ mLocaleList.add(new ResourceQualifier[] { qual, null });
+
+ // now find the matching regions and add them
+ SortedSet<String> regions = project.getRegions(language);
+ for (String region : regions) {
+ mLocale.add(String.format("%1$s_%2$s", language, region)); //$NON-NLS-1$
+ RegionQualifier qual2 = new RegionQualifier(region);
+ mLocaleList.add(new ResourceQualifier[] { qual, qual2 });
+ }
+
+ }
+ }
+
+ mDisableUpdates = false;
+
+ // handle default selection of themes
+ if (mThemeCombo.getItemCount() > 0) {
+ mThemeCombo.setEnabled(true);
+ if (selection == -1) {
+ selection = 0;
+ }
+
+ if (mThemeCombo.getItemCount() <= selection) {
+ mThemeCombo.select(0);
+ } else {
+ mThemeCombo.select(selection);
+ }
+ } else {
+ mThemeCombo.setEnabled(false);
+ }
+
+ mThemeCombo.getParent().layout();
+ }
+
+ /**
+ * Returns the current theme, or null if the combo has no selection.
+ */
+ public String getTheme() {
+ int themeIndex = mThemeCombo.getSelectionIndex();
+ if (themeIndex != -1) {
+ return mThemeCombo.getItem(themeIndex);
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns whether the current theme selection is a project theme.
+ * <p/>The returned value is meaningless if {@link #getTheme()} returns <code>null</code>.
+ * @return true for project theme, false for framework theme
+ */
+ public boolean isProjectTheme() {
+ return mThemeCombo.getSelectionIndex() >= mPlatformThemeCount;
+ }
+
+
+ public void setEnabledCreate(boolean enabled) {
+ mCreateButton.setEnabled(enabled);
+ }
+
+ /**
+ * Update the UI controls state with a given {@link FolderConfiguration}.
+ * <p/>If <var>force</var> is set to <code>true</code> the UI will be changed to exactly reflect
+ * <var>config</var>, otherwise, if a qualifier is not present in <var>config</var>,
+ * the UI control is not modified. However if the value in the control is not the default value,
+ * a warning icon is shown.
+ * @param config The {@link FolderConfiguration} to set.
+ * @param force Whether the UI should be changed to exactly match the received configuration.
+ */
+ public void setConfiguration(FolderConfiguration config, boolean force) {
+ mDisableUpdates = true; // we do not want to trigger onXXXChange when setting new values in the widgets.
+
+ // TODO: find a device that can display this particular config or create a custom one if needed.
+
+
+ // update the string showing the folder name
+ String current = config.toDisplayString();
+ mCurrentLayoutLabel.setText(current != null ? current : "(Default)");
+
+ mDisableUpdates = false;
+ }
+
+ private void onCountryCodeChange() {
+ // because mCountry triggers onCountryCodeChange at each modification, calling setText()
+ // will trigger notifications, and we don't want that.
+ if (mDisableUpdates == true) {
+ return;
+ }
+
+ // update the current config
+ String value = mCountry.getText();
+
+ // empty string, means no qualifier.
+ if (value.length() == 0) {
+ mCurrentConfig.setCountryCodeQualifier(null);
+ } else {
+ try {
+ CountryCodeQualifier qualifier = CountryCodeQualifier.getQualifier(
+ CountryCodeQualifier.getFolderSegment(Integer.parseInt(value)));
+ if (qualifier != null) {
+ mCurrentConfig.setCountryCodeQualifier(qualifier);
+ } else {
+ // Failure! Looks like the value is wrong (for instance a one letter string).
+ // We do nothing in this case.
+ mCountryIcon.setImage(mErrorImage);
+ return;
+ }
+ } catch (NumberFormatException e) {
+ // Looks like the code is not a number. This should not happen since the text
+ // field has a VerifyListener that prevents it.
+ mCurrentConfig.setCountryCodeQualifier(null);
+ mCountryIcon.setImage(mErrorImage);
+ }
+ }
+
+ if (mListener != null) {
+ mListener.onConfigurationChange();
+ }
+ }
+
+ private void onNetworkCodeChange() {
+ // because mNetwork triggers onNetworkCodeChange at each modification, calling setText()
+ // will trigger notifications, and we don't want that.
+ if (mDisableUpdates == true) {
+ return;
+ }
+
+ // update the current config
+ String value = mNetwork.getText();
+
+ // empty string, means no qualifier.
+ if (value.length() == 0) {
+ mCurrentConfig.setNetworkCodeQualifier(null);
+ } else {
+ try {
+ NetworkCodeQualifier qualifier = NetworkCodeQualifier.getQualifier(
+ NetworkCodeQualifier.getFolderSegment(Integer.parseInt(value)));
+ if (qualifier != null) {
+ mCurrentConfig.setNetworkCodeQualifier(qualifier);
+ } else {
+ // Failure! Looks like the value is wrong (for instance a one letter string).
+ // We do nothing in this case.
+ mNetworkIcon.setImage(mErrorImage);
+ return;
+ }
+ } catch (NumberFormatException e) {
+ // Looks like the code is not a number. This should not happen since the text
+ // field has a VerifyListener that prevents it.
+ mCurrentConfig.setNetworkCodeQualifier(null);
+ mNetworkIcon.setImage(mErrorImage);
+ }
+ }
+
+ if (mListener != null) {
+ mListener.onConfigurationChange();
+ }
+ }
+
+ /**
+ * Call back for language combo selection
+ */
+ private void onLocaleChange() {
+ // because mLanguage triggers onLanguageChange at each modification, the filling
+ // of the combo with data will trigger notifications, and we don't want that.
+ if (mDisableUpdates == true) {
+ return;
+ }
+
+ int localeIndex = mLocale.getSelectionIndex();
+ ResourceQualifier[] localeQualifiers = mLocaleList.get(localeIndex);
+
+ mCurrentConfig.setLanguageQualifier((LanguageQualifier)localeQualifiers[0]); // language
+ mCurrentConfig.setRegionQualifier((RegionQualifier)localeQualifiers[1]); // region
+
+ if (mListener != null) {
+ mListener.onConfigurationChange();
+ }
+ }
+
+ private void onDeviceChange() {
+
+ int deviceIndex = mDeviceList.getSelectionIndex();
+ DeviceConfiguration device = mDevices[deviceIndex];
+
+ mDeviceConfigs.removeAll();
+
+ Set<String> configNames = device.getConfigs().keySet();
+ for (String name : configNames) {
+ mDeviceConfigs.add(name);
+ }
+
+ mDeviceConfigs.select(0);
+ if (configNames.size() == 1) {
+ mDeviceConfigs.setEnabled(false);
+ }
+
+ onDeviceConfigChange();
+ }
+
+ private void onDeviceConfigChange() {
+ int deviceIndex = mDeviceList.getSelectionIndex();
+ DeviceConfiguration device = mDevices[deviceIndex];
+
+ int configIndex = mDeviceConfigs.getSelectionIndex();
+ String name = mDeviceConfigs.getItem(configIndex);
+ FolderConfiguration config = device.getConfigs().get(name);
+
+ // get the current qualifiers from the current config
+ CountryCodeQualifier mcc = mCurrentConfig.getCountryCodeQualifier();
+ NetworkCodeQualifier mnc = mCurrentConfig.getNetworkCodeQualifier();
+ LanguageQualifier lang = mCurrentConfig.getLanguageQualifier();
+ RegionQualifier region = mCurrentConfig.getRegionQualifier();
+ VersionQualifier version = mCurrentConfig.getVersionQualifier();
+
+ // replace the config with the one from the device
+ mCurrentConfig.set(config);
+
+ // and put back the rest of the qualifiers
+ mCurrentConfig.addQualifier(mcc);
+ mCurrentConfig.addQualifier(mnc);
+ mCurrentConfig.addQualifier(lang);
+ mCurrentConfig.addQualifier(region);
+ mCurrentConfig.addQualifier(version);
+
+ if (mListener != null) {
+ mListener.onConfigurationChange();
+ }
+ }
+
+ private void onThemeChange() {
+ int themeIndex = mThemeCombo.getSelectionIndex();
+ if (themeIndex != -1) {
+ String theme = mThemeCombo.getItem(themeIndex);
+
+ if (theme.equals(THEME_SEPARATOR)) {
+ mThemeCombo.select(0);
+ }
+
+ if (mListener != null) {
+ mListener.onThemeChange();
+ }
+ }
+ }
+
+ /**
+ * Creates a composite with no margin/spacing, and puts a {@link Label} in it with the matching
+ * icon.
+ * @param parent the parent to receive the composite
+ * @return the created {@link Label} object.
+ */
+ private Label createControlComposite(Composite parent, boolean grab) {
+ GridLayout gl;
+
+ Composite composite = new Composite(parent, SWT.NONE);
+ composite.setLayout(gl = new GridLayout(2, false));
+ gl.marginHeight = gl.marginWidth = 0;
+ gl.horizontalSpacing = 0;
+ if (grab) {
+ composite.setLayoutData(
+ new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
+ }
+
+ // create the label
+ Label icon = new Label(composite, SWT.NONE);
+ icon.setImage(mMatchImage);
+
+ return icon;
+ }
+
+ /**
+ * Returns whether the given <var>style</var> is a theme.
+ * This is done by making sure the parent is a theme.
+ * @param value the style to check
+ * @param styleMap the map of styles for the current project. Key is the style name.
+ * @return True if the given <var>style</var> is a theme.
+ */
+ private boolean isTheme(IResourceValue value, Map<String, IResourceValue> styleMap) {
+ if (value instanceof IStyleResourceValue) {
+ IStyleResourceValue style = (IStyleResourceValue)value;
+
+ boolean frameworkStyle = false;
+ String parentStyle = style.getParentStyle();
+ if (parentStyle == null) {
+ // if there is no specified parent style we look an implied one.
+ // For instance 'Theme.light' is implied child style of 'Theme',
+ // and 'Theme.light.fullscreen' is implied child style of 'Theme.light'
+ String name = style.getName();
+ int index = name.lastIndexOf('.');
+ if (index != -1) {
+ parentStyle = name.substring(0, index);
+ }
+ } else {
+ // remove the useless @ if it's there
+ if (parentStyle.startsWith("@")) {
+ parentStyle = parentStyle.substring(1);
+ }
+
+ // check for framework identifier.
+ if (parentStyle.startsWith("android:")) {
+ frameworkStyle = true;
+ parentStyle = parentStyle.substring("android:".length());
+ }
+
+ // at this point we could have the format style/<name>. we want only the name
+ if (parentStyle.startsWith("style/")) {
+ parentStyle = parentStyle.substring("style/".length());
+ }
+ }
+
+ if (parentStyle != null) {
+ if (frameworkStyle) {
+ // if the parent is a framework style, it has to be 'Theme' or 'Theme.*'
+ return parentStyle.equals("Theme") || parentStyle.startsWith("Theme.");
+ } else {
+ // if it's a project style, we check this is a theme.
+ value = styleMap.get(parentStyle);
+ if (value != null) {
+ return isTheme(value, styleMap);
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/DropFeedback.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/DropFeedback.java
index 327b53c..d75e016 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/DropFeedback.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/DropFeedback.java
@@ -4,7 +4,7 @@
* Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
*
* Unless required by applicable law or agreed to in writing, software
@@ -35,6 +35,8 @@
* This class uses some temporary static storage to avoid excessive allocations during
* drop operations. It is expected to only be invoked from the main UI thread with no
* concurrent access.
+ *
+ * @since GLE1
*/
class DropFeedback {
@@ -43,12 +45,12 @@
private static final int BOTTOM = 2;
private static final int RIGHT = 3;
private static final int MAX_DIR = RIGHT;
-
+
private static final int sOppositeDirection[] = { BOTTOM, RIGHT, TOP, LEFT };
private static final UiElementEditPart sTempClosests[] = new UiElementEditPart[4];
private static final int sTempMinDists[] = new int[4];
-
+
/**
* Target information computed from a drop on a RelativeLayout.
@@ -69,20 +71,20 @@
private static final RelativeInfo sRelativeInfo = new RelativeInfo();
/** A temporary array of 2 {@link UiElementEditPart} to avoid allocations. */
private static final UiElementEditPart sTempTwoParts[] = new UiElementEditPart[2];
-
+
private DropFeedback() {
}
-
+
//----- Package methods called by users of this helper class -----
-
-
+
+
/**
* This method is used by {@link ElementCreateCommand#execute()} when a new item
* needs to be "dropped" in the current XML document. It creates the new item using
* the given descriptor as a child of the given parent part.
- *
+ *
* @param parentPart The parent part.
* @param descriptor The descriptor for the new XML element.
* @param where The drop location (in parent coordinates)
@@ -91,16 +93,16 @@
static void addElementToXml(UiElementEditPart parentPart,
ElementDescriptor descriptor, Point where,
UiEditorActions actions) {
-
+
String layoutXmlName = getXmlLocalName(parentPart);
RelativeInfo info = null;
UiElementEditPart sibling = null;
-
+
// TODO consider merge like a vertical layout
// TODO consider TableLayout like a linear
if (LayoutConstants.LINEAR_LAYOUT.equals(layoutXmlName)) {
sibling = findLinearTarget(parentPart, where)[1];
-
+
} else if (LayoutConstants.RELATIVE_LAYOUT.equals(layoutXmlName)) {
info = findRelativeTarget(parentPart, where, sRelativeInfo);
if (info != null) {
@@ -114,7 +116,7 @@
UiElementNode uiParent = parentPart.getUiNode();
UiElementNode uiNode = actions.addElement(uiParent, uiSibling, descriptor,
false /*updateLayout*/);
-
+
if (LayoutConstants.ABSOLUTE_LAYOUT.equals(layoutXmlName)) {
adjustAbsoluteAttributes(uiNode, where);
} else if (LayoutConstants.RELATIVE_LAYOUT.equals(layoutXmlName)) {
@@ -128,8 +130,8 @@
* highlight information when a drop target is moved over a valid drop area.
* <p/>
* Since there are no "out" parameters in Java, all the information is returned
- * via the {@link HighlightInfo} structure passed as parameter.
- *
+ * via the {@link HighlightInfo} structure passed as parameter.
+ *
* @param parentPart The parent part, always a layout.
* @param highlightInfo A structure where result is stored to perform highlight.
* @param where The target drop point, in parent's coordinates
@@ -139,16 +141,16 @@
HighlightInfo highlightInfo,
Point where) {
String layoutType = getXmlLocalName(parentPart);
-
+
if (LayoutConstants.ABSOLUTE_LAYOUT.equals(layoutType)) {
highlightInfo.anchorPoint = where;
-
+
} else if (LayoutConstants.LINEAR_LAYOUT.equals(layoutType)) {
boolean isVertical = isVertical(parentPart);
highlightInfo.childParts = findLinearTarget(parentPart, where);
computeLinearLine(parentPart, isVertical, highlightInfo);
-
+
} else if (LayoutConstants.RELATIVE_LAYOUT.equals(layoutType)) {
RelativeInfo info = findRelativeTarget(parentPart, where, sRelativeInfo);
@@ -157,13 +159,13 @@
computeRelativeLine(parentPart, info, highlightInfo);
}
}
-
+
return highlightInfo;
}
-
-
+
+
//----- Misc utilities -----
-
+
/**
* Returns the next UI sibling of this part, i.e. the element which is just after in
* the UI/XML order in the same parent. Returns null if there's no such part.
@@ -208,7 +210,7 @@
/**
* Adjusts the attributes of a new node dropped in an AbsoluteLayout.
- *
+ *
* @param uiNode The new node being dropped.
* @param where The drop location (in parent coordinates)
*/
@@ -237,12 +239,12 @@
* will "attach". The anchor part can be null, either because the layout is currently
* empty or the user is attaching to an existing empty border.
* <li> direction: the direction from the anchor part to the drop point. That's also the
- * direction from the anchor part to the new part.
+ * direction from the anchor part to the new part.
* <li> the new node; it is created either after the anchor for right or top directions
- * or before the anchor for left or bottom directions. This means the new part can
- * reference the id of the anchor part.
+ * or before the anchor for left or bottom directions. This means the new part can
+ * reference the id of the anchor part.
* </ul>
- *
+ *
* Several cases:
* <ul>
* <li> set: layout_above/below/toLeftOf/toRightOf to point to the anchor.
@@ -251,7 +253,7 @@
* <li> copy: layout_above/below/toLeftOf/toRightOf for the orthogonal direction
* (i.e. top/bottom or left/right.)
* </ul>
- *
+ *
* @param uiNode The new node being dropped.
* @param info The context computed by {@link #findRelativeTarget(UiElementEditPart, Point, RelativeInfo)}.
*/
@@ -259,10 +261,10 @@
if (uiNode == null || info == null) {
return;
}
-
- final UiElementEditPart anchorPart = info.targetParts[info.anchorIndex]; // can be null
+
+ final UiElementEditPart anchorPart = info.targetParts[info.anchorIndex]; // can be null
final int direction = info.direction;
-
+
uiNode.getEditor().editXmlModel(new Runnable() {
public void run() {
HashMap<String, String> map = new HashMap<String, String>();
@@ -276,7 +278,7 @@
anchorId = DescriptorsUtils.getFreeWidgetId(anchorUiNode);
anchorUiNode.setAttributeValue("id", anchorId, true /*override*/); //$NON-NLS-1$
}
-
+
if (anchorId != null) {
switch(direction) {
case TOP:
@@ -315,7 +317,7 @@
map.put(LayoutConstants.ATTR_LAYOUT_ALIGN_BASELINE,
anchorUiNode.getAttributeValue(
LayoutConstants.ATTR_LAYOUT_ALIGN_BASELINE));
-
+
map.put(LayoutConstants.ATTR_LAYOUT_ABOVE,
anchorUiNode.getAttributeValue(LayoutConstants.ATTR_LAYOUT_ABOVE));
map.put(LayoutConstants.ATTR_LAYOUT_BELOW,
@@ -344,7 +346,7 @@
break;
}
}
-
+
for (Entry<String, String> entry : map.entrySet()) {
uiNode.setAttributeValue(entry.getKey(), entry.getValue(), true /* override */);
}
@@ -365,7 +367,7 @@
* <p/>
* It returns null if it can't be determined, in which case the element will be added at the
* end of the parent child list.
- *
+ *
* @return The edit parts that correspond to what will be the "prev" and "next sibling" of the
* new element. The previous sibling can be null if adding before the first element.
* The next sibling can be null if adding after the last element.
@@ -373,9 +375,9 @@
private static UiElementEditPart[] findLinearTarget(UiElementEditPart parent, Point point) {
// default orientation is horizontal
boolean isVertical = isVertical(parent);
-
+
int target = isVertical ? point.y : point.x;
-
+
UiElementEditPart prev = null;
UiElementEditPart next = null;
@@ -391,7 +393,7 @@
prev = childPart;
}
}
-
+
sTempTwoParts[0] = prev;
sTempTwoParts[1] = next;
return sTempTwoParts;
@@ -405,7 +407,7 @@
* The result is stored in HighlightInfo.
* <p/>
* Caller must clear the HighlightInfo as appropriate before this call.
- *
+ *
* @param parentPart The parent part, always a layout.
* @param isVertical True for vertical parts, thus computing an horizontal line.
* @param highlightInfo The in-out highlight info.
@@ -455,18 +457,18 @@
/**
* Returns true if the linear layout is marked as vertical.
- *
- * @param parent The a layout part that must be a LinearLayout
+ *
+ * @param parent The a layout part that must be a LinearLayout
* @return True if the linear layout has a vertical orientation attribute.
*/
private static boolean isVertical(UiElementEditPart parent) {
String orientation = parent.getStringAttr("orientation"); //$NON-NLS-1$
- boolean isVertical = "vertical".equals(orientation) || //$NON-NLS-1$
+ boolean isVertical = "vertical".equals(orientation) || //$NON-NLS-1$
"1".equals(orientation); //$NON-NLS-1$
return isVertical;
}
-
+
//----- RelativeLayout --------
/**
@@ -481,26 +483,26 @@
* <p/>
* If the drop point is not exactly on a current element, find the closest in each
* direction and align with the two closest of these.
- *
+ *
* @return null if we fail to find anything (such as there are currently no items to compare
* with); otherwise fills the {@link RelativeInfo} and return it.
*/
private static RelativeInfo findRelativeTarget(UiElementEditPart parent,
Point point,
RelativeInfo outInfo) {
-
+
for (int i = 0; i < 4; i++) {
sTempMinDists[i] = Integer.MAX_VALUE;
sTempClosests[i] = null;
}
-
+
for (Object child : parent.getChildren()) {
if (child instanceof UiElementEditPart) {
UiElementEditPart childPart = (UiElementEditPart) child;
Rectangle r = childPart.getBounds();
if (r.contains(point)) {
-
+
float rx = ((float)(point.x - r.x) / (float)r.width ) - 0.5f;
float ry = ((float)(point.y - r.y) / (float)r.height) - 0.5f;
@@ -537,18 +539,18 @@
return outInfo;
}
-
+
computeClosest(point, childPart, sTempClosests, sTempMinDists, TOP);
computeClosest(point, childPart, sTempClosests, sTempMinDists, LEFT);
computeClosest(point, childPart, sTempClosests, sTempMinDists, BOTTOM);
computeClosest(point, childPart, sTempClosests, sTempMinDists, RIGHT);
}
}
-
+
UiElementEditPart closest = null;
int minDist = Integer.MAX_VALUE;
int minDir = -1;
-
+
for (int i = 0; i <= MAX_DIR; i++) {
if (sTempClosests[i] != null && sTempMinDists[i] < minDist) {
closest = sTempClosests[i];
@@ -556,7 +558,7 @@
minDir = i;
}
}
-
+
if (closest != null) {
int index = 0;
switch(minDir) {
@@ -662,7 +664,7 @@
Point p = null;
boolean usable = false;
-
+
switch(direction) {
case TOP:
p = r.getBottom();
@@ -701,7 +703,7 @@
if (referencePart == null || referencePart.getParent() == null) {
return null;
}
-
+
Rectangle r = referencePart.getBounds();
Point ref = null;
switch(direction) {
@@ -718,16 +720,16 @@
ref = r.getRight();
break;
}
-
+
int minDist = Integer.MAX_VALUE;
UiElementEditPart closestPart = null;
-
+
for (Object childPart : referencePart.getParent().getChildren()) {
if (childPart != referencePart && childPart instanceof UiElementEditPart) {
r = ((UiElementEditPart) childPart).getBounds();
Point p = null;
boolean usable = false;
-
+
switch(direction) {
case TOP:
p = r.getBottom();
@@ -756,7 +758,7 @@
}
}
}
-
+
return closestPart;
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/ElementCreateCommand.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/ElementCreateCommand.java
index 0795b90..562570f 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/ElementCreateCommand.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/ElementCreateCommand.java
@@ -29,6 +29,8 @@
/**
* A command that knows how to instantiate a new element based on a given {@link ElementDescriptor},
* the parent {@link UiElementEditPart} and an optional target location.
+ *
+ * @since GLE1
*/
public class ElementCreateCommand extends Command {
@@ -41,7 +43,7 @@
/**
* Creates a new {@link ElementCreateCommand}.
- *
+ *
* @param descriptor Descriptor of the new element to create
* @param targetPart The edit part that hosts the new edit part
* @param targetPoint The drop location in parent coordinates
@@ -52,7 +54,7 @@
mParentPart = targetPart;
mTargetPoint = targetPoint;
}
-
+
// --- Methods inherited from Command ---
@Override
@@ -82,14 +84,14 @@
}
});
}
- }
+ }
}
@Override
public void redo() {
throw new UnsupportedOperationException("redo not supported by this command"); //$NON-NLS-1$
}
-
+
@Override
public void undo() {
throw new UnsupportedOperationException("undo not supported by this command"); //$NON-NLS-1$
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/ElementFigure.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/ElementFigure.java
index 2d82058..23c2c9e 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/ElementFigure.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/ElementFigure.java
@@ -22,11 +22,13 @@
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.SWT;
-
+
/**
* The figure used to draw basic elements.
* <p/>
* The figure is totally empty and transparent except for the selection border.
+ *
+ * @since GLE1
*/
class ElementFigure extends Figure {
@@ -36,18 +38,18 @@
public ElementFigure() {
setOpaque(false);
}
-
+
public void setSelected(boolean isSelected) {
if (isSelected != mIsSelected) {
mIsSelected = isSelected;
repaint();
}
}
-
+
@Override
public void setBounds(Rectangle rect) {
super.setBounds(rect);
-
+
mInnerBounds = getBounds().getCopy();
if (mInnerBounds.width > 0) {
mInnerBounds.width--;
@@ -56,11 +58,11 @@
mInnerBounds.height--;
}
}
-
+
public Rectangle getInnerBounds() {
return mInnerBounds;
}
-
+
@Override
protected void paintBorder(Graphics graphics) {
super.paintBorder(graphics);
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/LayoutFigure.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/LayoutFigure.java
index 4e5527f..6334751 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/LayoutFigure.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/LayoutFigure.java
@@ -30,11 +30,13 @@
* By default the figure is transparent and empty.
* The base {@link ElementFigure} knows how to draw the selection border.
* This figure knows how to draw the drop feedback.
+ *
+ * @since GLE1
*/
class LayoutFigure extends ElementFigure {
private HighlightInfo mHighlightInfo;
-
+
public LayoutFigure() {
super();
}
@@ -50,12 +52,12 @@
* The parent {@link Figure#paint(Graphics)} calls {@link #paintFigure(Graphics)} then
* {@link #paintClientArea(Graphics)} then {@link #paintBorder(Graphics)}. Here we thus
* draw the actual highlight border but also the highlight anchor lines and points so that
- * we can make sure they are all drawn on top of the border.
+ * we can make sure they are all drawn on top of the border.
* <p/>
* Note: This method doesn't really need to restore its graphic state. The parent
* Figure will do it for us.
* <p/>
- *
+ *
* @param graphics The Graphics object used for painting
*/
@Override
@@ -98,7 +100,7 @@
int y1 = mHighlightInfo.linePoints[0].y;
int x2 = mHighlightInfo.linePoints[1].x;
int y2 = mHighlightInfo.linePoints[1].y;
-
+
// if the line is right to the edge, draw it one pixel more inside so that the
// full 2-pixel width be visible.
if (x1 <= 0) x1++;
@@ -110,12 +112,12 @@
if (x2 >= w - 1) x2--;
if (y1 >= h - 1) y1--;
if (y2 >= h - 1) y2--;
-
+
x1 += bx;
x2 += bx;
y1 += by;
y2 += by;
-
+
graphics.setLineWidth(2);
graphics.setLineStyle(SWT.LINE_DASH);
graphics.setLineCap(SWT.CAP_ROUND);
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiDocumentEditPart.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiDocumentEditPart.java
index d12856c..9f6557c 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiDocumentEditPart.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiDocumentEditPart.java
@@ -46,27 +46,29 @@
/**
* Graphical edit part for the root document.
* <p/>
- * It acts as a simple container.
+ * It acts as a simple container.
+ *
+ * @since GLE1
*/
public class UiDocumentEditPart extends UiElementEditPart {
-
+
private Display mDisplay;
private FreeformLayer mLayer;
private ImageBackground mImage;
private Label mChild = null;
-
+
final static class ImageBackground extends AbstractBackground {
-
+
private BufferedImage mBufferedImage;
private Image mImage;
ImageBackground() {
}
-
+
ImageBackground(BufferedImage image, Display display) {
setImage(image, display);
}
-
+
@Override
public void paintBackground(IFigure figure, Graphics graphics, Insets insets) {
if (mImage != null) {
@@ -74,7 +76,7 @@
graphics.drawImage(mImage, rect.x, rect.y);
}
}
-
+
void setImage(BufferedImage image, Display display) {
if (image != null) {
int[] data = ((DataBufferInt)image.getData().getDataBuffer()).getData();
@@ -104,27 +106,27 @@
protected IFigure createFigure() {
mLayer = new FreeformLayer();
mLayer.setLayoutManager(new FreeformLayout());
-
+
mLayer.setOpaque(true);
mLayer.setBackgroundColor(ColorConstants.lightGray);
-
+
return mLayer;
}
-
+
@Override
protected void refreshVisuals() {
UiElementNode model = (UiElementNode)getModel();
-
+
Object editData = model.getEditData();
if (editData instanceof BufferedImage) {
BufferedImage image = (BufferedImage)editData;
-
+
if (mImage == null || image != mImage.getBufferedImage()) {
mImage = new ImageBackground(image, mDisplay);
}
-
+
mLayer.setBorder(mImage);
-
+
if (mChild != null && mChild.getParent() == mLayer) {
mLayer.remove(mChild);
}
@@ -156,7 +158,7 @@
protected void showSelection() {
// no selection at this level.
}
-
+
@Override
protected void createEditPolicies() {
super.createEditPolicies();
@@ -168,12 +170,12 @@
}
/**
- * Returns the EditPart that should be used as the target for the specified Request.
+ * Returns the EditPart that should be used as the target for the specified Request.
* For instance this is called during drag'n'drop with a CreateRequest.
* <p/>
* For the root document, we want the first child edit part to the be the target
* since an XML document can have only one root element.
- *
+ *
* {@inheritDoc}
*/
@Override
@@ -194,7 +196,7 @@
return null;
}
}
-
+
}
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiDocumentTreeEditPart.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiDocumentTreeEditPart.java
index 57db255..b78c519 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiDocumentTreeEditPart.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiDocumentTreeEditPart.java
@@ -22,13 +22,15 @@
/**
* Implementation of {@link UiElementTreeEditPart} for the document root.
+ *
+ * @since GLE1
*/
public class UiDocumentTreeEditPart extends UiElementTreeEditPart {
public UiDocumentTreeEditPart(UiDocumentNode model) {
super(model);
}
-
+
@SuppressWarnings("unchecked")
@Override
protected List getModelChildren() {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementEditPart.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementEditPart.java
index 94590c5..75c7b6d 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementEditPart.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementEditPart.java
@@ -44,10 +44,12 @@
/**
* An {@link EditPart} for a {@link UiElementNode}.
+ *
+ * @since GLE1
*/
public abstract class UiElementEditPart extends AbstractGraphicalEditPart
implements IUiUpdateListener {
-
+
public UiElementEditPart(UiElementNode uiElementNode) {
setModel(uiElementNode);
}
@@ -60,7 +62,7 @@
//-------------------------
// Base class overrides
-
+
@Override
public DragTracker getDragTracker(Request request) {
return new SelectEditPartTracker(this);
@@ -71,12 +73,12 @@
/*
* This is no longer needed, as a selection edit policy is set by the parent layout.
* Leave this code commented out right now, I'll want to play with this later.
- *
+ *
installEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE,
new NonResizableSelectionEditPolicy(this));
*/
}
-
+
/* (non-javadoc)
* Returns a List containing the children model objects.
* Must not return null, instead use the super which returns an empty list.
@@ -92,7 +94,7 @@
super.activate();
getUiNode().addUpdateListener(this);
}
-
+
@Override
public void deactivate() {
super.deactivate();
@@ -104,11 +106,11 @@
if (getFigure().getParent() != null) {
((GraphicalEditPart) getParent()).setLayoutConstraint(this, getFigure(), getBounds());
}
-
+
// update the visuals of the children as well
refreshChildrenVisuals();
}
-
+
protected void refreshChildrenVisuals() {
if (children != null) {
for (Object child : children) {
@@ -131,7 +133,7 @@
break;
case CHILDREN_CHANGED:
refreshChildren();
-
+
// new children list, need to update the layout
refreshVisuals();
break;
@@ -151,19 +153,19 @@
public final UiElementNode getUiNode() {
return (UiElementNode) getModel();
}
-
+
protected final ElementDescriptor getDescriptor() {
return getUiNode().getDescriptor();
}
-
+
protected final UiElementEditPart getEditPartParent() {
EditPart parent = getParent();
if (parent instanceof UiElementEditPart) {
- return (UiElementEditPart)parent;
+ return (UiElementEditPart)parent;
}
return null;
}
-
+
/**
* Returns a given XML attribute.
* @param attrName The local name of the attribute.
@@ -186,17 +188,17 @@
}
return null;
}
-
+
protected final Rectangle getBounds() {
UiElementNode model = (UiElementNode)getModel();
-
+
Object editData = model.getEditData();
if (editData != null) {
// assert with fully qualified class name to prevent import changes to another
// Rectangle class.
assert (editData instanceof org.eclipse.draw2d.geometry.Rectangle);
-
+
return (Rectangle)editData;
}
@@ -205,12 +207,12 @@
}
/**
- * Returns the EditPart that should be used as the target for the specified Request.
+ * Returns the EditPart that should be used as the target for the specified Request.
* <p/>
* For instance this is called during drag'n'drop with a CreateRequest.
* <p/>
* Reject being a target for elements which descriptor does not allow children.
- *
+ *
* {@inheritDoc}
*/
@Override
@@ -227,7 +229,7 @@
/**
* Used by derived classes {@link UiDocumentEditPart} and {@link UiLayoutEditPart}
* to accept drag'n'drop of new items from the palette.
- *
+ *
* @param layoutEditPart The layout edit part where this policy is installed. It can
* be either a {@link UiDocumentEditPart} or a {@link UiLayoutEditPart}.
*/
@@ -269,18 +271,18 @@
Point where = request.getLocation().getCopy();
Point origin = getLayoutContainer().getClientArea().getLocation();
where.translate(origin.getNegated());
-
+
// The host is the EditPart where this policy is installed,
// e.g. this UiElementEditPart.
EditPart host = getHost();
if (host instanceof UiElementEditPart) {
-
+
return new ElementCreateCommand((ElementDescriptor) newType,
(UiElementEditPart) host,
where);
}
}
-
+
return null;
}
@@ -289,14 +291,14 @@
// TODO Auto-generated method stub
return null;
}
-
+
@Override
public void showLayoutTargetFeedback(Request request) {
super.showLayoutTargetFeedback(request);
-
+
// for debugging
// System.out.println("target: " + request.toString() + " -- " + layoutEditPart.getUiNode().getBreadcrumbTrailDescription(false));
-
+
if (layoutEditPart instanceof UiLayoutEditPart &&
request instanceof DropRequest) {
Point where = ((DropRequest) request).getLocation().getCopy();
@@ -314,24 +316,24 @@
((UiLayoutEditPart) layoutEditPart).hideDropTarget();
}
}
-
+
@Override
protected IFigure createSizeOnDropFeedback(CreateRequest createRequest) {
// TODO understand if this is useful for us or remove
return super.createSizeOnDropFeedback(createRequest);
}
-
+
});
}
-
+
protected static class NonResizableSelectionEditPolicy extends SelectionEditPolicy {
-
+
private final UiElementEditPart mEditPart;
public NonResizableSelectionEditPolicy(UiElementEditPart editPart) {
mEditPart = editPart;
}
-
+
@Override
protected void hideSelection() {
mEditPart.hideSelection();
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementTreeEditPart.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementTreeEditPart.java
index 459919a..81737f8 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementTreeEditPart.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementTreeEditPart.java
@@ -25,6 +25,8 @@
/**
* Base {@link AbstractTreeEditPart} to represent {@link UiElementNode} objects in the
* {@link IContentOutlinePage} linked to the layout editor.
+ *
+ * @since GLE1
*/
public class UiElementTreeEditPart extends AbstractTreeEditPart {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementTreeEditPartFactory.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementTreeEditPartFactory.java
index 4c9eeb8..c10298c 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementTreeEditPartFactory.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementTreeEditPartFactory.java
@@ -26,7 +26,9 @@
/**
* {@link EditPartFactory} to create {@link AbstractTreeEditPart} for {@link UiElementNode} objects.
- * These objects are used in the {@link IContentOutlinePage} linked to the layout editor.
+ * These objects are used in the {@link IContentOutlinePage} linked to the layout editor.
+ *
+ * @since GLE1
*/
public class UiElementTreeEditPartFactory implements EditPartFactory {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementsEditPartFactory.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementsEditPartFactory.java
index 8fe4702..5f17937 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementsEditPartFactory.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementsEditPartFactory.java
@@ -28,15 +28,17 @@
* <p/>
* The only model objects we use are {@link UiElementNode} objects and they are
* edited using {@link UiElementEditPart}.
+ *
+ * @since GLE1
*/
public class UiElementsEditPartFactory implements EditPartFactory {
-
+
private Display mDisplay;
public UiElementsEditPartFactory(Display display) {
mDisplay = display;
}
-
+
public EditPart createEditPart(EditPart context, Object model) {
if (model instanceof UiDocumentNode) {
return new UiDocumentEditPart((UiDocumentNode) model, mDisplay);
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiLayoutEditPart.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiLayoutEditPart.java
index 2fe0a59..14d6edb 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiLayoutEditPart.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiLayoutEditPart.java
@@ -29,10 +29,12 @@
/**
* Graphical edit part for an {@link UiElementNode} that represents a ViewLayout.
* <p/>
- * It acts as a simple container.
+ * It acts as a simple container.
+ *
+ * @since GLE1
*/
public final class UiLayoutEditPart extends UiElementEditPart {
-
+
static class HighlightInfo {
public boolean drawDropBorder;
public UiElementEditPart[] childParts;
@@ -48,24 +50,24 @@
linePoints = null;
}
}
-
+
private final HighlightInfo mHighlightInfo = new HighlightInfo();
-
+
public UiLayoutEditPart(UiElementNode uiElementNode) {
super(uiElementNode);
}
-
+
@Override
protected void createEditPolicies() {
super.createEditPolicies();
-
+
installEditPolicy(EditPolicy.CONTAINER_ROLE, new ContainerEditPolicy() {
@Override
protected Command getCreateCommand(CreateRequest request) {
return null;
}
});
-
+
installLayoutEditPolicy(this);
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiLayoutTreeEditPart.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiLayoutTreeEditPart.java
index 09fdc86..5e78b46 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiLayoutTreeEditPart.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiLayoutTreeEditPart.java
@@ -22,13 +22,15 @@
/**
* Implementation of {@link UiElementTreeEditPart} for layout objects.
+ *
+ * @since GLE1
*/
public class UiLayoutTreeEditPart extends UiElementTreeEditPart {
public UiLayoutTreeEditPart(UiElementNode node) {
super(node);
}
-
+
@SuppressWarnings("unchecked")
@Override
protected List getModelChildren() {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiViewEditPart.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiViewEditPart.java
index e8a1b83..430e1ce 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiViewEditPart.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiViewEditPart.java
@@ -23,6 +23,8 @@
/**
* Graphical edit part for an {@link UiElementNode} that represents a View.
+ *
+ * @since GLE1
*/
public class UiViewEditPart extends UiElementEditPart {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiViewTreeEditPart.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiViewTreeEditPart.java
index b505ec0..22cc952 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiViewTreeEditPart.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiViewTreeEditPart.java
@@ -20,6 +20,8 @@
/**
* Implementation of {@link UiElementTreeEditPart} for view objects.
+ *
+ * @since GLE1
*/
public class UiViewTreeEditPart extends UiElementTreeEditPart {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/properties/AndroidPropertyPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/properties/AndroidPropertyPage.java
index 7dc4ae2..a88f864 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/properties/AndroidPropertyPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/properties/AndroidPropertyPage.java
@@ -18,7 +18,7 @@
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.sdklib.IAndroidTarget;
-import com.android.sdkuilib.internal.widgets.ApkConfigWidget;
+import com.android.sdklib.internal.project.ApkSettings;
import com.android.sdkuilib.internal.widgets.SdkTargetSelector;
import org.eclipse.core.resources.IProject;
@@ -27,14 +27,14 @@
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.IWorkbenchPropertyPage;
import org.eclipse.ui.dialogs.PropertyPage;
-import java.util.Map;
-
/**
* Property page for "Android" project.
* This is accessible from the Package Explorer when right clicking a project and choosing
@@ -45,7 +45,7 @@
private IProject mProject;
private SdkTargetSelector mSelector;
- private ApkConfigWidget mApkConfigWidget;
+ private Button mSplitByDensity;
public AndroidPropertyPage() {
// pass
@@ -72,13 +72,13 @@
mSelector = new SdkTargetSelector(top, targets);
- l = new Label(top, SWT.SEPARATOR | SWT.HORIZONTAL);
- l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ Group g = new Group(top, SWT.NONE);
+ g.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ g.setLayout(new GridLayout(1, false));
+ g.setText("APK Generation");
- l = new Label(top, SWT.NONE);
- l.setText("Project APK Configurations");
-
- mApkConfigWidget = new ApkConfigWidget(top);
+ mSplitByDensity = new Button(g, SWT.CHECK);
+ mSplitByDensity.setText("One APK per density");
// fill the ui
Sdk currentSdk = Sdk.getCurrent();
@@ -89,9 +89,9 @@
mSelector.setSelection(target);
}
- // get the apk configurations
- Map<String, String> configs = currentSdk.getProjectApkConfigs(mProject);
- mApkConfigWidget.fillTable(configs);
+ // get the project settings
+ ApkSettings settings = currentSdk.getApkSettings(mProject);
+ mSplitByDensity.setSelection(settings.isSplitByDpi());
}
mSelector.setSelectionListener(new SelectionAdapter() {
@@ -114,8 +114,10 @@
public boolean performOk() {
Sdk currentSdk = Sdk.getCurrent();
if (currentSdk != null) {
- currentSdk.setProject(mProject, mSelector.getSelected(),
- mApkConfigWidget.getApkConfigs());
+ ApkSettings apkSettings = new ApkSettings();
+ apkSettings.setSplitByDensity(mSplitByDensity.getSelection());
+
+ currentSdk.setProject(mProject, mSelector.getSelected(), apkSettings);
}
return true;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java
index 23a6440..7857997 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java
@@ -314,8 +314,11 @@
for (ResourceQualifier qualifier : mQualifiers) {
if (qualifier != null) {
- result.append(QUALIFIER_SEP);
- result.append(qualifier.getFolderSegment(target));
+ String segment = qualifier.getFolderSegment(target);
+ if (segment != null && segment.length() > 0) {
+ result.append(QUALIFIER_SEP);
+ result.append(segment);
+ }
}
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/LanguageQualifier.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/LanguageQualifier.java
index a098e44..50f5c52 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/LanguageQualifier.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/LanguageQualifier.java
@@ -63,6 +63,14 @@
return null;
}
+ public LanguageQualifier() {
+
+ }
+
+ public LanguageQualifier(String value) {
+ mValue = value;
+ }
+
public String getValue() {
if (mValue != null) {
return mValue;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/NavigationMethodQualifier.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/NavigationMethodQualifier.java
index 0a5c968..9363e87 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/NavigationMethodQualifier.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/NavigationMethodQualifier.java
@@ -134,8 +134,7 @@
public boolean checkAndSet(String value, FolderConfiguration config) {
NavigationMethod method = NavigationMethod.getEnum(value);
if (method != null) {
- NavigationMethodQualifier qualifier = new NavigationMethodQualifier();
- qualifier.mValue = method;
+ NavigationMethodQualifier qualifier = new NavigationMethodQualifier(method);
config.setNavigationMethodQualifier(qualifier);
return true;
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/RegionQualifier.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/RegionQualifier.java
index 3f3abcc..bf192c2 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/RegionQualifier.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/RegionQualifier.java
@@ -67,6 +67,14 @@
return ""; //$NON-NLS-1$
}
+ public RegionQualifier() {
+
+ }
+
+ public RegionQualifier(String value) {
+ mValue = value;
+ }
+
public String getValue() {
if (mValue != null) {
return mValue;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenDimensionQualifier.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenDimensionQualifier.java
index ff8a930..30929b2 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenDimensionQualifier.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenDimensionQualifier.java
@@ -48,6 +48,15 @@
* Default value is <code>DEFAULT_SIZE</code> */
private int mValue2 = DEFAULT_SIZE;
+ public ScreenDimensionQualifier() {
+ // pass
+ }
+
+ public ScreenDimensionQualifier(int value1, int value2) {
+ mValue1 = value1;
+ mValue2 = value2;
+ }
+
public int getValue1() {
return mValue1;
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java
index e6f81e2..e8c3687 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java
@@ -32,10 +32,11 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
/**
* Represents the resources of a project. This is a file view of the resources, with handling
@@ -44,30 +45,30 @@
public class ProjectResources implements IResourceRepository {
private final HashMap<ResourceFolderType, List<ResourceFolder>> mFolderMap =
new HashMap<ResourceFolderType, List<ResourceFolder>>();
-
+
private final HashMap<ResourceType, List<ProjectResourceItem>> mResourceMap =
new HashMap<ResourceType, List<ProjectResourceItem>>();
-
+
/** Map of (name, id) for resources of type {@link ResourceType#ID} coming from R.java */
private Map<String, Map<String, Integer>> mResourceValueMap;
/** Map of (id, [name, resType]) for all resources coming from R.java */
private Map<Integer, String[]> mResIdValueToNameMap;
/** Map of (int[], name) for styleable resources coming from R.java */
private Map<IntArrayWrapper, String> mStyleableValueToNameMap;
-
+
/** Cached list of {@link IdResourceItem}. This is mix of IdResourceItem created by
* {@link MultiResourceFile} for ids coming from XML files under res/values and
* {@link IdResourceItem} created manually, from the list coming from R.java */
private final ArrayList<IdResourceItem> mIdResourceList = new ArrayList<IdResourceItem>();
private final boolean mIsFrameworkRepository;
-
+
private final IntArrayWrapper mWrapper = new IntArrayWrapper(null);
public ProjectResources(boolean isFrameworkRepository) {
mIsFrameworkRepository = isFrameworkRepository;
}
-
+
public boolean isSystemRepository() {
return mIsFrameworkRepository;
}
@@ -83,7 +84,7 @@
IAbstractFolder folder) {
// get the list for the resource type
List<ResourceFolder> list = mFolderMap.get(type);
-
+
if (list == null) {
list = new ArrayList<ResourceFolder>();
@@ -91,7 +92,7 @@
list.add(cf);
mFolderMap.put(type, list);
-
+
return cf;
}
@@ -109,7 +110,7 @@
// So we add one.
ResourceFolder cf = new ResourceFolder(type, config, folder, mIsFrameworkRepository);
list.add(cf);
-
+
return cf;
}
@@ -121,7 +122,7 @@
protected void removeFolder(ResourceFolderType type, IFolder folder) {
// get the list of folders for the resource type.
List<ResourceFolder> list = mFolderMap.get(type);
-
+
if (list != null) {
int count = list.size();
for (int i = 0 ; i < count ; i++) {
@@ -129,7 +130,7 @@
if (resFolder.getFolder().getIFolder().equals(folder)) {
// we found the matching ResourceFolder. we need to remove it.
list.remove(i);
-
+
// we now need to invalidate this resource type.
// The easiest way is to touch one of the other folders of the same type.
if (list.size() > 0) {
@@ -146,23 +147,23 @@
// If we don't find a single folder to touch, then it's fine, as the top
// level items (the list of generated resource types) is not cached
// (for now)
-
+
// get the lists of ResourceTypes generated by this ResourceFolderType
ResourceType[] resTypes = FolderTypeRelationship.getRelatedResourceTypes(
type);
-
+
// for each of those, make sure to find one folder to touch so that the
// list of ResourceItem associated with the type is rebuilt.
for (ResourceType resType : resTypes) {
// get the list of folder that can generate this type
ResourceFolderType[] folderTypes =
FolderTypeRelationship.getRelatedFolders(resType);
-
+
// we only need to touch one folder in any of those (since it's one
// folder per type, not per folder type).
for (ResourceFolderType folderType : folderTypes) {
List<ResourceFolder> resFolders = mFolderMap.get(folderType);
-
+
if (resFolders != null && resFolders.size() > 0) {
resFolders.get(0).touch();
break;
@@ -170,7 +171,7 @@
}
}
}
-
+
// we're done updating/touching, we can stop
break;
}
@@ -178,7 +179,7 @@
}
}
-
+
/**
* Returns a list of {@link ResourceFolder} for a specific {@link ResourceFolderType}.
* @param type The {@link ResourceFolderType}
@@ -186,13 +187,13 @@
public List<ResourceFolder> getFolders(ResourceFolderType type) {
return mFolderMap.get(type);
}
-
+
/* (non-Javadoc)
* @see com.android.ide.eclipse.editors.resources.IResourceRepository#getAvailableResourceTypes()
*/
public ResourceType[] getAvailableResourceTypes() {
ArrayList<ResourceType> list = new ArrayList<ResourceType>();
-
+
// For each key, we check if there's a single ResourceType match.
// If not, we look for the actual content to give us the resource type.
@@ -213,7 +214,7 @@
if (folders != null) {
for (ResourceFolder folder : folders) {
Collection<ResourceType> folderContent = folder.getResourceTypes();
-
+
// then we add them, but only if they aren't already in the list.
for (ResourceType folderResType : folderContent) {
if (list.indexOf(folderResType) == -1) {
@@ -224,7 +225,7 @@
}
}
}
-
+
// in case ResourceType.ID haven't been added yet because there's no id defined
// in XML, we check on the list of compiled id resources.
if (list.indexOf(ResourceType.ID) == -1 && mResourceValueMap != null) {
@@ -237,7 +238,7 @@
// at this point the list is full of ResourceType defined in the files.
// We need to sort it.
Collections.sort(list);
-
+
return list.toArray(new ResourceType[list.size()]);
}
@@ -246,15 +247,15 @@
*/
public ProjectResourceItem[] getResources(ResourceType type) {
checkAndUpdate(type);
-
+
if (type == ResourceType.ID) {
synchronized (mIdResourceList) {
return mIdResourceList.toArray(new ProjectResourceItem[mIdResourceList.size()]);
}
}
-
+
List<ProjectResourceItem> items = mResourceMap.get(type);
-
+
return items.toArray(new ProjectResourceItem[items.size()]);
}
@@ -287,10 +288,10 @@
}
}
}
-
+
return null;
}
-
+
/**
* Returns the {@link ResourceFile} matching the given name, {@link ResourceFolderType} and
* configuration.
@@ -305,29 +306,29 @@
// look for folders containing a file with the given name.
ArrayList<ResourceFolder> matchingFolders = new ArrayList<ResourceFolder>();
-
+
// remove the folders that do not have a file with the given name, or if their config
// is incompatible.
for (int i = 0 ; i < folders.size(); i++) {
ResourceFolder folder = folders.get(i);
-
+
if (folder.hasFile(name) == true) {
matchingFolders.add(folder);
}
}
-
+
// from those, get the folder with a config matching the given reference configuration.
Resource match = findMatchingConfiguredResource(matchingFolders, config);
-
+
// do we have a matching folder?
if (match instanceof ResourceFolder) {
// get the ResourceFile from the filename
return ((ResourceFolder)match).getFile(name);
}
-
+
return null;
}
-
+
/**
* Returns the resources values matching a given {@link FolderConfiguration}.
* @param referenceConfig the configuration that each value must match.
@@ -337,7 +338,7 @@
Map<String, Map<String, IResourceValue>> map =
new HashMap<String, Map<String, IResourceValue>>();
-
+
// special case for Id since there's a mix of compiled id (declared inline) and id declared
// in the XML files.
if (mIdResourceList.size() > 0) {
@@ -348,10 +349,10 @@
idMap.put(id.getName(), new ResourceValue(idType, id.getName(),
mIsFrameworkRepository));
}
-
+
map.put(ResourceType.ID.getName(), idMap);
}
-
+
Set<ResourceType> keys = mResourceMap.keySet();
for (ResourceType key : keys) {
// we don't process ID resources since we already did it above.
@@ -359,10 +360,10 @@
map.put(key.getName(), getConfiguredResource(key, referenceConfig));
}
}
-
+
return map;
}
-
+
/**
* Loads all the resources. Essentially this forces to load the values from the
* {@link ResourceFile} objects to make sure they are up to date and loaded
@@ -371,13 +372,13 @@
public void loadAll() {
// gets all the resource types available.
ResourceType[] types = getAvailableResourceTypes();
-
+
// loop on them and load them
for (ResourceType type: types) {
checkAndUpdate(type);
}
}
-
+
/**
* Resolves a compiled resource id into the resource name and type
* @param id
@@ -387,7 +388,7 @@
if (mResIdValueToNameMap != null) {
return mResIdValueToNameMap.get(id);
}
-
+
return null;
}
@@ -399,7 +400,7 @@
mWrapper.set(id);
return mStyleableValueToNameMap.get(mWrapper);
}
-
+
return null;
}
@@ -416,12 +417,12 @@
return null;
}
-
+
/**
- * Returns the list of languages used in the resources.
+ * Returns the sorted list of languages used in the resources.
*/
- public Set<String> getLanguages() {
- Set<String> set = new HashSet<String>();
+ public SortedSet<String> getLanguages() {
+ SortedSet<String> set = new TreeSet<String>();
Collection<List<ResourceFolder>> folderList = mFolderMap.values();
for (List<ResourceFolder> folderSubList : folderList) {
@@ -433,22 +434,22 @@
}
}
}
-
+
return set;
}
-
+
/**
- * Returns the list of regions used in the resources with the given language.
+ * Returns the sorted list of regions used in the resources with the given language.
* @param currentLanguage the current language the region must be associated with.
*/
- public Set<String> getRegions(String currentLanguage) {
- Set<String> set = new HashSet<String>();
+ public SortedSet<String> getRegions(String currentLanguage) {
+ SortedSet<String> set = new TreeSet<String>();
Collection<List<ResourceFolder>> folderList = mFolderMap.values();
for (List<ResourceFolder> folderSubList : folderList) {
for (ResourceFolder folder : folderSubList) {
FolderConfiguration config = folder.getConfiguration();
-
+
// get the language
LanguageQualifier lang = config.getLanguageQualifier();
if (lang != null && lang.getStringValue().equals(currentLanguage)) {
@@ -459,7 +460,7 @@
}
}
}
-
+
return set;
}
@@ -474,23 +475,23 @@
FolderConfiguration referenceConfig) {
// get the resource item for the given type
List<ProjectResourceItem> items = mResourceMap.get(type);
-
+
// create the map
HashMap<String, IResourceValue> map = new HashMap<String, IResourceValue>();
-
+
for (ProjectResourceItem item : items) {
// get the source files generating this resource
List<ResourceFile> list = item.getSourceFileList();
-
+
// look for the best match for the given configuration
Resource match = findMatchingConfiguredResource(list, referenceConfig);
-
+
if (match instanceof ResourceFile) {
ResourceFile matchResFile = (ResourceFile)match;
-
+
// get the value of this configured resource.
IResourceValue value = matchResFile.getValue(type, item.getName());
-
+
if (value != null) {
map.put(item.getName(), value);
}
@@ -501,7 +502,7 @@
}
/**
- * Returns the best matching {@link Resource}.
+ * Returns the best matching {@link Resource}.
* @param resources the list of {@link Resource} to choose from.
* @param referenceConfig the {@link FolderConfiguration} to match.
*/
@@ -512,7 +513,7 @@
ArrayList<Resource> matchingResources = new ArrayList<Resource>();
for (int i = 0 ; i < resources.size(); i++) {
Resource res = resources.get(i);
-
+
int count = res.getConfiguration().match(referenceConfig);
if (count > currentMax) {
matchingResources.clear();
@@ -522,7 +523,7 @@
matchingResources.add(res);
}
}
-
+
// if we have more than one match, we look for the match with the qualifiers with the
// highest priority.
Resource resMatch = null;
@@ -537,7 +538,7 @@
int highest = -1;
for (int i = 0 ; i < matchingResources.size() ; i++) {
Resource folder = matchingResources.get(i);
-
+
// get highest priority qualifiers.
int m = folder.getConfiguration().getHighestPriorityQualifier(startIndex);
@@ -552,12 +553,12 @@
}
}
}
-
+
// at this point, we have a list with 1+ resources that all have the same highest
// priority qualifiers. Go through the list again looking for the next highest
// priority qualifier.
startIndex = highest + 1;
-
+
// this should not happen, but it's better to check.
if (matchingResources.size() == tmpResources.size() && highest == -1) {
// this means all the resources match with the same qualifiers
@@ -571,7 +572,7 @@
}
tmpResources.clear();
}
-
+
// we should have only one match here.
resMatch = matchingResources.get(0);
}
@@ -581,7 +582,7 @@
/**
* Checks if the list of {@link ResourceItem}s for the specified {@link ResourceType} needs
- * to be updated.
+ * to be updated.
* @param type the Resource Type.
*/
private void checkAndUpdate(ResourceType type) {
@@ -590,7 +591,7 @@
for (ResourceFolderType folderType : folderTypes) {
List<ResourceFolder> folders = mFolderMap.get(folderType);
-
+
if (folders != null) {
for (ResourceFolder folder : folders) {
if (folder.isTouched()) {
@@ -618,7 +619,7 @@
// get the cache list, and lets make a backup
List<ProjectResourceItem> items = mResourceMap.get(type);
List<ProjectResourceItem> backup = new ArrayList<ProjectResourceItem>();
-
+
if (items == null) {
items = new ArrayList<ProjectResourceItem>();
mResourceMap.put(type, items);
@@ -629,7 +630,7 @@
// we reset the list itself.
items.clear();
}
-
+
// get the list of folder that can output this type
ResourceFolderType[] folderTypes = FolderTypeRelationship.getRelatedFolders(type);
@@ -656,7 +657,7 @@
for (int i = 0 ; i < count;) {
// get the "new" item
ProjectResourceItem item = items.get(i);
-
+
// look for a similar item in the old list.
ProjectResourceItem foundOldItem = null;
for (ProjectResourceItem oldItem : backup) {
@@ -665,15 +666,15 @@
break;
}
}
-
+
if (foundOldItem != null) {
// erase the data of the old item with the data from the new one.
foundOldItem.replaceWith(item);
-
+
// remove the old and new item from their respective lists
items.remove(i);
backup.remove(foundOldItem);
-
+
// add the old item to the new list
items.add(foundOldItem);
} else {
@@ -682,7 +683,7 @@
}
}
}
-
+
// if this is the ResourceType.ID, we create the actual list, from this list and
// the compiled resource list.
if (type == ResourceType.ID) {
@@ -694,20 +695,20 @@
}
/**
- * Looks up an existing {@link ProjectResourceItem} by {@link ResourceType} and name.
+ * Looks up an existing {@link ProjectResourceItem} by {@link ResourceType} and name.
* @param type the Resource Type.
* @param name the Resource name.
* @return the existing ResourceItem or null if no match was found.
*/
protected ProjectResourceItem findResourceItem(ResourceType type, String name) {
List<ProjectResourceItem> list = mResourceMap.get(type);
-
+
for (ProjectResourceItem item : list) {
if (name.equals(item.getName())) {
return item;
}
}
-
+
return null;
}
@@ -745,13 +746,13 @@
// empty the current list
mIdResourceList.clear();
-
+
// get the list of compile id resources.
Map<String, Integer> idMap = null;
if (mResourceValueMap != null) {
idMap = mResourceValueMap.get(ResourceType.ID.getName());
}
-
+
if (idMap == null) {
if (xmlIdResources != null) {
for (ProjectResourceItem resourceItem : xmlIdResources) {
@@ -764,21 +765,21 @@
} else {
// loop on the full list of id, and look for a match in the old list,
// in the list coming from XML (in case a new XML item was created.)
-
+
Set<String> idSet = idMap.keySet();
-
+
idLoop: for (String idResource : idSet) {
// first look in the XML list in case an id went from inline to XML declared.
if (xmlIdResources != null) {
for (ProjectResourceItem resourceItem : xmlIdResources) {
- if (resourceItem instanceof IdResourceItem &&
+ if (resourceItem instanceof IdResourceItem &&
resourceItem.getName().equals(idResource)) {
mIdResourceList.add((IdResourceItem)resourceItem);
continue idLoop;
}
}
}
-
+
// if we haven't found it, look in the old items.
int count = oldItems.size();
for (int i = 0 ; i < count ; i++) {
@@ -789,14 +790,14 @@
continue idLoop;
}
}
-
+
// if we haven't found it, it looks like it's a new id that was
// declared inline.
mIdResourceList.add(new IdResourceItem(idResource,
true /* isDeclaredInline */));
}
}
-
+
// now we sort the list
Collections.sort(mIdResourceList);
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java
index 118fb4f..0d93a0c 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java
@@ -36,7 +36,7 @@
* This class contains the data of an Android Target as loaded from the SDK.
*/
public class AndroidTargetData {
-
+
public final static int DESCRIPTOR_MANIFEST = 1;
public final static int DESCRIPTOR_LAYOUT = 2;
public final static int DESCRIPTOR_MENU = 3;
@@ -45,15 +45,15 @@
public final static int DESCRIPTOR_SEARCHABLE = 6;
public final static int DESCRIPTOR_PREFERENCES = 7;
public final static int DESCRIPTOR_APPWIDGET_PROVIDER = 8;
-
+
public final static class LayoutBridge {
/** Link to the layout bridge */
public ILayoutBridge bridge;
public LoadStatus status = LoadStatus.LOADING;
-
+
public ClassLoader classLoader;
-
+
public int apiLevel;
}
@@ -67,12 +67,12 @@
* The attribute namespace prefix must be:
* - "android" for AndroidConstants.NS_RESOURCES
* - "xmlns" for the XMLNS URI.
- *
+ *
* This is used for attributes that do not have a unique name, but still need to be populated
* with values in the UI. Uniquely named attributes have their values in {@link #mEnumValueMap}.
*/
private Hashtable<String, String[]> mAttributeValues = new Hashtable<String, String[]>();
-
+
private IResourceRepository mSystemResourceRepository;
private AndroidManifestDescriptors mManifestDescriptors;
@@ -86,19 +86,20 @@
private LayoutBridge mLayoutBridge;
private boolean mLayoutBridgeInit = false;
+ private DeviceConfiguration[] mDevices;
AndroidTargetData(IAndroidTarget androidTarget) {
mTarget = androidTarget;
}
-
+
void setDexWrapper(DexWrapper wrapper) {
mDexWrapper = wrapper;
}
-
+
/**
* Creates an AndroidTargetData object.
- * @param platformLibraries
- * @param optionalLibraries
+ * @param platformLibraries
+ * @param optionalLibraries
*/
void setExtraData(IResourceRepository systemResourceRepository,
AndroidManifestDescriptors manifestDescriptors,
@@ -113,15 +114,17 @@
String[] intentCategoryValues,
String[] platformLibraries,
IOptionalLibrary[] optionalLibraries,
+ DeviceConfiguration[] devices,
ProjectResources resources,
LayoutBridge layoutBridge) {
-
+
mSystemResourceRepository = systemResourceRepository;
mManifestDescriptors = manifestDescriptors;
mLayoutDescriptors = layoutDescriptors;
mMenuDescriptors = menuDescriptors;
mXmlDescriptors = xmlDescriptors;
mEnumValueMap = enumValueMap;
+ mDevices = devices;
mFrameworkResources = resources;
mLayoutBridge = layoutBridge;
@@ -134,11 +137,11 @@
public DexWrapper getDexWrapper() {
return mDexWrapper;
}
-
+
public IResourceRepository getSystemResources() {
return mSystemResourceRepository;
}
-
+
/**
* Returns an {@link IDescriptorProvider} from a given Id.
* The Id can be one of {@link #DESCRIPTOR_MANIFEST}, {@link #DESCRIPTOR_LAYOUT},
@@ -168,21 +171,21 @@
throw new IllegalArgumentException();
}
}
-
+
/**
* Returns the manifest descriptors.
*/
public AndroidManifestDescriptors getManifestDescriptors() {
return mManifestDescriptors;
}
-
+
/**
* Returns the layout Descriptors.
*/
public LayoutDescriptors getLayoutDescriptors() {
return mLayoutDescriptors;
}
-
+
/**
* Returns the menu descriptors.
*/
@@ -202,7 +205,7 @@
* <p/>This should only be called for attributes for which possible values depend on the
* parent element node.
* <p/>For attributes that have the same values no matter the parent node, use
- * {@link #getEnumValueMap()}.
+ * {@link #getEnumValueMap()}.
* @param elementName the name of the element containing the attribute.
* @param attributeName the name of the attribute
* @return an array of String with the possible values, or <code>null</code> if no values were
@@ -220,7 +223,7 @@
* <p/>The typical example of this is for the 'name' attribute under
* activity/intent-filter/action
* <p/>For attributes that have the same values no matter the parent node, use
- * {@link #getEnumValueMap()}.
+ * {@link #getEnumValueMap()}.
* @param elementName the name of the element containing the attribute.
* @param attributeName the name of the attribute
* @param greatGrandParentElementName the great-grand-parent node.
@@ -231,13 +234,13 @@
String greatGrandParentElementName) {
if (greatGrandParentElementName != null) {
String key = String.format("(%1$s,%2$s,%3$s)", //$NON-NLS-1$
- greatGrandParentElementName, elementName, attributeName);
+ greatGrandParentElementName, elementName, attributeName);
String[] values = mAttributeValues.get(key);
if (values != null) {
return values;
}
}
-
+
return getAttributeValues(elementName, attributeName);
}
@@ -250,14 +253,14 @@
public Map<String, Map<String, Integer>> getEnumValueMap() {
return mEnumValueMap;
}
-
+
/**
* Returns the {@link ProjectResources} containing the Framework Resources.
*/
public ProjectResources getFrameworkResources() {
return mFrameworkResources;
}
-
+
/**
* Returns a {@link LayoutBridge} object possibly containing a {@link ILayoutBridge} object.
* <p/>If {@link LayoutBridge#bridge} is <code>null</code>, {@link LayoutBridge#status} will
@@ -272,7 +275,11 @@
}
return mLayoutBridge;
}
-
+
+ public DeviceConfiguration[] getDevices() {
+ return mDevices;
+ }
+
/**
* Sets the permission values
* @param permissionValues the list of permissions
@@ -285,7 +292,7 @@
setValues("(service,android:permission)", permissionValues); //$NON-NLS-1$
setValues("(provider,android:permission)", permissionValues); //$NON-NLS-1$
}
-
+
private void setIntentFilterActionsAndCategories(String[] activityIntentActions,
String[] broadcastIntentActions, String[] serviceIntentActions,
String[] intentCategoryValues) {
@@ -294,18 +301,18 @@
setValues("(service,action,android:name)", serviceIntentActions); //$NON-NLS-1$
setValues("(category,android:name)", intentCategoryValues); //$NON-NLS-1$
}
-
+
private void setOptionalLibraries(String[] platformLibraries,
IOptionalLibrary[] optionalLibraries) {
-
+
ArrayList<String> libs = new ArrayList<String>();
-
+
if (platformLibraries != null) {
for (String name : platformLibraries) {
libs.add(name);
}
}
-
+
if (optionalLibraries != null) {
for (int i = 0; i < optionalLibraries.length; i++) {
libs.add(optionalLibraries[i].getName());
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java
index a499137..9e14579 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java
@@ -254,6 +254,9 @@
LayoutBridge layoutBridge = loadLayoutBridge();
progress.worked(1);
+ // get the devices
+ DeviceConfiguration[] devices = getDevices();
+
// and finally create the PlatformData with all that we loaded.
targetData.setExtraData(frameworkRepository,
manifestDescriptors,
@@ -268,6 +271,7 @@
categories.toArray(new String[categories.size()]),
mAndroidTarget.getPlatformLibraries(),
mAndroidTarget.getOptionalLibraries(),
+ devices,
resources,
layoutBridge);
@@ -701,4 +705,10 @@
return layoutBridge;
}
+
+ private DeviceConfiguration[] getDevices() {
+ // TODO: load this from the target.
+ return null;
+ }
+
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/DeviceConfiguration.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/DeviceConfiguration.java
new file mode 100644
index 0000000..e829f93
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/DeviceConfiguration.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.ide.eclipse.adt.internal.sdk;
+
+import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
+import com.android.ide.eclipse.adt.internal.resources.configurations.KeyboardStateQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationMethodQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenDimensionQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenRatioQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenSizeQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.TextInputMethodQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.TouchScreenQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.KeyboardStateQualifier.KeyboardState;
+import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationMethodQualifier.NavigationMethod;
+import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier.Density;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier.ScreenOrientation;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenRatioQualifier.ScreenRatio;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenSizeQualifier.ScreenSize;
+import com.android.ide.eclipse.adt.internal.resources.configurations.TextInputMethodQualifier.TextInputMethod;
+import com.android.ide.eclipse.adt.internal.resources.configurations.TouchScreenQualifier.TouchScreenType;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class DeviceConfiguration {
+
+ private final String mName;
+ private Map<String, FolderConfiguration> mMap =
+ new HashMap<String, FolderConfiguration>();
+
+ DeviceConfiguration(String name) {
+ mName = name;
+ }
+
+ void addConfig(String name, FolderConfiguration config) {
+ mMap.put(name, config);
+ }
+
+ void seal() {
+ mMap = Collections.unmodifiableMap(mMap);
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public Map<String, FolderConfiguration> getConfigs() {
+ return mMap;
+ }
+
+ /**
+ * temp method returning some hard-coded devices.
+ * TODO: load devices from the SDK and add-ons and remove this method.
+ */
+ public static DeviceConfiguration[] getDevices() {
+ DeviceConfiguration adp1 = new DeviceConfiguration("ADP1");
+ // default config
+ FolderConfiguration defConfig = new FolderConfiguration();
+ defConfig.addQualifier(new ScreenSizeQualifier(ScreenSize.NORMAL));
+ defConfig.addQualifier(new ScreenRatioQualifier(ScreenRatio.NOTLONG));
+ defConfig.addQualifier(new PixelDensityQualifier(Density.MEDIUM));
+ defConfig.addQualifier(new TouchScreenQualifier(TouchScreenType.FINGER));
+ defConfig.addQualifier(new TextInputMethodQualifier(TextInputMethod.QWERTY));
+ defConfig.addQualifier(new NavigationMethodQualifier(NavigationMethod.TRACKBALL));
+ defConfig.addQualifier(new ScreenDimensionQualifier(480, 320));
+
+ // specific configs
+ FolderConfiguration closedLand = new FolderConfiguration();
+ closedLand.set(defConfig);
+ closedLand.addQualifier(new ScreenOrientationQualifier(ScreenOrientation.LANDSCAPE));
+ closedLand.addQualifier(new KeyboardStateQualifier(KeyboardState.HIDDEN));
+ adp1.addConfig("Closed, landscape", closedLand);
+
+ FolderConfiguration closedPort = new FolderConfiguration();
+ closedPort.set(defConfig);
+ closedPort.addQualifier(new ScreenOrientationQualifier(ScreenOrientation.PORTRAIT));
+ closedPort.addQualifier(new KeyboardStateQualifier(KeyboardState.HIDDEN));
+ adp1.addConfig("Closed, portrait", closedPort);
+
+ FolderConfiguration opened = new FolderConfiguration();
+ opened.set(defConfig);
+ opened.addQualifier(new ScreenOrientationQualifier(ScreenOrientation.LANDSCAPE));
+ opened.addQualifier(new KeyboardStateQualifier(KeyboardState.EXPOSED));
+ adp1.addConfig("Opened", opened);
+
+ DeviceConfiguration ion = new DeviceConfiguration("Ion");
+ // default config
+ defConfig = new FolderConfiguration();
+ defConfig.addQualifier(new ScreenSizeQualifier(ScreenSize.NORMAL));
+ defConfig.addQualifier(new ScreenRatioQualifier(ScreenRatio.NOTLONG));
+ defConfig.addQualifier(new PixelDensityQualifier(Density.MEDIUM));
+ defConfig.addQualifier(new TouchScreenQualifier(TouchScreenType.FINGER));
+ defConfig.addQualifier(new KeyboardStateQualifier(KeyboardState.EXPOSED));
+ defConfig.addQualifier(new TextInputMethodQualifier(TextInputMethod.NOKEY));
+ defConfig.addQualifier(new NavigationMethodQualifier(NavigationMethod.TRACKBALL));
+ defConfig.addQualifier(new ScreenDimensionQualifier(480, 320));
+
+ // specific configs
+ FolderConfiguration landscape = new FolderConfiguration();
+ landscape.set(defConfig);
+ landscape.addQualifier(new ScreenOrientationQualifier(ScreenOrientation.LANDSCAPE));
+ ion.addConfig("Landscape", landscape);
+
+ FolderConfiguration portrait = new FolderConfiguration();
+ portrait.set(defConfig);
+ portrait.addQualifier(new ScreenOrientationQualifier(ScreenOrientation.PORTRAIT));
+ ion.addConfig("Portrait", portrait);
+
+ return new DeviceConfiguration[] { adp1, ion };
+
+ }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
index f2e883d..f93b6c5 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
@@ -30,6 +30,7 @@
import com.android.sdklib.SdkManager;
import com.android.sdklib.internal.avd.AvdManager;
import com.android.sdklib.internal.project.ApkConfigurationHelper;
+import com.android.sdklib.internal.project.ApkSettings;
import com.android.sdklib.internal.project.ProjectProperties;
import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
@@ -68,8 +69,8 @@
new HashMap<IProject, IAndroidTarget>();
private final HashMap<IAndroidTarget, AndroidTargetData> mTargetDataMap =
new HashMap<IAndroidTarget, AndroidTargetData>();
- private final HashMap<IProject, Map<String, String>> mProjectApkConfigMap =
- new HashMap<IProject, Map<String, String>>();
+ private final HashMap<IProject, ApkSettings> mApkSettingsMap =
+ new HashMap<IProject, ApkSettings>();
private final String mDocBaseUrl;
/**
@@ -193,11 +194,9 @@
* apk configurations should not be updated.
*/
public void setProject(IProject project, IAndroidTarget target,
- Map<String, String> apkConfigMap) {
+ ApkSettings settings) {
synchronized (AdtPlugin.getDefault().getSdkLockObject()) {
boolean resolveProject = false;
- boolean compileProject = false;
- boolean cleanProject = false;
ProjectProperties properties = ProjectProperties.load(
project.getLocation().toOSString(), PropertyType.DEFAULT);
@@ -222,16 +221,18 @@
}
}
- if (apkConfigMap != null) {
- // save the apk configs in the project persistent property
- cleanProject = ApkConfigurationHelper.setConfigs(properties, apkConfigMap);
-
- // put it in a local map for easy access.
- mProjectApkConfigMap.put(project, apkConfigMap);
-
- compileProject = true;
+ // if there's no settings, force default values (to reset possibly changed
+ // values in a previous call.
+ if (settings == null) {
+ settings = new ApkSettings();
}
+ // save the project settings into the project persistent property
+ ApkConfigurationHelper.setProperties(properties, settings);
+
+ // put it in a local map for easy access.
+ mApkSettingsMap.put(project, settings);
+
// we are done with the modification. Save the property file.
try {
properties.save();
@@ -242,17 +243,14 @@
if (resolveProject) {
// force a resolve of the project by updating the classpath container.
+ // This will also force a recompile.
IJavaProject javaProject = JavaCore.create(project);
AndroidClasspathContainerInitializer.updateProjects(
new IJavaProject[] { javaProject });
- } else if (compileProject) {
- // If there was removed configs, we clean instead of build
- // (to remove the obsolete ap_ and apk file from removed configs).
+ } else {
+ // always do a full clean/build.
try {
- project.build(cleanProject ?
- IncrementalProjectBuilder.CLEAN_BUILD :
- IncrementalProjectBuilder.FULL_BUILD,
- null);
+ project.build(IncrementalProjectBuilder.CLEAN_BUILD, null);
} catch (CoreException e) {
// failed to build? force resolve instead.
IJavaProject javaProject = JavaCore.create(project);
@@ -316,10 +314,10 @@
if (sdkStorage != null) {
synchronized (AdtPlugin.getDefault().getSdkLockObject()) {
- Map<String, String> configMap = ApkConfigurationHelper.getConfigs(properties);
+ ApkSettings settings = ApkConfigurationHelper.getSettings(properties);
- if (configMap != null) {
- sdkStorage.mProjectApkConfigMap.put(project, configMap);
+ if (settings != null) {
+ sdkStorage.mApkSettingsMap.put(project, settings);
}
}
}
@@ -392,13 +390,11 @@
}
/**
- * Returns the configuration map for a given project.
- * <p/>The Map key are name to be used in the apk filename, while the values are comma separated
- * config values. The config value can be passed directly to aapt through the -c option.
+ * Returns the APK settings for a given project.
*/
- public Map<String, String> getProjectApkConfigs(IProject project) {
+ public ApkSettings getApkSettings(IProject project) {
synchronized (AdtPlugin.getDefault().getSdkLockObject()) {
- return mProjectApkConfigMap.get(project);
+ return mApkSettingsMap.get(project);
}
}
@@ -505,7 +501,7 @@
// now remove the project for the maps.
mProjectTargetMap.remove(project);
- mProjectApkConfigMap.remove(project);
+ mApkSettingsMap.remove(project);
}
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/KeyCheckPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/KeyCheckPage.java
index 0f630c4..5f8a8d5 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/KeyCheckPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/KeyCheckPage.java
@@ -19,6 +19,7 @@
import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.internal.wizards.export.ExportWizard.ExportWizardPage;
+import com.android.sdklib.internal.project.ApkSettings;
import org.eclipse.core.resources.IProject;
import org.eclipse.swt.SWT;
@@ -68,17 +69,17 @@
private Text mDestination;
private boolean mFatalSigningError;
private FormText mDetailText;
- /** The Apk Config map for the current project */
- private Map<String, String> mApkConfig;
private ScrolledComposite mScrolledComposite;
-
+
+ private ApkSettings mApkSettings;
+
private String mKeyDetails;
private String mDestinationDetails;
protected KeyCheckPage(ExportWizard wizard, String pageName) {
super(pageName);
mWizard = wizard;
-
+
setTitle("Destination and key/certificate checks");
setDescription(""); // TODO
}
@@ -93,7 +94,7 @@
GridLayout gl = new GridLayout(3, false);
gl.verticalSpacing *= 3;
composite.setLayout(gl);
-
+
GridData gd;
new Label(composite, SWT.NONE).setText("Destination APK file:");
@@ -110,26 +111,26 @@
@Override
public void widgetSelected(SelectionEvent e) {
FileDialog fileDialog = new FileDialog(browseButton.getShell(), SWT.SAVE);
-
+
fileDialog.setText("Destination file name");
// get a default apk name based on the project
String filename = ProjectHelper.getApkFilename(mWizard.getProject(),
null /*config*/);
fileDialog.setFileName(filename);
-
+
String saveLocation = fileDialog.open();
if (saveLocation != null) {
mDestination.setText(saveLocation);
}
}
});
-
+
mScrolledComposite = new ScrolledComposite(composite, SWT.V_SCROLL);
mScrolledComposite.setLayoutData(gd = new GridData(GridData.FILL_BOTH));
gd.horizontalSpan = 3;
mScrolledComposite.setExpandHorizontal(true);
mScrolledComposite.setExpandVertical(true);
-
+
mDetailText = new FormText(mScrolledComposite, SWT.NONE);
mScrolledComposite.setContent(mDetailText);
@@ -139,18 +140,18 @@
updateScrolling();
}
});
-
+
setControl(composite);
}
-
+
@Override
void onShow() {
// fill the texts with information loaded from the project.
if ((mProjectDataChanged & DATA_PROJECT) != 0) {
// reset the destination from the content of the project
IProject project = mWizard.getProject();
- mApkConfig = Sdk.getCurrent().getProjectApkConfigs(project);
-
+ mApkSettings = Sdk.getCurrent().getApkSettings(project);
+
String destination = ProjectHelper.loadStringProperty(project,
ExportWizard.PROPERTY_DESTINATION);
String filename = ProjectHelper.loadStringProperty(project,
@@ -159,7 +160,7 @@
mDestination.setText(destination + File.separator + filename);
}
}
-
+
// if anything change we basically reload the data.
if (mProjectDataChanged != 0) {
mFatalSigningError = false;
@@ -170,7 +171,7 @@
mPrivateKey = null;
mCertificate = null;
mKeyDetails = null;
-
+
if (mWizard.getKeystoreCreationMode() || mWizard.getKeyCreationMode()) {
int validity = mWizard.getValidity();
StringBuilder sb = new StringBuilder(
@@ -196,13 +197,13 @@
mWizard.getKeyAlias(),
new KeyStore.PasswordProtection(
mWizard.getKeyPassword().toCharArray()));
-
+
if (entry != null) {
mPrivateKey = entry.getPrivateKey();
mCertificate = (X509Certificate)entry.getCertificate();
} else {
setErrorMessage("Unable to find key.");
-
+
setPageComplete(false);
}
} catch (FileNotFoundException e) {
@@ -220,33 +221,33 @@
} catch (IOException e) {
onException(e);
}
-
+
if (mPrivateKey != null && mCertificate != null) {
Calendar expirationCalendar = Calendar.getInstance();
expirationCalendar.setTime(mCertificate.getNotAfter());
Calendar today = Calendar.getInstance();
-
+
if (expirationCalendar.before(today)) {
mKeyDetails = String.format(
"<p>Certificate expired on %s</p>",
mCertificate.getNotAfter().toString());
-
+
// fatal error = nothing can make the page complete.
mFatalSigningError = true;
-
+
setErrorMessage("Certificate is expired.");
setPageComplete(false);
} else {
// valid, key/cert: put it in the wizard so that it can be finished
mWizard.setSigningInfo(mPrivateKey, mCertificate);
-
+
StringBuilder sb = new StringBuilder(String.format(
"<p>Certificate expires on %s.</p>",
mCertificate.getNotAfter().toString()));
-
+
int expirationYear = expirationCalendar.get(Calendar.YEAR);
int thisYear = today.get(Calendar.YEAR);
-
+
if (thisYear + 25 < expirationYear) {
// do nothing
} else {
@@ -258,14 +259,14 @@
"<p>The Certificate expires in %1$s %2$s.</p>",
count, count == 1 ? "year" : "years"));
}
-
+
sb.append("<p>Make sure the certificate is valid for the planned lifetime of the product.</p>");
sb.append("<p>If the certificate expires, you will be forced to sign your application with a different one.</p>");
sb.append("<p>Applications cannot be upgraded if their certificate changes from one version to another, ");
sb.append("forcing a full uninstall/install, which will make the user lose his/her data.</p>");
sb.append("<p>Android Market currently requires certificates to be valid until 2033.</p>");
}
-
+
mKeyDetails = sb.toString();
}
} else {
@@ -277,7 +278,7 @@
onDestinationChange(true /*forceDetailUpdate*/);
}
-
+
/**
* Callback for destination field edition
* @param forceDetailUpdate if true, the detail {@link FormText} is updated even if a fatal
@@ -319,12 +320,12 @@
// display the list of files that will actually be created
Map<String, String[]> apkFileMap = getApkFileMap(file);
-
+
// display them
boolean fileExists = false;
StringBuilder sb = new StringBuilder(String.format(
"<p>This will create the following files:</p>"));
-
+
Set<Entry<String, String[]>> set = apkFileMap.entrySet();
for (Entry<String, String[]> entry : set) {
String[] apkArray = entry.getValue();
@@ -360,7 +361,7 @@
updateDetailText();
}
}
-
+
/**
* Updates the scrollbar to match the content of the {@link FormText} or the new size
* of the {@link ScrolledComposite}.
@@ -372,41 +373,40 @@
mScrolledComposite.layout();
}
}
-
+
private void updateDetailText() {
StringBuilder sb = new StringBuilder("<form>");
if (mKeyDetails != null) {
sb.append(mKeyDetails);
}
-
+
if (mDestinationDetails != null && mFatalSigningError == false) {
sb.append(mDestinationDetails);
}
-
+
sb.append("</form>");
-
+
mDetailText.setText(sb.toString(), true /* parseTags */,
true /* expandURLs */);
mDetailText.getParent().layout();
updateScrolling();
-
}
/**
* Creates the list of destination filenames based on the content of the destination field
* and the list of APK configurations for the project.
- *
+ *
* @param file File name from the destination field
* @return A list of destination filenames based <code>file</code> and the list of APK
* configurations for the project.
*/
private Map<String, String[]> getApkFileMap(File file) {
String filename = file.getName();
-
+
HashMap<String, String[]> map = new HashMap<String, String[]>();
-
+
// add the default APK filename
String[] apkArray = new String[ExportWizard.APK_COUNT];
apkArray[ExportWizard.APK_FILE_SOURCE] = ProjectHelper.getApkFilename(
@@ -415,29 +415,32 @@
map.put(null, apkArray);
// add the APKs for each APK configuration.
- if (mApkConfig != null && mApkConfig.size() > 0) {
- // remove the extension.
- int index = filename.lastIndexOf('.');
- String base = filename.substring(0, index);
- String extension = filename.substring(index);
-
- Set<Entry<String, String>> set = mApkConfig.entrySet();
- for (Entry<String, String> entry : set) {
- apkArray = new String[ExportWizard.APK_COUNT];
- apkArray[ExportWizard.APK_FILE_SOURCE] = ProjectHelper.getApkFilename(
- mWizard.getProject(), entry.getKey());
- apkArray[ExportWizard.APK_FILE_DEST] = base + "-" + entry.getKey() + extension;
- map.put(entry.getKey(), apkArray);
+ if (mApkSettings != null) {
+ Map<String, String> apkFilters = mApkSettings.getResourceFilters();
+ if (apkFilters.size() > 0) {
+ // remove the extension from the user-chosen filename
+ int index = filename.lastIndexOf('.');
+ String base = filename.substring(0, index);
+ String extension = filename.substring(index);
+
+ for (Entry<String, String> entry : apkFilters.entrySet()) {
+ apkArray = new String[ExportWizard.APK_COUNT];
+ apkArray[ExportWizard.APK_FILE_SOURCE] = ProjectHelper.getApkFilename(
+ mWizard.getProject(), entry.getKey());
+ apkArray[ExportWizard.APK_FILE_DEST] = base + "-" + //$NON-NLS-1$
+ entry.getKey() + extension;
+ map.put(entry.getKey(), apkArray);
+ }
}
}
-
+
return map;
}
-
+
@Override
protected void onException(Throwable t) {
super.onException(t);
-
+
mKeyDetails = String.format("ERROR: %1$s", ExportWizard.getExceptionMessage(t));
}
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.common/.gitignore b/tools/eclipse/plugins/com.android.ide.eclipse.common/.gitignore
new file mode 100644
index 0000000..d392f0e
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.common/.gitignore
@@ -0,0 +1 @@
+*.jar
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.ddms/META-INF/MANIFEST.MF b/tools/eclipse/plugins/com.android.ide.eclipse.ddms/META-INF/MANIFEST.MF
index 9f021ae..fd818bf 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.ddms/META-INF/MANIFEST.MF
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.ddms/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
Bundle-ManifestVersion: 2
Bundle-Name: Dalvik Debug Monitor Service
Bundle-SymbolicName: com.android.ide.eclipse.ddms;singleton:=true
-Bundle-Version: 0.9.3.qualifier
+Bundle-Version: 0.9.4.qualifier
Bundle-Activator: com.android.ide.eclipse.ddms.DdmsPlugin
Bundle-Vendor: The Android Open Source Project
Bundle-Localization: plugin
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/.gitignore b/tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/.gitignore
new file mode 100644
index 0000000..f432e88
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/.gitignore
@@ -0,0 +1,31 @@
+add.png
+backward.png
+clear.png
+d.png
+debug-attach.png
+debug-error.png
+debug-wait.png
+delete.png
+device.png
+down.png
+e.png
+edit.png
+empty.png
+emulator.png
+forward.png
+gc.png
+halt.png
+heap.png
+i.png
+importBug.png
+load.png
+pause.png
+play.png
+pull.png
+push.png
+save.png
+thread.png
+up.png
+v.png
+w.png
+warning.png
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.ddms/libs/.gitignore b/tools/eclipse/plugins/com.android.ide.eclipse.ddms/libs/.gitignore
new file mode 100644
index 0000000..d392f0e
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.ddms/libs/.gitignore
@@ -0,0 +1 @@
+*.jar
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/.gitignore b/tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/.gitignore
new file mode 100644
index 0000000..76d9981
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/.gitignore
@@ -0,0 +1,2 @@
+ddmlib
+ddmuilib
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.editors/.gitignore b/tools/eclipse/plugins/com.android.ide.eclipse.editors/.gitignore
new file mode 100644
index 0000000..d392f0e
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.editors/.gitignore
@@ -0,0 +1 @@
+*.jar
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/.gitignore b/tools/eclipse/plugins/com.android.ide.eclipse.tests/.gitignore
new file mode 100644
index 0000000..d392f0e
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/.gitignore
@@ -0,0 +1 @@
+*.jar
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF b/tools/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF
index a93bdef..61e8ee4 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
Bundle-ManifestVersion: 2
Bundle-Name: Android Plugin Tests
Bundle-SymbolicName: com.android.ide.eclipse.tests
-Bundle-Version: 0.9.3.qualifier
+Bundle-Version: 0.9.4.qualifier
Bundle-Activator: com.android.ide.eclipse.tests.AndroidTestPlugin
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime,
diff --git a/tools/eclipse/sites/external/site.xml b/tools/eclipse/sites/external/site.xml
index a00d1a4..ef0df64 100644
--- a/tools/eclipse/sites/external/site.xml
+++ b/tools/eclipse/sites/external/site.xml
@@ -3,10 +3,10 @@
<description url="https://dl-ssl.google.com/android/eclipse/">
Update Site for Android Development Toolkit
</description>
- <feature url="features/com.android.ide.eclipse.adt_0.9.3.qualifier.jar" id="com.android.ide.eclipse.adt" version="0.9.3.qualifier">
+ <feature url="features/com.android.ide.eclipse.adt_0.9.4.qualifier.jar" id="com.android.ide.eclipse.adt" version="0.9.4.qualifier">
<category name="developer"/>
</feature>
- <feature url="features/com.android.ide.eclipse.ddms_0.9.3.qualifier.jar" id="com.android.ide.eclipse.ddms" version="0.9.3.qualifier">
+ <feature url="features/com.android.ide.eclipse.ddms_0.9.4.qualifier.jar" id="com.android.ide.eclipse.ddms" version="0.9.4.qualifier">
<category name="developer"/>
</feature>
<category-def name="developer" label="Developer Tools">
diff --git a/tools/eclipse/sites/internal/site.xml b/tools/eclipse/sites/internal/site.xml
index e19c119..31744d7 100644
--- a/tools/eclipse/sites/internal/site.xml
+++ b/tools/eclipse/sites/internal/site.xml
@@ -3,13 +3,13 @@
<description url="https://android.corp.google.com/adt/">
Update Site for Android Development Toolkit
</description>
- <feature url="features/com.android.ide.eclipse.adt_0.9.3.qualifier.jar" id="com.android.ide.eclipse.adt" version="0.9.3.qualifier">
+ <feature url="features/com.android.ide.eclipse.adt_0.9.4.qualifier.jar" id="com.android.ide.eclipse.adt" version="0.9.4.qualifier">
<category name="developer"/>
</feature>
- <feature url="features/com.android.ide.eclipse.ddms_0.9.3.qualifier.jar" id="com.android.ide.eclipse.ddms" version="0.9.3.qualifier">
+ <feature url="features/com.android.ide.eclipse.ddms_0.9.4.qualifier.jar" id="com.android.ide.eclipse.ddms" version="0.9.4.qualifier">
<category name="developer"/>
</feature>
- <feature url="features/com.android.ide.eclipse.tests_0.9.3.qualifier.jar" id="com.android.ide.eclipse.tests" version="0.9.3.qualifier">
+ <feature url="features/com.android.ide.eclipse.tests_0.9.4.qualifier.jar" id="com.android.ide.eclipse.tests" version="0.9.4.qualifier">
<category name="test"/>
</feature>
<category-def name="developer" label="Application Developer Tools">
diff --git a/tools/findunused/findunusedresources b/tools/findunused/findunusedresources
index 748139a..54b1596 100755
--- a/tools/findunused/findunusedresources
+++ b/tools/findunused/findunusedresources
@@ -26,11 +26,15 @@
for app in $apps
do
echo '-----------------------------------------------------------'
+ if [ "$app" == "." ]
+ then
+ app=$(pwd)
+ fi
if [ -d $app/res ]
then
appname=$(basename $app)
resources=
- for res in $(echo $app/res/*)
+ for res in $(echo $app/res/* $(find $ANDROID_BUILD_TOP/vendor -type d -wholename $ANDROID_BUILD_TOP/vendor/*/$appname/res | grep overlay))
do
resources="$resources $(echo $res | grep -v '\-mcc\|[a-z]*-[a-z][a-z]$\|[a-z]*-[a-z][a-z]-.*')"
done
@@ -45,19 +49,25 @@
fi
# find the R.java file that contains all the generated resource identifiers
- rDotJava=$(find out/target/common/obj/APPS/${appname}_intermediates/ -name R.java)
+ rDotJava=$(find $ANDROID_BUILD_TOP/out/target/common/obj/APPS/${appname}_intermediates/ -name R.java)
# Simplistically process the content of the file to get the names of all the constants,
# and try to find a reference to each constant.
+
+ # First take all the input files and concatenate them, removing newlines. This allows us to
+ # find expressions that are broken up over multiple lines, i.e. R.drawable.\nsomeconstant
+ find $resources $sources $app/AndroidManifest.xml -type f -print |xargs cat | tr -d '\n ' > /tmp/everything$$
+
+ # Now look for each of the constants in the contatenated file.
for i in $(cat $rDotJava | grep "\w*=0x\d*" | sed 's/ *public static final int //' | sed 's/=0x.*//')
do
# Since periods in the names get translated to underscores in R.java, and you can actually
# refer to such constants from java by using an underscore instead of a period, we also
# replace all underscores with a pattern that will match periods and underscores.
p=$(echo $i | sed 's/_/[\\._]/g')
- echo $i $(grep -Rw R\\..*\\.$i\\\|@style/$p\\\|@drawable/$p\\\|@anim/$p\\\|@color/$p\\\|@xml/$p\\\|@layout/$p\\\|@menu/$p\\\|@+id/$p\\\|@array/$p\\\|@string/$p\\\|@dimen/$p $resources $sources $app/AndroidManifest.xml | wc -l)
+ echo $i $(grep -cw R\\..*\\.$i\\\|@style/$p\\\|@drawable/$p\\\|@anim/$p\\\|@color/$p\\\|@xml/$p\\\|@layout/$p\\\|@menu/$p\\\|@+id/$p\\\|@array/$p\\\|@string/$p\\\|@dimen/$p\\\|\[a-z\]\*:$p\\\|enumname=\"$p\\\|\<item\>$p\< < /tmp/everything$$)
done | grep " 0$" | {
- # this block gets as its input a list of constants which no references were found, one per line
+ # this block gets as its input a list of constants for which no references were found, one per line
if [ "$showall" == "yes" ]
then
echo $app
@@ -70,5 +80,6 @@
fi
fi
}
+ rm /tmp/everything$$
fi
done
diff --git a/tools/findunused/removeunusedresources b/tools/findunused/removeunusedresources
new file mode 100755
index 0000000..0c38494
--- /dev/null
+++ b/tools/findunused/removeunusedresources
@@ -0,0 +1,84 @@
+#!/bin/bash
+
+if ! which xmlstarlet > /dev/null
+then
+ echo "You need to have the 'xmlstarlet' command in your path"
+ exit
+fi
+
+apps=$1
+CWD=$(pwd)/
+if [ "$apps" = "" ]
+then
+ echo "Please specify the path to an application, or '--all' to process all applications"
+ exit
+elif [ "$apps" = "--all" ]
+then
+ apps=$ANDROID_BUILD_TOP/packages/apps/*
+fi
+
+BASE=$(pwd)/$(dirname $0)
+
+for app in $apps
+do
+ pushd $app
+ $BASE/findunusedresources -p . | {
+ read LINE NUM
+ while [ "$LINE" != "" ]
+ do
+ if [ "Z$LINE" = "Z-----------------------------------------------------------" ]
+ then
+ # skip
+ true
+ elif [ "$LINE" = "$app" ]
+ then
+ # skip
+ true
+ else
+ # try to find the missing resource
+ find res | grep -w $LINE | {
+ read RESLINE
+ while [ "$RESLINE" != "" ]
+ do
+ if [ -f $RESLINE ]
+ then
+ echo REMOVING FILE: $RESLINE
+ git rm $RESLINE > /dev/null
+ else
+ echo WARNING unexpected result for $LINE
+ fi
+ read RESLINE
+ done
+ }
+ grep -Rwl $LINE res | {
+ read RESLINE
+ while [ "$RESLINE" != "" ]
+ do
+ ISSTRING=$(echo "$RESLINE" | grep -w "strings\.xml")
+ if [ -n "$ISSTRING" ]
+ then
+ echo REMOVING STRING $LINE from $RESLINE
+ xmlstarlet ed -P -S -d "/resources/string[@name='$LINE']" $RESLINE > tf$$
+ mv tf$$ $RESLINE
+ git add $RESLINE
+ else
+ echo REMOVING $LINE from $RESLINE
+ xmlstarlet ed -P -S -d "/resources/*[@name='$LINE']" $RESLINE > tf$$
+ mv tf$$ $RESLINE
+ git add $RESLINE
+ fi
+ read RESLINE
+ done
+ }
+ fi
+ read LINE NUM
+ done
+ }
+ popd
+done
+echo
+echo "Done."
+echo "Please rebuild the updated applications to make sure that everything still builds."
+echo "After rebuilding, rerun 'findunusedresources' or 'removeunusedresources' to see if any more resources are now unused."
+echo "When you're done, you can 'git commit' the change."
+echo
diff --git a/tools/scripts/alias_rules.xml b/tools/scripts/alias_rules.xml
index 5702461..273da45 100644
--- a/tools/scripts/alias_rules.xml
+++ b/tools/scripts/alias_rules.xml
@@ -5,22 +5,22 @@
-->
<project name="alias_rules" default="package">
- <!-- No user servicable parts below. -->
+ <!-- No user serviceable parts below. -->
<!-- Input directories -->
- <property name="resource-dir" value="res" />
+ <property name="resource.dir" value="res" />
<!-- The final package file to generate -->
- <property name="out-package" value="${ant.project.name}.apk" />
+ <property name="out.package" value="${ant.project.name}.apk" />
<!-- Tools -->
- <condition property="aapt" value="${android-tools}/aapt.exe" else="${android-tools}/aapt" >
- <os family="windows"/>
+ <condition property="aapt" value="${android.tools.dir}/aapt.exe" else="${android.tools.dir}/aapt">
+ <os family="windows" />
</condition>
- <condition property="adb" value="${android-tools}/adb.exe" else="${android-tools}/adb" >
- <os family="windows"/>
+ <condition property="adb" value="${android.tools.dir}/adb.exe" else="${android.tools.dir}/adb">
+ <os family="windows" />
</condition>
- <property name="android-jar" value="${sdk-folder}/android.jar" />
+ <property name="android.jar" value="${sdk.dir}/android.jar" />
<!-- Rules -->
@@ -33,11 +33,11 @@
<arg value="-M" />
<arg value="AndroidManifest.xml" />
<arg value="-S" />
- <arg value="${resource-dir}" />
+ <arg value="${resource.dir}" />
<arg value="-I" />
- <arg value="${android-jar}" />
+ <arg value="${android.jar}" />
<arg value="-F" />
- <arg value="${out-package}" />
+ <arg value="${out.package}" />
</exec>
</target>
@@ -49,7 +49,7 @@
<echo>Sending package to default emulator...</echo>
<exec executable="${adb}" failonerror="true">
<arg value="install" />
- <arg value="${out-package}" />
+ <arg value="${out.package}" />
</exec>
</target>
diff --git a/tools/scripts/android_rules.xml b/tools/scripts/android_rules.xml
index ad36cbe..64f0e39 100644
--- a/tools/scripts/android_rules.xml
+++ b/tools/scripts/android_rules.xml
@@ -6,7 +6,7 @@
com.android.ant.AndroidInitTask
The following properties are put in place by the importing task:
- android-jar, android-aidl, aapt, aidl, and dx
+ android.jar, android.aidl, aapt, aidl, and dx
Additionnaly, the task sets up the following classpath reference:
android.target.classpath
@@ -16,184 +16,177 @@
<!-- Custom tasks -->
<taskdef name="aaptexec"
classname="com.android.ant.AaptExecLoopTask"
- classpathref="android.antlibs"/>
+ classpathref="android.antlibs" />
<taskdef name="apkbuilder"
classname="com.android.ant.ApkBuilderTask"
- classpathref="android.antlibs"/>
+ classpathref="android.antlibs" />
<!-- Properties -->
- <property name="android-tools" value="${sdk-location}/tools" />
+ <property name="android.tools.dir" location="${sdk.dir}/tools" />
<!-- Input directories -->
- <property name="source-folder" value="src" />
- <property name="gen-folder" value="gen" />
- <property name="resource-folder" value="res" />
- <property name="asset-folder" value="assets" />
- <property name="source-location" value="${basedir}/${source-folder}" />
+ <property name="source.dir" location="src" />
+ <property name="gen.dir" location="gen" />
+ <property name="resource.dir" location="res" />
+ <property name="asset.dir" location="assets" />
- <!-- folder for the 3rd party java libraries -->
- <property name="external-libs-folder" value="libs" />
+ <!-- Directory for the 3rd party java libraries -->
+ <property name="external.libs.dir" location="libs" />
- <!-- folder for the native libraries -->
- <property name="native-libs-folder" value="libs" />
+ <!-- Directory for the native libraries -->
+ <property name="native.libs.dir" location="libs" />
<!-- Output directories -->
- <property name="gen-folder" value="gen" />
- <property name="out-folder" value="bin" />
- <property name="out-classes" value="${out-folder}/classes" />
- <property name="out-classes-location" value="${basedir}/${out-classes}"/>
- <!-- out folders for a parent project if this project is an instrumentation project -->
- <property name="main-out-folder" value="../${out-folder}" />
- <property name="main-out-classes" value="${main-out-folder}/classes"/>
+ <property name="gen.dir" location="gen" />
+ <property name="out.dir" location="bin" />
+ <property name="out.classes.dir" location="${out.dir}/classes" />
+
+ <!-- Out directory for a parent project if this project is an instrumentation project -->
+ <property name="main.out.dir" location="../${out.dir}" />
+ <property name="main.out.classes.dir" location="${main.out.dir}/classes" />
<!-- Intermediate files -->
- <property name="dex-file" value="classes.dex" />
- <property name="intermediate-dex" value="${out-folder}/${dex-file}" />
- <!-- dx does not properly support incorrect / or \ based on the platform
- and Ant cannot convert them because the parameter is not a valid path.
- Because of this we have to compute different paths depending on the platform. -->
- <condition property="intermediate-dex-location"
- value="${basedir}\${intermediate-dex}"
- else="${basedir}/${intermediate-dex}" >
- <os family="windows"/>
- </condition>
+ <property name="dex.file.name" value="classes.dex" />
+ <property name="intermediate.dex.file" location="${out.dir}/${dex.file.name}" />
<!-- The final package file to generate -->
- <property name="out-debug-unaligned-package" value="${out-folder}/${ant.project.name}-debug-unaligned.apk"/>
- <property name="out-debug-package" value="${out-folder}/${ant.project.name}-debug.apk"/>
- <property name="out-unsigned-package" value="${out-folder}/${ant.project.name}-unsigned.apk"/>
- <property name="out-unaligned-package" value="${out-folder}/${ant.project.name}-unaligned.apk"/>
- <property name="out-release-package" value="${out-folder}/${ant.project.name}-release.apk"/>
+ <property name="out.debug.unaligned.package" location="${out.dir}/${ant.project.name}-debug-unaligned.apk" />
+ <property name="out.debug.package" location="${out.dir}/${ant.project.name}-debug.apk" />
+ <property name="out.unsigned.package" location="${out.dir}/${ant.project.name}-unsigned.apk" />
+ <property name="out.unaligned.package" location="${out.dir}/${ant.project.name}-unaligned.apk" />
+ <property name="out.release.package" location="${out.dir}/${ant.project.name}-release.apk" />
<!-- Tools -->
- <condition property="exe" value=".exe" else=""><os family="windows"/></condition>
- <property name="adb" value="${android-tools}/adb${exe}"/>
- <property name="zipalign" value="${android-tools}/zipalign${exe}" />
+ <condition property="exe" value=".exe" else=""><os family="windows" /></condition>
+ <property name="adb" location="${android.tools.dir}/adb${exe}" />
+ <property name="zipalign" location="${android.tools.dir}/zipalign${exe}" />
- <!-- rules -->
+ <!-- Rules -->
- <!-- Create the output directories if they don't exist yet. -->
- <target name="dirs">
+ <!-- Creates the output directories if they don't exist yet. -->
+ <target name="-dirs">
<echo>Creating output directories if needed...</echo>
- <mkdir dir="${resource-folder}" />
- <mkdir dir="${external-libs-folder}" />
- <mkdir dir="${gen-folder}" />
- <mkdir dir="${out-folder}" />
- <mkdir dir="${out-classes}" />
+ <mkdir dir="${resource.dir}" />
+ <mkdir dir="${external.libs.dir}" />
+ <mkdir dir="${gen.dir}" />
+ <mkdir dir="${out.dir}" />
+ <mkdir dir="${out.classes.dir}" />
</target>
- <!-- Generate the R.java file for this project's resources. -->
- <target name="resource-src" depends="dirs">
+ <!-- Generates the R.java file for this project's resources. -->
+ <target name="-resource-src" depends="-dirs">
<echo>Generating R.java / Manifest.java from the resources...</echo>
<exec executable="${aapt}" failonerror="true">
<arg value="package" />
<arg value="-m" />
<arg value="-J" />
- <arg path="${gen-folder}" />
+ <arg path="${gen.dir}" />
<arg value="-M" />
<arg path="AndroidManifest.xml" />
<arg value="-S" />
- <arg path="${resource-folder}" />
+ <arg path="${resource.dir}" />
<arg value="-I" />
- <arg path="${android-jar}" />
+ <arg path="${android.jar}" />
</exec>
</target>
- <!-- Generate java classes from .aidl files. -->
- <target name="aidl" depends="dirs">
+ <!-- Generates java classes from .aidl files. -->
+ <target name="-aidl" depends="-dirs">
<echo>Compiling aidl files into Java classes...</echo>
<apply executable="${aidl}" failonerror="true">
- <arg value="-p${android-aidl}" />
- <arg value="-I${source-folder}" />
- <arg value="-o${gen-folder}" />
- <fileset dir="${source-folder}">
- <include name="**/*.aidl"/>
+ <arg value="-p${android.aidl}" />
+ <arg value="-I${source.dir}" />
+ <arg value="-o${gen.dir}" />
+ <fileset dir="${source.dir}">
+ <include name="**/*.aidl" />
</fileset>
</apply>
</target>
- <!-- Compile this project's .java files into .class files. -->
- <target name="compile" depends="resource-src, aidl">
+ <!-- Compiles this project's .java files into .class files. -->
+ <target name="compile" depends="-resource-src, -aidl"
+ description="Compiles project's .java files into .class files">
<javac encoding="ascii" target="1.5" debug="true" extdirs=""
- destdir="${out-classes}"
+ destdir="${out.classes.dir}"
bootclasspathref="android.target.classpath">
- <src path="${source-folder}" />
- <src path="${gen-folder}" />
+ <src path="${source.dir}" />
+ <src path="${gen.dir}" />
<classpath>
- <fileset dir="${external-libs-folder}" includes="*.jar"/>
- <pathelement path="${main-out-classes}"/>
+ <fileset dir="${external.libs.dir}" includes="*.jar" />
+ <pathelement path="${main.out.classes.dir}" />
</classpath>
</javac>
</target>
- <!-- Convert this project's .class files into .dex files. -->
- <target name="dex" depends="compile">
- <echo>Converting compiled files and external libraries into ${out-folder}/${dex-file}...</echo>
+ <!-- Converts this project's .class files into .dex files -->
+ <target name="-dex" depends="compile">
+ <echo>Converting compiled files and external libraries into ${out.dir}/${dex.file.name}...</echo>
<apply executable="${dx}" failonerror="true" parallel="true">
<arg value="--dex" />
- <arg value="--output=${intermediate-dex-location}" />
- <arg path="${out-classes-location}" />
- <fileset dir="${external-libs-folder}" includes="*.jar"/>
+ <arg value="--output=${intermediate.dex.file}" />
+ <arg path="${out.classes.dir}" />
+ <fileset dir="${external.libs.dir}" includes="*.jar" />
</apply>
</target>
- <!-- Put the project's resources into the output package file
+ <!-- Puts the project's resources into the output package file
This actually can create multiple resource package in case
Some custom apk with specific configuration have been
declared in default.properties.
-->
- <target name="package-resources">
+ <target name="-package-resources">
<echo>Packaging resources</echo>
<aaptexec executable="${aapt}"
command="package"
manifest="AndroidManifest.xml"
- resources="${resource-folder}"
- assets="${asset-folder}"
- androidjar="${android-jar}"
- outfolder="${out-folder}"
+ resources="${resource.dir}"
+ assets="${asset.dir}"
+ androidjar="${android.jar}"
+ outfolder="${out.dir}"
basename="${ant.project.name}" />
</target>
- <!-- Package the application and (maybe) sign it with a debug key.
+ <!-- Packages the application and (maybe) sign it with a debug key.
This requires the property sign.package to be set to true or false. -->
- <target name="package">
+ <target name="-package">
<apkbuilder
- outfolder="${out-folder}"
+ outfolder="${out.dir}"
basename="${ant.project.name}"
signed="${sign.package}"
verbose="true">
- <file path="${intermediate-dex}" />
- <sourcefolder path="${source-folder}" />
- <jarfolder path="${external-libs-folder}" />
- <nativefolder path="${native-libs-folder}" />
+ <file path="${intermediate.dex.file}" />
+ <sourcefolder path="${source.dir}" />
+ <jarfolder path="${external.libs.dir}" />
+ <nativefolder path="${native.libs.dir}" />
</apkbuilder>
</target>
- <target name="no-sign">
+ <target name="-no-sign">
<property name="sign.package" value="false" />
</target>
- <target name="debug-sign">
+ <target name="-debug-sign">
<property name="sign.package" value="true" />
</target>
- <target name="debug" depends="dex, package-resources, debug-sign, package">
+ <target name="debug" depends="-dex, -package-resources, -debug-sign, -package"
+ description="Builds the application and signs it with a debug key.">
<echo>Running zip align on final apk...</echo>
<exec executable="${zipalign}" failonerror="true">
<arg value="-f" />
<arg value="4" />
- <arg path="${out-debug-unaligned-package}" />
- <arg path="${out-debug-package}" />
+ <arg path="${out.debug.unaligned.package}" />
+ <arg path="${out.debug.package}" />
</exec>
- <echo>Debug Package: ${out-debug-package}</echo>
+ <echo>Debug Package: ${out.debug.package}</echo>
</target>
- <target name="release-package" depends="dex, package-resources, no-sign, package">
+ <target name="-release-package" depends="-dex, -package-resources, -no-sign, -package">
</target>
- <target name="release.check">
+ <target name="-release-check">
<condition property="release.sign">
<and>
<isset property="key.store" />
@@ -201,80 +194,95 @@
</and>
</condition>
</target>
- <target name="release.nosign" depends="release.check" unless="release.sign">
+
+ <target name="-release-nosign" depends="-release-check" unless="release.sign">
<echo>No key.store and key.alias properties found in build.properties.</echo>
- <echo>Please sign ${out-unsigned-package} manually</echo>
+ <echo>Please sign ${out.unsigned.package} manually</echo>
<echo>and run zipalign from the Android SDK tools.</echo>
</target>
- <target name="release" depends="release-package, release.nosign" if="release.sign">
- <!-- get passwords -->
+ <target name="release" depends="-release-package, -release-nosign" if="release-sign"
+ description="Builds the application. The generated apk file must be signed before it is published.">
+
+ <!-- Gets passwords -->
<input
message="Please enter keystore password (store:${key.store}):"
- addproperty="key.store.password"/>
+ addproperty="key.store.password" />
<input
message="Please enter password for alias '${key.alias}':"
- addproperty="key.alias.password"/>
- <!-- sign the APK -->
+ addproperty="key.alias.password" />
+
+ <!-- Signs the APK -->
<echo>Signing final apk...</echo>
<signjar
- jar="${out-unsigned-package}"
- signedjar="${out-unaligned-package}"
+ jar="${out.unsigned.package}"
+ signedjar="${out.unaligned.package}"
keystore="${key.store}"
storepass="${key.store.password}"
alias="${key.alias}"
- keypass="${key.alias.password}"/>
- <!-- zip align the APK -->
+ keypass="${key.alias.password}" />
+
+ <!-- Zip aligns the APK -->
<echo>Running zip align on final apk...</echo>
<exec executable="${zipalign}" failonerror="true">
<arg value="-f" />
<arg value="4" />
- <arg path="${out-unaligned-package}" />
- <arg path="${out-release-package}" />
+ <arg path="${out.unaligned.package}" />
+ <arg path="${out.release.package}" />
</exec>
- <echo>Release Package: ${out-release-package}</echo>
+ <echo>Release Package: ${out.release.package}</echo>
</target>
- <!-- Install the package on the default emulator -->
- <target name="install" depends="debug">
- <echo>Installing ${out-debug-package} onto default emulator...</echo>
+ <!-- Installs the package on the default emulator/device -->
+ <target name="install" depends="debug"
+ description="Installs/reinstalls the debug package onto a running emulator or device. This can only be used if the application has not yet been installed.">
+ <echo>Installing ${out.debug.package} onto default emulator or device...</echo>
<exec executable="${adb}" failonerror="true">
<arg value="install" />
<arg value="-r" />
- <arg path="${out-debug-package}" />
+ <arg path="${out.debug.package}" />
</exec>
</target>
- <!-- Uinstall the package from the default emulator -->
- <target name="uninstall.check">
+ <target name="-uninstall-check">
<condition property="uninstall.run">
- <isset property="application-package" />
+ <isset property="application.package" />
</condition>
</target>
- <target name="uninstall.error" depends="uninstall.check" unless="uninstall.run">
- <echo>Unable to run 'ant unintall', application-package is not defined in build.properties</echo>
+
+ <target name="-uninstall-error" depends="-uninstall-check" unless="uninstall.run">
+ <echo>Unable to run 'ant uninstall', application.package is not defined in build.properties</echo>
</target>
- <target name="uninstall" depends="uninstall.error" if="uninstall.run">
- <echo>Uninstalling ${application-package} from the default emulator...</echo>
+
+ <!-- Uninstalls the package from the default emulator/device -->
+ <target name="uninstall" depends="-uninstall-error" if="uninstall.run" description="Uninstalls the application from a running emulator or device.">
+ <echo>Uninstalling ${application.package} from the default emulator or device...</echo>
<exec executable="${adb}" failonerror="true">
<arg value="uninstall" />
- <arg value="${application-package}" />
+ <arg value="${application.package}" />
</exec>
</target>
-
+
+ <target name="clean" description="Removes output files created by other targets.">
+ <delete dir="${out.dir}" />
+ <delete dir="${gen.dir}" />
+ </target>
+
<target name="help">
<!-- displays starts at col 13
|13 80| -->
<echo>Android Ant Build. Available targets:</echo>
<echo> help: Displays this help.</echo>
- <echo> debug: Builds the application and sign it with a debug key.</echo>
+ <echo> clean: Removes output files created by other targets.</echo>
+ <echo> compile: Compiles project's .java files into .class files.</echo>
+ <echo> debug: Builds the application and signs it with a debug key.</echo>
<echo> release: Builds the application. The generated apk file must be</echo>
<echo> signed before it is published.</echo>
<echo> install: Installs/reinstall the debug package onto a running</echo>
<echo> emulator or device.</echo>
<echo> If the application was previously installed, the</echo>
<echo> signatures must match.</echo>
- <echo> uninstall: uninstall the application from a running emulator or</echo>
+ <echo> uninstall: Uninstalls the application from a running emulator or</echo>
<echo> device.</echo>
</target>
</project>
diff --git a/tools/scripts/build.alias.template b/tools/scripts/build.alias.template
index 1043af1..d051405 100644
--- a/tools/scripts/build.alias.template
+++ b/tools/scripts/build.alias.template
@@ -8,16 +8,16 @@
<!-- The build.properties file can be created by you and is never touched
by activitycreator. If you want to manually set properties, this is
the best place to set them. -->
- <property file="build.properties"/>
+ <property file="build.properties" />
<!-- The default.properties file is created and updated by activitycreator.
It will set any properties not already defined by build.properties. -->
- <property file="default.properties"/>
+ <property file="default.properties" />
<!-- ************************************************************************************* -->
- <!-- Import the default Android build rules.
+ <!-- Import the default Android build rules.
This requires ant 1.6.0 or above. -->
- <import file="${sdk-folder}/tools/lib/alias_rules.xml" />
+ <import file="${sdk.dir}/tools/lib/alias_rules.xml" />
</project>
diff --git a/tools/scripts/build.template b/tools/scripts/build.template
index 1ed3853..ca15954 100644
--- a/tools/scripts/build.template
+++ b/tools/scripts/build.template
@@ -2,22 +2,22 @@
<project name="PROJECT_NAME" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
- It contain the path to the SDK. It should *NOT* be checked in in Version
+ It contains the path to the SDK. It should *NOT* be checked in in Version
Control Systems. -->
- <property file="local.properties"/>
+ <property file="local.properties" />
<!-- The build.properties file can be created by you and is never touched
by the 'android' tool. This is the place to change some of the default property values
used by the Ant rules.
Here are some properties you may want to change/update:
- application-package
+ application.package
the name of your application package as defined in the manifest. Used by the
'uninstall' rule.
- source-folder
- the name of the source folder. Default is 'src'.
- out-folder
- the name of the output folder. Default is 'bin'.
+ source.dir
+ the name of the source directory. Default is 'src'.
+ out.dir
+ the name of the output directory. Default is 'bin'.
Properties related to the SDK location or the project target should be updated
using the 'android' tool with the 'update' action.
@@ -26,27 +26,27 @@
should be checked in in Version Control Systems.
-->
- <property file="build.properties"/>
+ <property file="build.properties" />
<!-- The default.properties file is created and updated by the 'android' tool, as well
- as ADT.
+ as ADT.
This file is an integral part of the build system for your application and
should be checked in in Version Control Systems. -->
- <property file="default.properties"/>
+ <property file="default.properties" />
<!-- Custom Android task to deal with the project target, and import the proper rules.
This requires ant 1.6.0 or above. -->
<path id="android.antlibs">
- <pathelement path="${sdk-location}/tools/lib/anttasks.jar" />
- <pathelement path="${sdk-location}/tools/lib/sdklib.jar" />
- <pathelement path="${sdk-location}/tools/lib/androidprefs.jar" />
- <pathelement path="${sdk-location}/tools/lib/apkbuilder.jar" />
- <pathelement path="${sdk-location}/tools/lib/jarutils.jar" />
+ <pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
+ <pathelement path="${sdk.dir}/tools/lib/sdklib.jar" />
+ <pathelement path="${sdk.dir}/tools/lib/androidprefs.jar" />
+ <pathelement path="${sdk.dir}/tools/lib/apkbuilder.jar" />
+ <pathelement path="${sdk.dir}/tools/lib/jarutils.jar" />
</path>
<taskdef name="setup"
classname="com.android.ant.SetupTask"
- classpathref="android.antlibs"/>
+ classpathref="android.antlibs" />
<!-- Execute the Android Setup task that will setup some properties specific to the target,
and import the build rules files.
@@ -58,7 +58,7 @@
- copy the content of the main node <project> from android_rules.xml
- paste it in this build.xml below the <setup /> task.
- disable the import by changing the setup task below to <setup import="false" />
-
+
This will ensure that the properties are setup correctly but that your customized
build steps are used.
-->
diff --git a/tools/scripts/doc_source.properties b/tools/scripts/doc_source.properties
index 9382836..e6d6915 100644
--- a/tools/scripts/doc_source.properties
+++ b/tools/scripts/doc_source.properties
@@ -1,4 +1,6 @@
Pkg.UserSrc=false
-Platform.Version=1.6
+Platform.Version=Eclair
Pkg.Revision=1
AndroidVersion.ApiLevel=4
+AndroidVersion.CodeName=Eclair
+
diff --git a/tools/scripts/platform_source.properties b/tools/scripts/platform_source.properties
index 49dc3ae..148c364 100644
--- a/tools/scripts/platform_source.properties
+++ b/tools/scripts/platform_source.properties
@@ -1,5 +1,6 @@
-Pkg.Desc=Android SDK Platform 1.6_r1
+Pkg.Desc=Android SDK Platform Eclair
Pkg.UserSrc=false
-Platform.Version=1.6
+Platform.Version=Eclair
Pkg.Revision=1
AndroidVersion.ApiLevel=4
+AndroidVersion.CodeName=Eclair
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkConfigurationHelper.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkConfigurationHelper.java
index eeab46a..4ba6fa6 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkConfigurationHelper.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkConfigurationHelper.java
@@ -16,91 +16,33 @@
package com.android.sdklib.internal.project;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.Map.Entry;
/**
* Helper class to read and write Apk Configuration into a {@link ProjectProperties} file.
*/
public class ApkConfigurationHelper {
- /** Prefix for property names for config definition. This prevents having config named
- * after other valid properties such as "target". */
- final static String CONFIG_PREFIX = "apk-config-";
-
/**
- * Reads the Apk Configurations from a {@link ProjectProperties} file and returns them as a map.
- * <p/>If there are no defined configurations, the returned map will be empty.
- * @return a map of apk configurations. The map contains (name, filter) where name is
- * the name of the configuration (a-zA-Z0-9 only), and filter is the comma separated list of
- * resource configuration to include in the apk (see aapt -c)
+ * Reads the project settings from a {@link ProjectProperties} file and returns them as a
+ * {@link ApkSettings} object.
*/
- public static Map<String, String> getConfigs(ProjectProperties properties) {
- HashMap<String, String> configMap = new HashMap<String, String>();
+ public static ApkSettings getSettings(ProjectProperties properties) {
+ ApkSettings apkSettings = new ApkSettings();
- // get the list of configs.
- String configList = properties.getProperty(ProjectProperties.PROPERTY_APK_CONFIGS);
- if (configList != null) {
- // this is a comma separated list
- String[] configs = configList.split(","); //$NON-NLS-1$
-
- // read the value of each config and store it in a map
- for (String config : configs) {
- config = config.trim();
- String configValue = properties.getProperty(CONFIG_PREFIX + config);
- if (configValue != null) {
- configMap.put(config, configValue);
- }
- }
- }
-
- return configMap;
+ boolean splitByDensity = Boolean.parseBoolean(properties.getProperty(
+ ProjectProperties.PROPERTY_SPLIT_BY_DENSITY));
+ apkSettings.setSplitByDensity(splitByDensity);
+
+
+ return apkSettings;
}
-
+
/**
- * Writes the Apk Configurations from a given map into a {@link ProjectProperties}.
- * @param properties the {@link ProjectProperties} in which to store the apk configurations.
- * @param configMap a map of apk configurations. The map contains (name, filter) where name is
- * the name of the configuration (a-zA-Z0-9 only), and filter is the comma separated list of
- * resource configuration to include in the apk (see aapt -c)
- * @return true if the {@link ProjectProperties} contained Apk Configuration that were not
- * present in the map.
+ * Sets the content of a {@link ApkSettings} into a {@link ProjectProperties}.
+ * @param properties the {@link ProjectProperties} in which to store the settings.
+ * @param settings the project settings to store.
*/
- public static boolean setConfigs(ProjectProperties properties, Map<String, String> configMap) {
- // load the current configs, in order to remove the value properties for each of them
- // in case a config was removed.
-
- // get the list of configs.
- String configList = properties.getProperty(ProjectProperties.PROPERTY_APK_CONFIGS);
-
- boolean hasRemovedConfig = false;
-
- if (configList != null) {
- // this is a comma separated list
- String[] configs = configList.split(","); //$NON-NLS-1$
-
- for (String config : configs) {
- config = config.trim();
- if (configMap.containsKey(config) == false) {
- hasRemovedConfig = true;
- properties.removeProperty(CONFIG_PREFIX + config);
- }
- }
- }
-
- // now add the properties.
- Set<Entry<String, String>> entrySet = configMap.entrySet();
- StringBuilder sb = new StringBuilder();
- for (Entry<String, String> entry : entrySet) {
- if (sb.length() > 0) {
- sb.append(",");
- }
- sb.append(entry.getKey());
- properties.setProperty(CONFIG_PREFIX + entry.getKey(), entry.getValue());
- }
- properties.setProperty(ProjectProperties.PROPERTY_APK_CONFIGS, sb.toString());
-
- return hasRemovedConfig;
+ public static void setProperties(ProjectProperties properties, ApkSettings settings) {
+ properties.setProperty(ProjectProperties.PROPERTY_SPLIT_BY_DENSITY,
+ Boolean.toString(settings.isSplitByDpi()));
}
}
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkSettings.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkSettings.java
new file mode 100644
index 0000000..c96e223
--- /dev/null
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkSettings.java
@@ -0,0 +1,58 @@
+/*
+ * 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.sdklib.internal.project;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Settings for multiple APK generation.
+ */
+public class ApkSettings {
+ private boolean mSplitByDpi = false;
+
+ public ApkSettings() {
+ }
+
+ /**
+ * Returns a map of configuration filters to be used by the -c option of aapt.
+ * <p/>The map stores (key, value) pairs where the keys is a filename modifier and the value
+ * is the parameter to pass to aapt through the -c option.
+ * @return a map, always. It can however be empty.
+ */
+ public Map<String, String> getResourceFilters() {
+ Map<String, String> map = new HashMap<String, String>();
+ if (mSplitByDpi) {
+ map.put("hdpi", "hdpi,nodpi");
+ map.put("mdpi", "mdpi,nodpi");
+ map.put("ldpi", "ldpi,nodpi");
+ }
+
+ return map;
+ }
+
+ /**
+ * Indicates whether APKs should be generate for each dpi level.
+ */
+ public boolean isSplitByDpi() {
+ return mSplitByDpi;
+ }
+
+ public void setSplitByDensity(boolean split) {
+ mSplitByDpi = split;
+ }
+}
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java
index f2b73c0..5b35d50 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java
@@ -35,9 +35,16 @@
public final class ProjectProperties {
/** The property name for the project target */
public final static String PROPERTY_TARGET = "target";
- public final static String PROPERTY_APK_CONFIGS = "apk-configurations";
- public final static String PROPERTY_SDK = "sdk-location";
- public final static String PROPERTY_APP_PACKAGE = "application-package";
+
+ public final static String PROPERTY_SDK = "sdk.dir";
+ // LEGACY - compatibility with 1.6 and before
+ public final static String PROPERTY_SDK_LEGACY = "sdk-location";
+
+ public final static String PROPERTY_APP_PACKAGE = "application.package";
+ // LEGACY - compatibility with 1.6 and before
+ public final static String PROPERTY_APP_PACKAGE_LEGACY = "application-package";
+
+ public final static String PROPERTY_SPLIT_BY_DENSITY = "split.density";
public static enum PropertyType {
BUILD("build.properties", BUILD_HEADER),
@@ -88,8 +95,8 @@
"# This file is only used by the Ant script.\n" +
"\n" +
"# You can use this to override default values such as\n" +
- "# 'source-folder' for the location of your java source folder and\n" +
- "# 'out-folder' for the location of your output folder.\n" +
+ "# 'source.dir' for the location of your java source folder and\n" +
+ "# 'out.dir' for the location of your output folder.\n" +
"\n" +
"# You can also use it define how the release builds are signed by declaring\n" +
"# the following properties:\n" +
@@ -103,17 +110,8 @@
// 1-------10--------20--------30--------40--------50--------60--------70--------80
COMMENT_MAP.put(PROPERTY_TARGET,
"# Project target.\n");
- COMMENT_MAP.put(PROPERTY_APK_CONFIGS,
- "# apk configurations. This property allows creation of APK files with limited\n" +
- "# resources. For example, if your application contains many locales and\n" +
- "# you wish to release multiple smaller apks instead of a large one, you can\n" +
- "# define configuration to create apks with limited language sets.\n" +
- "# Format is a comma separated list of configuration names. For each\n" +
- "# configuration, a property will declare the resource configurations to\n" +
- "# include. Example:\n" +
- "# " + PROPERTY_APK_CONFIGS +"=european,northamerica\n" +
- "# " + ApkConfigurationHelper.CONFIG_PREFIX + "european=en,fr,it,de,es\n" +
- "# " + ApkConfigurationHelper.CONFIG_PREFIX + "northamerica=en,es\n");
+ COMMENT_MAP.put(PROPERTY_SPLIT_BY_DENSITY,
+ "# Indicates whether an apk should be generated for each density.\n");
COMMENT_MAP.put(PROPERTY_SDK,
"# location of the SDK. This is only used by Ant\n" +
"# For customization when using a Version Control System, please read the\n" +
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigEditDialog.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigEditDialog.java
deleted file mode 100644
index be241a5..0000000
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigEditDialog.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.sdkuilib.internal.widgets;
-
-import org.eclipse.jface.dialogs.Dialog;
-import org.eclipse.jface.dialogs.IDialogConstants;
-import org.eclipse.jface.window.Window;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.events.VerifyEvent;
-import org.eclipse.swt.events.VerifyListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
-
-/**
- * Edit dialog to create/edit APK configuration. The dialog displays 2 text fields for the config
- * name and its filter.
- */
-class ApkConfigEditDialog extends Dialog implements ModifyListener, VerifyListener {
-
- private String mName;
- private String mFilter;
- private Text mNameField;
- private Text mFilterField;
- private Button mOkButton;
-
- /**
- * Creates an edit dialog with optional initial values for the name and filter.
- * @param name optional value for the name. Can be null.
- * @param filter optional value for the filter. Can be null.
- * @param parentShell the parent shell.
- */
- protected ApkConfigEditDialog(String name, String filter, Shell parentShell) {
- super(parentShell);
- mName = name;
- mFilter = filter;
- }
-
- /**
- * Returns the name of the config. This is only valid if the user clicked OK and {@link #open()}
- * returned {@link Window#OK}
- */
- public String getName() {
- return mName;
- }
-
- /**
- * Returns the filter for the config. This is only valid if the user clicked OK and
- * {@link #open()} returned {@link Window#OK}
- */
- public String getFilter() {
- return mFilter;
- }
-
- @Override
- protected Control createContents(Composite parent) {
- Control control = super.createContents(parent);
-
- mOkButton = getButton(IDialogConstants.OK_ID);
- validateButtons();
-
- return control;
- }
-
- @Override
- protected Control createDialogArea(Composite parent) {
- Composite composite = new Composite(parent, SWT.NONE);
- GridLayout layout;
- composite.setLayout(layout = new GridLayout(2, false));
- layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
- layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
- layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
- layout.horizontalSpacing = convertHorizontalDLUsToPixels(
- IDialogConstants.HORIZONTAL_SPACING);
-
- composite.setLayoutData(new GridData(GridData.FILL_BOTH));
-
- Label l = new Label(composite, SWT.NONE);
- l.setText("Name");
-
- mNameField = new Text(composite, SWT.BORDER);
- mNameField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- mNameField.addVerifyListener(this);
- if (mName != null) {
- mNameField.setText(mName);
- }
- mNameField.addModifyListener(this);
-
- l = new Label(composite, SWT.NONE);
- l.setText("Filter");
-
- mFilterField = new Text(composite, SWT.BORDER);
- mFilterField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- if (mFilter != null) {
- mFilterField.setText(mFilter);
- }
- mFilterField.addVerifyListener(this);
- mFilterField.addModifyListener(this);
-
- applyDialogFont(composite);
- return composite;
- }
-
- /**
- * Validates the OK button based on the content of the 2 text fields.
- */
- private void validateButtons() {
- mOkButton.setEnabled(mNameField.getText().trim().length() > 0 &&
- mFilterField.getText().trim().length() > 0);
- }
-
- @Override
- protected void okPressed() {
- mName = mNameField.getText();
- mFilter = mFilterField.getText().trim();
- super.okPressed();
- }
-
- /**
- * Callback for text modification in the 2 text fields.
- */
- public void modifyText(ModifyEvent e) {
- validateButtons();
- }
-
- /**
- * Callback to ensure the content of the text field are proper.
- */
- public void verifyText(VerifyEvent e) {
- Text source = ((Text)e.getSource());
- if (source == mNameField) {
- // check for a-zA-Z0-9.
- final String text = e.text;
- final int len = text.length();
- for (int i = 0 ; i < len; i++) {
- char letter = text.charAt(i);
- if (letter > 255 || Character.isLetterOrDigit(letter) == false) {
- e.doit = false;
- return;
- }
- }
- } else if (source == mFilterField) {
- // we can't validate the content as its typed, but we can at least ensure the characters
- // are valid. Same as mNameFiled + the comma.
- final String text = e.text;
- final int len = text.length();
- for (int i = 0 ; i < len; i++) {
- char letter = text.charAt(i);
- if (letter > 255 || (Character.isLetterOrDigit(letter) == false && letter != ',')) {
- e.doit = false;
- return;
- }
- }
- }
- }
-}
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigWidget.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigWidget.java
deleted file mode 100644
index a05f9bd..0000000
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigWidget.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.sdkuilib.internal.widgets;
-
-import org.eclipse.jface.dialogs.Dialog;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ControlAdapter;
-import org.eclipse.swt.events.ControlEvent;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Table;
-import org.eclipse.swt.widgets.TableColumn;
-import org.eclipse.swt.widgets.TableItem;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * The APK Configuration widget is a table that is added to the given parent composite.
- * <p/>
- * To use, create it using {@link #ApkConfigWidget(Composite)} then
- * call {@link #fillTable(Map)} to set the initial list of configurations.
- */
-public class ApkConfigWidget {
- private final static int INDEX_NAME = 0;
- private final static int INDEX_FILTER = 1;
-
- private Table mApkConfigTable;
- private Button mEditButton;
- private Button mDelButton;
-
- public ApkConfigWidget(final Composite parent) {
- final Composite apkConfigComp = new Composite(parent, SWT.NONE);
- apkConfigComp.setLayoutData(new GridData(GridData.FILL_BOTH));
- apkConfigComp.setLayout(new GridLayout(2, false));
-
- mApkConfigTable = new Table(apkConfigComp, SWT.FULL_SELECTION | SWT.SINGLE | SWT.BORDER);
- mApkConfigTable.setHeaderVisible(true);
- mApkConfigTable.setLinesVisible(true);
-
- GridData data = new GridData();
- data.grabExcessVerticalSpace = true;
- data.grabExcessHorizontalSpace = true;
- data.horizontalAlignment = GridData.FILL;
- data.verticalAlignment = GridData.FILL;
- mApkConfigTable.setLayoutData(data);
-
- // create the table columns
- final TableColumn column0 = new TableColumn(mApkConfigTable, SWT.NONE);
- column0.setText("Name");
- column0.setWidth(100);
- final TableColumn column1 = new TableColumn(mApkConfigTable, SWT.NONE);
- column1.setText("Configuration");
- column1.setWidth(100);
-
- Composite buttonComp = new Composite(apkConfigComp, SWT.NONE);
- buttonComp.setLayoutData(new GridData(GridData.FILL_VERTICAL));
- GridLayout gl;
- buttonComp.setLayout(gl = new GridLayout(1, false));
- gl.marginHeight = gl.marginWidth = 0;
-
- Button newButton = new Button(buttonComp, SWT.PUSH | SWT.FLAT);
- newButton.setText("New...");
- newButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
-
- mEditButton = new Button(buttonComp, SWT.PUSH | SWT.FLAT);
- mEditButton.setText("Edit...");
- mEditButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
-
- mDelButton = new Button(buttonComp, SWT.PUSH | SWT.FLAT);
- mDelButton.setText("Delete");
- mDelButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
-
- newButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- ApkConfigEditDialog dlg = new ApkConfigEditDialog(null /*name*/, null /*filter*/,
- apkConfigComp.getShell());
- if (dlg.open() == Dialog.OK) {
- TableItem item = new TableItem(mApkConfigTable, SWT.NONE);
- item.setText(INDEX_NAME, dlg.getName());
- item.setText(INDEX_FILTER, dlg.getFilter());
-
- onSelectionChanged();
- }
- }
- });
-
- mEditButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- // get the current selection (single mode so we don't care about any item beyond
- // index 0).
- TableItem[] items = mApkConfigTable.getSelection();
- if (items.length != 0) {
- ApkConfigEditDialog dlg = new ApkConfigEditDialog(
- items[0].getText(INDEX_NAME), items[0].getText(INDEX_FILTER),
- apkConfigComp.getShell());
- if (dlg.open() == Dialog.OK) {
- items[0].setText(INDEX_NAME, dlg.getName());
- items[0].setText(INDEX_FILTER, dlg.getFilter());
- }
- }
- }
- });
-
- mDelButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- // get the current selection (single mode so we don't care about any item beyond
- // index 0).
- int[] indices = mApkConfigTable.getSelectionIndices();
- if (indices.length != 0) {
- TableItem item = mApkConfigTable.getItem(indices[0]);
- if (MessageDialog.openQuestion(parent.getShell(),
- "Apk Configuration deletion",
- String.format(
- "Are you sure you want to delete configuration '%1$s'?",
- item.getText(INDEX_NAME)))) {
- // delete the item.
- mApkConfigTable.remove(indices[0]);
-
- onSelectionChanged();
- }
- }
- }
- });
-
- // Add a listener to resize the column to the full width of the table
- mApkConfigTable.addControlListener(new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- Rectangle r = mApkConfigTable.getClientArea();
- column0.setWidth(r.width * 30 / 100); // 30%
- column1.setWidth(r.width * 70 / 100); // 70%
- }
- });
-
- // add a selection listener on the table, to enable/disable buttons.
- mApkConfigTable.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onSelectionChanged();
- }
- });
- }
-
- public void fillTable(Map<String, String> apkConfigMap) {
- // get the names in a list so that we can sort them.
- if (apkConfigMap != null) {
- Set<String> keys = apkConfigMap.keySet();
- String[] keyArray = keys.toArray(new String[keys.size()]);
- Arrays.sort(keyArray);
-
- for (String key : keyArray) {
- TableItem item = new TableItem(mApkConfigTable, SWT.NONE);
- item.setText(INDEX_NAME, key);
- item.setText(INDEX_FILTER, apkConfigMap.get(key));
- }
- }
-
- onSelectionChanged();
- }
-
- public Map<String, String> getApkConfigs() {
- // go through all the items from the table and fill a new map
- HashMap<String, String> map = new HashMap<String, String>();
-
- TableItem[] items = mApkConfigTable.getItems();
- for (TableItem item : items) {
- map.put(item.getText(INDEX_NAME), item.getText(INDEX_FILTER));
- }
-
- return map;
- }
-
- /**
- * Handles table selection changes.
- */
- private void onSelectionChanged() {
- if (mApkConfigTable.getSelectionCount() > 0) {
- mEditButton.setEnabled(true);
- mDelButton.setEnabled(true);
- } else {
- mEditButton.setEnabled(false);
- mDelButton.setEnabled(false);
- }
- }
-}