summaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/BackupRestoreConfirmation/Android.mk31
-rw-r--r--packages/BackupRestoreConfirmation/AndroidManifest.xml33
-rw-r--r--packages/BackupRestoreConfirmation/res/layout/confirm_backup.xml54
-rw-r--r--packages/BackupRestoreConfirmation/res/layout/confirm_restore.xml54
-rw-r--r--packages/BackupRestoreConfirmation/res/values/strings.xml32
-rw-r--r--packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java242
-rw-r--r--packages/DefaultContainerService/jni/Android.mk2
-rw-r--r--packages/DefaultContainerService/res/values-am/strings.xml24
-rw-r--r--packages/DefaultContainerService/res/values-sw/strings.xml24
-rw-r--r--packages/DefaultContainerService/res/values-zu/strings.xml24
-rw-r--r--packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java313
-rw-r--r--packages/SettingsProvider/AndroidManifest.xml1
-rw-r--r--packages/SettingsProvider/res/values-af/strings.xml23
-rw-r--r--packages/SettingsProvider/res/values-am/strings.xml23
-rw-r--r--packages/SettingsProvider/res/values-ms/strings.xml23
-rw-r--r--packages/SettingsProvider/res/values-sw/strings.xml23
-rw-r--r--packages/SettingsProvider/res/values-zu/strings.xml23
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java2
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java167
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java6
-rw-r--r--packages/SharedStorageBackup/Android.mk33
-rw-r--r--packages/SharedStorageBackup/AndroidManifest.xml29
-rw-r--r--packages/SharedStorageBackup/proguard.flags1
-rw-r--r--packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java93
-rw-r--r--packages/SystemUI/res/drawable-hdpi/recents_bg_protect_tile.pngbin0 -> 7927 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/recents_blue_glow.9.pngbin0 -> 9784 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/recents_callout_line.pngbin0 -> 114 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg.pngbin0 -> 3981 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/status_bar_background.9.pngbin3233 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/status_bar_close_on.9.pngbin0 -> 1262 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/status_bar_veto_normal.pngbin0 -> 1139 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/status_bar_veto_pressed.pngbin0 -> 288 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/statusbar_background.9.pngbin0 -> 2846 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/panel_notification.pngbin7854 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/recents_bg_protect_tile.pngbin0 -> 7927 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/recents_blue_glow.9.pngbin0 -> 9784 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/recents_callout_line.pngbin0 -> 114 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg.pngbin0 -> 3981 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.pngbin0 -> 2056 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/status_bar_background.9.pngbin204 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/statusbar_background.9.pngbin0 -> 2849 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/panel_notification.pngbin243 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.pngbin0 -> 4050 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.pngbin0 -> 7760 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.pngbin0 -> 2642 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.pngbin0 -> 4140 bytes
-rw-r--r--packages/SystemUI/res/drawable/ic_sysbar_zoom.xml (renamed from packages/SystemUI/res/drawable/panel_notification_tiled.xml)13
-rw-r--r--packages/SystemUI/res/layout-land/status_bar_recent_item.xml79
-rw-r--r--packages/SystemUI/res/layout-land/status_bar_recent_panel.xml84
-rw-r--r--packages/SystemUI/res/layout-sw600dp/status_bar.xml7
-rw-r--r--packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml8
-rw-r--r--packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml33
-rw-r--r--packages/SystemUI/res/layout/navigation_bar.xml130
-rw-r--r--packages/SystemUI/res/layout/status_bar_notification_row.xml51
-rw-r--r--packages/SystemUI/res/layout/status_bar_recent_item.xml90
-rw-r--r--packages/SystemUI/res/layout/status_bar_recent_panel.xml84
-rw-r--r--packages/SystemUI/res/layout/status_bar_tracking.xml13
-rw-r--r--packages/SystemUI/res/values-af/strings.xml88
-rw-r--r--packages/SystemUI/res/values-am/strings.xml87
-rw-r--r--packages/SystemUI/res/values-land/dimens.xml37
-rw-r--r--packages/SystemUI/res/values-large/dimens.xml26
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml85
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml87
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml87
-rw-r--r--packages/SystemUI/res/values/colors.xml1
-rw-r--r--packages/SystemUI/res/values/config.xml5
-rw-r--r--packages/SystemUI/res/values/dimens.xml16
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerUI.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/Choreographer.java131
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java307
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentsListView.java92
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java)295
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java309
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/carousel/RecentApplicationsActivity.java (renamed from packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java)7
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/carousel/RecentApplicationsCarouselView.java (renamed from packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsCarouselView.java)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java114
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java133
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java19
-rw-r--r--packages/TtsService/Android.mk15
-rwxr-xr-xpackages/TtsService/AndroidManifest.xml17
-rw-r--r--packages/TtsService/MODULE_LICENSE_APACHE20
-rw-r--r--packages/TtsService/NOTICE190
-rwxr-xr-xpackages/TtsService/jni/Android.mk30
-rw-r--r--packages/TtsService/jni/android_tts_SynthProxy.cpp1073
-rw-r--r--packages/TtsService/proguard.flags5
-rw-r--r--packages/TtsService/res/drawable-hdpi/ic_launcher_text_to_speech.pngbin6931 -> 0 bytes
-rw-r--r--packages/TtsService/res/drawable-mdpi/ic_launcher_text_to_speech.pngbin6809 -> 0 bytes
-rwxr-xr-xpackages/TtsService/src/android/tts/SynthProxy.java238
-rwxr-xr-xpackages/TtsService/src/android/tts/TtsService.java1503
-rw-r--r--packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java19
94 files changed, 3505 insertions, 3535 deletions
diff --git a/packages/BackupRestoreConfirmation/Android.mk b/packages/BackupRestoreConfirmation/Android.mk
new file mode 100644
index 0000000..e775b44
--- /dev/null
+++ b/packages/BackupRestoreConfirmation/Android.mk
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := BackupRestoreConfirmation
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
+########################
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/packages/BackupRestoreConfirmation/AndroidManifest.xml b/packages/BackupRestoreConfirmation/AndroidManifest.xml
new file mode 100644
index 0000000..19848f6
--- /dev/null
+++ b/packages/BackupRestoreConfirmation/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 2011, 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.backupconfirm" >
+
+ <uses-permission android:name="android.permission.BACKUP" />
+
+ <application android:allowClearUserData="false"
+ android:allowBackup="false"
+ android:permission="android.permission.CONFIRM_FULL_BACKUP" >
+
+ <activity android:name=".BackupRestoreConfirmation"
+ android:windowSoftInputMode="stateAlwaysHidden"
+ android:excludeFromRecents="true"
+ android:exported="true" >
+ </activity>
+ </application>
+</manifest>
diff --git a/packages/BackupRestoreConfirmation/res/layout/confirm_backup.xml b/packages/BackupRestoreConfirmation/res/layout/confirm_backup.xml
new file mode 100644
index 0000000..a4564e6
--- /dev/null
+++ b/packages/BackupRestoreConfirmation/res/layout/confirm_backup.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+/*
+ * Copyright (C) 2011, 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.
+ */
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dp" >
+
+ <TextView android:id="@+id/confirm_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="30dp"
+ android:text="@string/backup_confirm_text" />
+
+ <TextView android:id="@+id/package_name"
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:layout_marginLeft="30dp"
+ android:layout_below="@id/confirm_text"
+ android:layout_marginBottom="30dp" />
+
+ <Button android:id="@+id/button_allow"
+ android:filterTouchesWhenObscured="true"
+ android:text="@string/allow_backup_button_label"
+ android:layout_below="@id/package_name"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content" />
+
+ <Button android:id="@+id/button_deny"
+ android:text="@string/deny_backup_button_label"
+ android:layout_below="@id/package_name"
+ android:layout_toRightOf="@id/button_allow"
+ android:layout_alignTop="@id/button_allow"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content" />
+
+</RelativeLayout>
diff --git a/packages/BackupRestoreConfirmation/res/layout/confirm_restore.xml b/packages/BackupRestoreConfirmation/res/layout/confirm_restore.xml
new file mode 100644
index 0000000..ca99ae1
--- /dev/null
+++ b/packages/BackupRestoreConfirmation/res/layout/confirm_restore.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+/*
+ * Copyright (C) 2011, 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.
+ */
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dp" >
+
+ <TextView android:id="@+id/confirm_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="30dp"
+ android:text="@string/restore_confirm_text" />
+
+ <TextView android:id="@+id/package_name"
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:layout_marginLeft="30dp"
+ android:layout_below="@id/confirm_text"
+ android:layout_marginBottom="30dp" />
+
+ <Button android:id="@+id/button_allow"
+ android:filterTouchesWhenObscured="true"
+ android:text="@string/allow_restore_button_label"
+ android:layout_below="@id/package_name"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content" />
+
+ <Button android:id="@+id/button_deny"
+ android:text="@string/deny_restore_button_label"
+ android:layout_below="@id/package_name"
+ android:layout_toRightOf="@id/button_allow"
+ android:layout_alignTop="@id/button_allow"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content" />
+
+</RelativeLayout>
diff --git a/packages/BackupRestoreConfirmation/res/values/strings.xml b/packages/BackupRestoreConfirmation/res/values/strings.xml
new file mode 100644
index 0000000..3d85e86
--- /dev/null
+++ b/packages/BackupRestoreConfirmation/res/values/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Text for message to user that a full backup has been requested, and must be confirmed. -->
+ <string name="backup_confirm_text">A full backup of all data to a connected desktop computer has been requested. Do you want to allow this to happen\?\n\nIf you did not request the backup yourself, do not allow the operation to proceed.</string>
+ <!-- Button to allow a requested full backup to occur -->
+ <string name="allow_backup_button_label">Back up my data</string>
+ <!-- Button to refuse to allow the requested full backup -->
+ <string name="deny_backup_button_label">Do not back up</string>
+
+ <!-- Text for message to user that a full restore has been requested, and must be confirmed. -->
+ <string name="restore_confirm_text">A full restore of all data from a connected desktop computer has been requested. Do you want to allow this to happen\?\n\nIf you did not request the restore yourself, do not allow the operation to proceed. This will replace any data currently on the device!</string>
+ <!-- Button to allow a requested full restore to occur -->
+ <string name="allow_restore_button_label">Restore my data</string>
+ <!-- Button to refuse to allow the requested full restore -->
+ <string name="deny_restore_button_label">Do not restore</string>
+
+</resources>
diff --git a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
new file mode 100644
index 0000000..ed413e6
--- /dev/null
+++ b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2011 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.backupconfirm;
+
+import android.app.Activity;
+import android.app.backup.FullBackup;
+import android.app.backup.IBackupManager;
+import android.app.backup.IFullBackupRestoreObserver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Confirm with the user that a requested full backup/restore operation is legitimate.
+ * Any attempt to perform a full backup/restore will launch this UI and wait for a
+ * designated timeout interval (nominally 30 seconds) for the user to confirm. If the
+ * user fails to respond within the timeout period, or explicitly refuses the operation
+ * within the UI presented here, no data will be transferred off the device.
+ *
+ * Note that the fully scoped name of this class is baked into the backup manager service.
+ *
+ * @hide
+ */
+public class BackupRestoreConfirmation extends Activity {
+ static final String TAG = "BackupRestoreConfirmation";
+ static final boolean DEBUG = true;
+
+ static final int MSG_START_BACKUP = 1;
+ static final int MSG_BACKUP_PACKAGE = 2;
+ static final int MSG_END_BACKUP = 3;
+ static final int MSG_START_RESTORE = 11;
+ static final int MSG_RESTORE_PACKAGE = 12;
+ static final int MSG_END_RESTORE = 13;
+ static final int MSG_TIMEOUT = 100;
+
+ Handler mHandler;
+ IBackupManager mBackupManager;
+ FullObserver mObserver;
+ int mToken;
+ boolean mDidAcknowledge;
+
+ TextView mStatusView;
+ Button mAllowButton;
+ Button mDenyButton;
+
+ // Handler for dealing with observer callbacks on the main thread
+ class ObserverHandler extends Handler {
+ Context mContext;
+ ObserverHandler(Context context) {
+ mContext = context;
+ mDidAcknowledge = false;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_START_BACKUP: {
+ Toast.makeText(mContext, "!!! Backup starting !!!", Toast.LENGTH_LONG).show();
+ }
+ break;
+
+ case MSG_BACKUP_PACKAGE: {
+ String name = (String) msg.obj;
+ mStatusView.setText(name);
+ }
+ break;
+
+ case MSG_END_BACKUP: {
+ Toast.makeText(mContext, "!!! Backup ended !!!", Toast.LENGTH_SHORT).show();
+ finish();
+ }
+ break;
+
+ case MSG_START_RESTORE: {
+ Toast.makeText(mContext, "!!! Restore starting !!!", Toast.LENGTH_LONG).show();
+ }
+ break;
+
+ case MSG_RESTORE_PACKAGE: {
+ String name = (String) msg.obj;
+ mStatusView.setText(name);
+ }
+ break;
+
+ case MSG_END_RESTORE: {
+ Toast.makeText(mContext, "!!! Restore ended !!!", Toast.LENGTH_SHORT).show();
+ finish();
+ }
+ break;
+
+ case MSG_TIMEOUT: {
+ Toast.makeText(mContext, "!!! TIMED OUT !!!", Toast.LENGTH_LONG).show();
+ }
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ final Intent intent = getIntent();
+ final String action = intent.getAction();
+
+ int layoutId;
+ if (action.equals(FullBackup.FULL_BACKUP_INTENT_ACTION)) {
+ layoutId = R.layout.confirm_backup;
+ } else if (action.equals(FullBackup.FULL_RESTORE_INTENT_ACTION)) {
+ layoutId = R.layout.confirm_restore;
+ } else {
+ Slog.w(TAG, "Backup/restore confirmation activity launched with invalid action!");
+ finish();
+ return;
+ }
+
+ mToken = intent.getIntExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, -1);
+ if (mToken < 0) {
+ Slog.e(TAG, "Backup/restore confirmation requested but no token passed!");
+ finish();
+ return;
+ }
+
+ mBackupManager = IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE));
+
+ mHandler = new ObserverHandler(getApplicationContext());
+ mObserver = new FullObserver();
+
+ setContentView(layoutId);
+
+ // Same resource IDs for each layout variant (backup / restore)
+ mStatusView = (TextView) findViewById(R.id.package_name);
+ mAllowButton = (Button) findViewById(R.id.button_allow);
+ mDenyButton = (Button) findViewById(R.id.button_deny);
+
+ mAllowButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ sendAcknowledgement(mToken, true, mObserver);
+ mAllowButton.setEnabled(false);
+ mDenyButton.setEnabled(false);
+ }
+ });
+
+ mDenyButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ sendAcknowledgement(mToken, false, mObserver);
+ mAllowButton.setEnabled(false);
+ mDenyButton.setEnabled(false);
+ }
+ });
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+
+ // We explicitly equate departure from the UI with refusal. This includes the
+ // implicit configuration-changed stop/restart cycle.
+ sendAcknowledgement(mToken, false, null);
+ finish();
+ }
+
+ void sendAcknowledgement(int token, boolean allow, IFullBackupRestoreObserver observer) {
+ if (!mDidAcknowledge) {
+ mDidAcknowledge = true;
+ try {
+ mBackupManager.acknowledgeFullBackupOrRestore(mToken, true, mObserver);
+ } catch (RemoteException e) {
+ // TODO: bail gracefully if we can't contact the backup manager
+ }
+ }
+ }
+
+ /**
+ * The observer binder for showing backup/restore progress. This binder just bounces
+ * the notifications onto the main thread.
+ */
+ class FullObserver extends IFullBackupRestoreObserver.Stub {
+ //
+ // IFullBackupRestoreObserver implementation
+ //
+ @Override
+ public void onStartBackup() throws RemoteException {
+ mHandler.sendEmptyMessage(MSG_START_BACKUP);
+ }
+
+ @Override
+ public void onBackupPackage(String name) throws RemoteException {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_BACKUP_PACKAGE, name));
+ }
+
+ @Override
+ public void onEndBackup() throws RemoteException {
+ mHandler.sendEmptyMessage(MSG_END_BACKUP);
+ }
+
+ @Override
+ public void onStartRestore() throws RemoteException {
+ mHandler.sendEmptyMessage(MSG_START_RESTORE);
+ }
+
+ @Override
+ public void onRestorePackage(String name) throws RemoteException {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_RESTORE_PACKAGE, name));
+ }
+
+ @Override
+ public void onEndRestore() throws RemoteException {
+ mHandler.sendEmptyMessage(MSG_END_RESTORE);
+ }
+
+ @Override
+ public void onTimeout() throws RemoteException {
+ mHandler.sendEmptyMessage(MSG_TIMEOUT);
+ }
+ }
+}
diff --git a/packages/DefaultContainerService/jni/Android.mk b/packages/DefaultContainerService/jni/Android.mk
index a2febec..79ff451 100644
--- a/packages/DefaultContainerService/jni/Android.mk
+++ b/packages/DefaultContainerService/jni/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_PRELINK_MODULE := false
+
LOCAL_SRC_FILES := \
com_android_defcontainer_MeasurementUtils.cpp
diff --git a/packages/DefaultContainerService/res/values-am/strings.xml b/packages/DefaultContainerService/res/values-am/strings.xml
new file mode 100644
index 0000000..1101a45
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-am/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"ጥቅል ድረስ አጋዥ"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-sw/strings.xml b/packages/DefaultContainerService/res/values-sw/strings.xml
new file mode 100644
index 0000000..8d1f515
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-sw/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Kisaidizi cha Kufikia Furushi"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-zu/strings.xml b/packages/DefaultContainerService/res/values-zu/strings.xml
new file mode 100644
index 0000000..d501165
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-zu/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Umsizi Wokufinyelela Kwiphakheji"</string>
+</resources>
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 337593f..15c1653 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -47,7 +47,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.util.LinkedList;
+import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
@@ -117,7 +117,7 @@ public class DefaultContainerService extends IntentService {
* @return Returns PackageInfoLite object containing
* the package info and recommended app location.
*/
- public PackageInfoLite getMinimalPackageInfo(final Uri fileUri, int flags) {
+ public PackageInfoLite getMinimalPackageInfo(final Uri fileUri, int flags, long threshold) {
PackageInfoLite ret = new PackageInfoLite();
if (fileUri == null) {
Log.i(TAG, "Invalid package uri " + fileUri);
@@ -131,15 +131,9 @@ public class DefaultContainerService extends IntentService {
return ret;
}
String archiveFilePath = fileUri.getPath();
- PackageParser packageParser = new PackageParser(archiveFilePath);
- File sourceFile = new File(archiveFilePath);
DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
- PackageParser.PackageLite pkg = packageParser.parsePackageLite(
- archiveFilePath, 0);
- // Nuke the parser reference right away and force a gc
- packageParser = null;
- Runtime.getRuntime().gc();
+ PackageParser.PackageLite pkg = PackageParser.parsePackageLite(archiveFilePath, 0);
if (pkg == null) {
Log.w(TAG, "Failed to parse package");
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
@@ -147,12 +141,22 @@ public class DefaultContainerService extends IntentService {
}
ret.packageName = pkg.packageName;
ret.installLocation = pkg.installLocation;
- ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation, archiveFilePath, flags);
+ ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation,
+ archiveFilePath, flags, threshold);
return ret;
}
- public boolean checkFreeStorage(boolean external, Uri fileUri) {
- return checkFreeStorageInner(external, fileUri);
+ @Override
+ public boolean checkInternalFreeStorage(Uri packageUri, long threshold)
+ throws RemoteException {
+ final File apkFile = new File(packageUri.getPath());
+ return isUnderInternalThreshold(apkFile, threshold);
+ }
+
+ @Override
+ public boolean checkExternalFreeStorage(Uri packageUri) throws RemoteException {
+ final File apkFile = new File(packageUri.getPath());
+ return isUnderExternalThreshold(apkFile);
}
public ObbInfo getObbInfo(String filename) {
@@ -225,41 +229,15 @@ public class DefaultContainerService extends IntentService {
String codePath = packageURI.getPath();
File codeFile = new File(codePath);
- // Calculate size of container needed to hold base APK.
- long sizeBytes = codeFile.length();
-
- // Check all the native files that need to be copied and add that to the container size.
- ZipFile zipFile;
- List<Pair<ZipEntry, String>> nativeFiles;
- try {
- zipFile = new ZipFile(codeFile);
-
- nativeFiles = new LinkedList<Pair<ZipEntry, String>>();
-
- NativeLibraryHelper.listPackageNativeBinariesLI(zipFile, nativeFiles);
+ // Native files we need to copy to the container.
+ List<Pair<ZipEntry, String>> nativeFiles = new ArrayList<Pair<ZipEntry, String>>();
- final int N = nativeFiles.size();
- for (int i = 0; i < N; i++) {
- final Pair<ZipEntry, String> entry = nativeFiles.get(i);
-
- /*
- * Note that PackageHelper.createSdDir adds a 1MB padding on
- * our claimed size, so we don't have to worry about block
- * alignment here.
- */
- sizeBytes += entry.first.getSize();
- }
- } catch (ZipException e) {
- Log.w(TAG, "Failed to extract data from package file", e);
- return null;
- } catch (IOException e) {
- Log.w(TAG, "Failed to cache package shared libs", e);
- return null;
- }
+ // Calculate size of container needed to hold base APK.
+ final int sizeMb = calculateContainerSize(codeFile, nativeFiles);
// Create new container
String newCachePath = null;
- if ((newCachePath = PackageHelper.createSdDir(sizeBytes, newCid, key, Process.myUid())) == null) {
+ if ((newCachePath = PackageHelper.createSdDir(sizeMb, newCid, key, Process.myUid())) == null) {
Log.e(TAG, "Failed to create container " + newCid);
return null;
}
@@ -274,6 +252,8 @@ public class DefaultContainerService extends IntentService {
}
try {
+ ZipFile zipFile = new ZipFile(codeFile);
+
File sharedLibraryDir = new File(newCachePath, LIB_DIR_NAME);
sharedLibraryDir.mkdir();
@@ -386,163 +366,196 @@ public class DefaultContainerService extends IntentService {
return true;
}
- // Constants related to app heuristics
- // No-installation limit for internal flash: 10% or less space available
- private static final double LOW_NAND_FLASH_TRESHOLD = 0.1;
+ private static final int PREFER_INTERNAL = 1;
+ private static final int PREFER_EXTERNAL = 2;
- // No-installation limit for internal flash: 150MB or less space available
- private static final long NAND_MIN_FREE_SPACE = (1024L * 1024L * 150L);
-
- // SD-to-internal app size threshold: currently set to 1 MB
- private static final long INSTALL_ON_SD_THRESHOLD = (1024 * 1024);
- private static final int ERR_LOC = -1;
-
- private int recommendAppInstallLocation(int installLocation,
- String archiveFilePath, int flags) {
- boolean checkInt = false;
- boolean checkExt = false;
+ private int recommendAppInstallLocation(int installLocation, String archiveFilePath, int flags,
+ long threshold) {
+ int prefer;
boolean checkBoth = false;
+
check_inner : {
- // Check flags.
+ /*
+ * Explicit install flags should override the manifest settings.
+ */
if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) {
- // Check for forward locked app
- checkInt = true;
+ /*
+ * Forward-locked applications cannot be installed on SD card,
+ * so only allow checking internal storage.
+ */
+ prefer = PREFER_INTERNAL;
break check_inner;
} else if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
- // Explicit flag to install internally.
- // Check internal storage and return
- checkInt = true;
+ prefer = PREFER_INTERNAL;
break check_inner;
} else if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
- // Explicit flag to install externally.
- // Check external storage and return
- checkExt = true;
+ prefer = PREFER_EXTERNAL;
break check_inner;
}
- // Check for manifest option
+
+ /* No install flags. Check for manifest option. */
if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
- checkInt = true;
+ prefer = PREFER_INTERNAL;
break check_inner;
} else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
- checkExt = true;
+ prefer = PREFER_EXTERNAL;
checkBoth = true;
break check_inner;
} else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
- checkInt = true;
+ // We default to preferring internal storage.
+ prefer = PREFER_INTERNAL;
checkBoth = true;
break check_inner;
}
+
// Pick user preference
int installPreference = Settings.System.getInt(getApplicationContext()
.getContentResolver(),
Settings.Secure.DEFAULT_INSTALL_LOCATION,
PackageHelper.APP_INSTALL_AUTO);
if (installPreference == PackageHelper.APP_INSTALL_INTERNAL) {
- checkInt = true;
+ prefer = PREFER_INTERNAL;
break check_inner;
} else if (installPreference == PackageHelper.APP_INSTALL_EXTERNAL) {
- checkExt = true;
+ prefer = PREFER_EXTERNAL;
break check_inner;
}
- // Fall back to default policy if nothing else is specified.
- checkInt = true;
+
+ /*
+ * Fall back to default policy of internal-only if nothing else is
+ * specified.
+ */
+ prefer = PREFER_INTERNAL;
}
- // Package size = code size + cache size + data size
- // If code size > 1 MB, install on SD card.
- // Else install on internal NAND flash, unless space on NAND is less than 10%
- String status = Environment.getExternalStorageState();
- long availSDSize = -1;
- boolean mediaAvailable = false;
- final boolean mediaEmulated = Environment.isExternalStorageEmulated();
- if (!mediaEmulated && status.equals(Environment.MEDIA_MOUNTED)) {
- StatFs sdStats = new StatFs(
- Environment.getExternalStorageDirectory().getPath());
- availSDSize = (long)sdStats.getAvailableBlocks() *
- (long)sdStats.getBlockSize();
- mediaAvailable = true;
+ final boolean emulated = Environment.isExternalStorageEmulated();
+
+ final File apkFile = new File(archiveFilePath);
+
+ boolean fitsOnInternal = false;
+ if (checkBoth || prefer == PREFER_INTERNAL) {
+ fitsOnInternal = isUnderInternalThreshold(apkFile, threshold);
}
- StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath());
- long totalInternalSize = (long)internalStats.getBlockCount() *
- (long)internalStats.getBlockSize();
- long availInternalSize = (long)internalStats.getAvailableBlocks() *
- (long)internalStats.getBlockSize();
-
- double pctNandFree = (double)availInternalSize / (double)totalInternalSize;
-
- File apkFile = new File(archiveFilePath);
- long pkgLen = apkFile.length();
-
- // To make final copy
- long reqInstallSize = pkgLen;
- // For dex files. Just ignore and fail when extracting. Max limit of 2Gig for now.
- long reqInternalSize = 0;
- boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD);
- boolean intAvailOk = (reqInstallSize + reqInternalSize) < (availInternalSize - NAND_MIN_FREE_SPACE);
+
boolean fitsOnSd = false;
- if (mediaAvailable && (reqInstallSize < availSDSize)) {
- // If we do not have an internal size requirement
- // don't do a threshold check.
- if (reqInternalSize == 0) {
- fitsOnSd = true;
- } else if ((reqInternalSize < availInternalSize) && intThresholdOk) {
- fitsOnSd = true;
- }
+ if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)) {
+ fitsOnSd = isUnderExternalThreshold(apkFile);
}
- boolean fitsOnInt = intThresholdOk || intAvailOk;
- if (checkInt) {
- // Check for internal memory availability
- if (fitsOnInt) {
+
+ if (prefer == PREFER_INTERNAL) {
+ if (fitsOnInternal) {
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
}
- } else if (checkExt) {
+ } else if (!emulated && prefer == PREFER_EXTERNAL) {
if (fitsOnSd) {
return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
}
}
+
if (checkBoth) {
- // Check for internal first
- if (fitsOnInt) {
+ if (fitsOnInternal) {
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
- }
- // Check for external next
- if (fitsOnSd) {
+ } else if (!emulated && fitsOnSd) {
return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
}
}
- if (!mediaEmulated && (checkExt || checkBoth) && !mediaAvailable) {
+
+ /*
+ * If they requested to be on the external media by default, return that
+ * the media was unavailable. Otherwise, indicate there was insufficient
+ * storage space available.
+ */
+ if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)
+ && !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
return PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE;
+ } else {
+ return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
- return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
- private boolean checkFreeStorageInner(boolean external, Uri packageURI) {
- File apkFile = new File(packageURI.getPath());
- long size = apkFile.length();
- if (external) {
- String status = Environment.getExternalStorageState();
- long availSDSize = -1;
- if (status.equals(Environment.MEDIA_MOUNTED)) {
- StatFs sdStats = new StatFs(
- Environment.getExternalStorageDirectory().getPath());
- availSDSize = (long)sdStats.getAvailableBlocks() *
- (long)sdStats.getBlockSize();
+ private boolean isUnderInternalThreshold(File apkFile, long threshold) {
+ final long size = apkFile.length();
+
+ final StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath());
+ final long availInternalSize = (long) internalStats.getAvailableBlocks()
+ * (long) internalStats.getBlockSize();
+
+ return (availInternalSize - size) > threshold;
+ }
+
+
+ private boolean isUnderExternalThreshold(File apkFile) {
+ if (Environment.isExternalStorageEmulated()) {
+ return false;
+ }
+
+ final int sizeMb = calculateContainerSize(apkFile, null);
+
+ final int availSdMb;
+ if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
+ StatFs sdStats = new StatFs(Environment.getExternalStorageDirectory().getPath());
+ long availSdSize = (long) (sdStats.getAvailableBlocks() * sdStats.getBlockSize());
+ availSdMb = (int) (availSdSize >> 20);
+ } else {
+ availSdMb = -1;
+ }
+
+ return availSdMb > sizeMb;
+ }
+
+ /**
+ * Calculate the container size for an APK. Takes into account the
+ *
+ * @param apkFile file from which to calculate size
+ * @return size in megabytes (2^20 bytes)
+ */
+ private int calculateContainerSize(File apkFile, List<Pair<ZipEntry, String>> outFiles) {
+ // Calculate size of container needed to hold base APK.
+ long sizeBytes = apkFile.length();
+
+ // Check all the native files that need to be copied and add that to the
+ // container size.
+ ZipFile zipFile;
+ final List<Pair<ZipEntry, String>> nativeFiles;
+ try {
+ zipFile = new ZipFile(apkFile);
+
+ if (outFiles != null) {
+ nativeFiles = outFiles;
+ } else {
+ nativeFiles = new ArrayList<Pair<ZipEntry, String>>();
+ }
+
+ NativeLibraryHelper.listPackageNativeBinariesLI(zipFile, nativeFiles);
+
+ final int N = nativeFiles.size();
+ for (int i = 0; i < N; i++) {
+ final Pair<ZipEntry, String> entry = nativeFiles.get(i);
+
+ /*
+ * Note a 1MB padding is added to the claimed size, so we don't
+ * have to worry about block alignment here.
+ */
+ sizeBytes += entry.first.getSize();
}
- return availSDSize > size;
+ } catch (ZipException e) {
+ Log.w(TAG, "Failed to extract data from package file", e);
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to cache package shared libs", e);
}
- StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath());
- long totalInternalSize = (long)internalStats.getBlockCount() *
- (long)internalStats.getBlockSize();
- long availInternalSize = (long)internalStats.getAvailableBlocks() *
- (long)internalStats.getBlockSize();
-
- double pctNandFree = (double)availInternalSize / (double)totalInternalSize;
- // To make final copy
- long reqInstallSize = size;
- // For dex files. Just ignore and fail when extracting. Max limit of 2Gig for now.
- long reqInternalSize = 0;
- boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD);
- boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalSize);
- return intThresholdOk && intAvailOk;
+
+ int sizeMb = (int) (sizeBytes >> 20);
+ if ((sizeBytes - (sizeMb * 1024 * 1024)) > 0) {
+ sizeMb++;
+ }
+
+ /*
+ * Add buffer size because we don't have a good way to determine the
+ * real FAT size. Your FAT size varies with how many directory entries
+ * you need, how big the whole filesystem is, and other such headaches.
+ */
+ sizeMb++;
+
+ return sizeMb;
}
}
diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml
index dd0d064..e5f52e2 100644
--- a/packages/SettingsProvider/AndroidManifest.xml
+++ b/packages/SettingsProvider/AndroidManifest.xml
@@ -6,6 +6,7 @@
android:label="@string/app_label"
android:process="system"
android:backupAgent="SettingsBackupAgent"
+ android:fullBackupAgent="SettingsBackupAgent"
android:killAfterRestore="false"
android:icon="@drawable/ic_launcher_settings">
diff --git a/packages/SettingsProvider/res/values-af/strings.xml b/packages/SettingsProvider/res/values-af/strings.xml
new file mode 100644
index 0000000..79a7f2d
--- /dev/null
+++ b/packages/SettingsProvider/res/values-af/strings.xml
@@ -0,0 +1,23 @@
+<?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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4567566098528588863">"Instellingsberging"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-am/strings.xml b/packages/SettingsProvider/res/values-am/strings.xml
new file mode 100644
index 0000000..6380b99
--- /dev/null
+++ b/packages/SettingsProvider/res/values-am/strings.xml
@@ -0,0 +1,23 @@
+<?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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4567566098528588863">"የቅንብሮች ማከማቻ"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-ms/strings.xml b/packages/SettingsProvider/res/values-ms/strings.xml
new file mode 100644
index 0000000..9108b07
--- /dev/null
+++ b/packages/SettingsProvider/res/values-ms/strings.xml
@@ -0,0 +1,23 @@
+<?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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4567566098528588863">"Storan Tetapan"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-sw/strings.xml b/packages/SettingsProvider/res/values-sw/strings.xml
new file mode 100644
index 0000000..d17f60b
--- /dev/null
+++ b/packages/SettingsProvider/res/values-sw/strings.xml
@@ -0,0 +1,23 @@
+<?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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4567566098528588863">"Hifadhi ya Mipangilio"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-zu/strings.xml b/packages/SettingsProvider/res/values-zu/strings.xml
new file mode 100644
index 0000000..61f3d11
--- /dev/null
+++ b/packages/SettingsProvider/res/values-zu/strings.xml
@@ -0,0 +1,23 @@
+<?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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4567566098528588863">" Izilungiselelo zokugcina"</string>
+</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 2ed968b..0f5f095 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -61,7 +61,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
// database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
// is properly propagated through your change. Not doing so will result in a loss of user
// settings.
- private static final int DATABASE_VERSION = 64;
+ private static final int DATABASE_VERSION = 65;
private Context mContext;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 744798e..0c4ef7d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -36,6 +36,7 @@ import java.util.zip.CRC32;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupAgentHelper;
+import android.app.backup.FullBackup;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
@@ -54,6 +55,7 @@ import android.util.Log;
*/
public class SettingsBackupAgent extends BackupAgentHelper {
private static final boolean DEBUG = false;
+ private static final boolean DEBUG_BACKUP = DEBUG || true;
private static final String KEY_SYSTEM = "system";
private static final String KEY_SECURE = "secure";
@@ -74,6 +76,9 @@ public class SettingsBackupAgent extends BackupAgentHelper {
private static final int STATE_WIFI_CONFIG = 4;
private static final int STATE_SIZE = 5; // The number of state items
+ // Versioning of the 'full backup' format
+ private static final int FULL_BACKUP_VERSION = 1;
+
private static String[] sortedSystemKeys = null;
private static String[] sortedSecureKeys = null;
@@ -99,11 +104,17 @@ public class SettingsBackupAgent extends BackupAgentHelper {
private static final String KEY_WIFI_SUPPLICANT = "\uffedWIFI";
private static final String KEY_WIFI_CONFIG = "\uffedCONFIG_WIFI";
+ // Name of the temporary file we use during full backup/restore. This is
+ // stored in the full-backup tarfile as well, so should not be changed.
+ private static final String STAGE_FILE = "flattened-data";
+
private SettingsHelper mSettingsHelper;
private WifiManager mWfm;
private static String mWifiConfigFile;
public void onCreate() {
+ if (DEBUG_BACKUP) Log.d(TAG, "onCreate() invoked");
+
mSettingsHelper = new SettingsHelper(this);
super.onCreate();
@@ -121,22 +132,58 @@ public class SettingsBackupAgent extends BackupAgentHelper {
byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
byte[] wifiConfigData = getFileData(mWifiConfigFile);
- long[] stateChecksums = readOldChecksums(oldState);
+ // This same agent class is used for both full and incremental backups. A full
+ // backup is flagged by a 'null' oldState argument. In the case of a full backup,
+ // the output is structured as tarfile contents.
+ if (oldState != null) {
+ long[] stateChecksums = readOldChecksums(oldState);
- stateChecksums[STATE_SYSTEM] =
+ stateChecksums[STATE_SYSTEM] =
writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data);
- stateChecksums[STATE_SECURE] =
+ stateChecksums[STATE_SECURE] =
writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data);
- stateChecksums[STATE_LOCALE] =
+ stateChecksums[STATE_LOCALE] =
writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
- stateChecksums[STATE_WIFI_SUPPLICANT] =
+ stateChecksums[STATE_WIFI_SUPPLICANT] =
writeIfChanged(stateChecksums[STATE_WIFI_SUPPLICANT], KEY_WIFI_SUPPLICANT,
- wifiSupplicantData, data);
- stateChecksums[STATE_WIFI_CONFIG] =
+ wifiSupplicantData, data);
+ stateChecksums[STATE_WIFI_CONFIG] =
writeIfChanged(stateChecksums[STATE_WIFI_CONFIG], KEY_WIFI_CONFIG, wifiConfigData,
- data);
-
- writeNewChecksums(stateChecksums, newState);
+ data);
+
+ writeNewChecksums(stateChecksums, newState);
+ } else {
+ // Write the data to the staging file, then emit that as our tarfile
+ // representation of the backed-up settings.
+ String root = getFilesDir().getAbsolutePath();
+ File stage = new File(root, STAGE_FILE);
+ try {
+ FileOutputStream filestream = new FileOutputStream(stage);
+ BufferedOutputStream bufstream = new BufferedOutputStream(filestream);
+ DataOutputStream out = new DataOutputStream(bufstream);
+
+ out.writeInt(FULL_BACKUP_VERSION);
+
+ out.writeInt(systemSettingsData.length);
+ out.write(systemSettingsData);
+ out.writeInt(secureSettingsData.length);
+ out.write(secureSettingsData);
+ out.writeInt(locale.length);
+ out.write(locale);
+ out.writeInt(wifiSupplicantData.length);
+ out.write(wifiSupplicantData);
+ out.writeInt(wifiConfigData.length);
+ out.write(wifiConfigData);
+
+ out.flush(); // also flushes downstream
+
+ // now we're set to emit the tar stream
+ FullBackup.backupToTar(getPackageName(), FullBackup.DATA_TREE_TOKEN, null,
+ root, stage.getAbsolutePath(), data);
+ } finally {
+ stage.delete();
+ }
+ }
}
@Override
@@ -164,7 +211,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
} else if (KEY_LOCALE.equals(key)) {
byte[] localeData = new byte[size];
data.readEntityData(localeData, 0, size);
- mSettingsHelper.setLocaleData(localeData);
+ mSettingsHelper.setLocaleData(localeData, size);
} else if (KEY_WIFI_CONFIG.equals(key)) {
restoreFileData(mWifiConfigFile, data);
} else {
@@ -173,6 +220,70 @@ public class SettingsBackupAgent extends BackupAgentHelper {
}
}
+ @Override
+ public void onRestoreFile(ParcelFileDescriptor data, long size,
+ int type, String domain, String relpath, long mode, long mtime)
+ throws IOException {
+ if (DEBUG_BACKUP) Log.d(TAG, "onRestoreFile() invoked");
+ // Our data is actually a blob of flattened settings data identical to that
+ // produced during incremental backups. Just unpack and apply it all in
+ // turn.
+ FileInputStream instream = new FileInputStream(data.getFileDescriptor());
+ DataInputStream in = new DataInputStream(instream);
+
+ int version = in.readInt();
+ if (DEBUG_BACKUP) Log.d(TAG, "Flattened data version " + version);
+ if (version == FULL_BACKUP_VERSION) {
+ // system settings data first
+ int nBytes = in.readInt();
+ if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of settings data");
+ byte[] buffer = new byte[nBytes];
+ in.read(buffer, 0, nBytes);
+ restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI);
+
+ // secure settings
+ nBytes = in.readInt();
+ if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of secure settings data");
+ if (nBytes > buffer.length) buffer = new byte[nBytes];
+ in.read(buffer, 0, nBytes);
+ restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI);
+
+ // locale
+ nBytes = in.readInt();
+ if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of locale data");
+ if (nBytes > buffer.length) buffer = new byte[nBytes];
+ in.read(buffer, 0, nBytes);
+ mSettingsHelper.setLocaleData(buffer, nBytes);
+
+ // wifi supplicant
+ nBytes = in.readInt();
+ if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of wifi supplicant data");
+ if (nBytes > buffer.length) buffer = new byte[nBytes];
+ in.read(buffer, 0, nBytes);
+ int retainedWifiState = enableWifi(false);
+ restoreWifiSupplicant(FILE_WIFI_SUPPLICANT, buffer, nBytes);
+ FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
+ FileUtils.S_IRUSR | FileUtils.S_IWUSR |
+ FileUtils.S_IRGRP | FileUtils.S_IWGRP,
+ Process.myUid(), Process.WIFI_UID);
+ // retain the previous WIFI state.
+ enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED ||
+ retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
+
+ // wifi config
+ nBytes = in.readInt();
+ if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of wifi config data");
+ if (nBytes > buffer.length) buffer = new byte[nBytes];
+ in.read(buffer, 0, nBytes);
+ restoreFileData(mWifiConfigFile, buffer, nBytes);
+
+ if (DEBUG_BACKUP) Log.d(TAG, "Full restore complete.");
+ } else {
+ data.close();
+ throw new IOException("Invalid file schema");
+ }
+ }
+
private long[] readOldChecksums(ParcelFileDescriptor oldState) throws IOException {
long[] stateChecksums = new long[STATE_SIZE];
@@ -252,6 +363,17 @@ public class SettingsBackupAgent extends BackupAgentHelper {
}
private void restoreSettings(BackupDataInput data, Uri contentUri) {
+ byte[] settings = new byte[data.getDataSize()];
+ try {
+ data.readEntityData(settings, 0, settings.length);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Couldn't read entity data");
+ return;
+ }
+ restoreSettings(settings, settings.length, contentUri);
+ }
+
+ private void restoreSettings(byte[] settings, int bytes, Uri contentUri) {
if (DEBUG) Log.i(TAG, "restoreSettings: " + contentUri);
String[] whitelist = null;
if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
@@ -261,15 +383,8 @@ public class SettingsBackupAgent extends BackupAgentHelper {
}
ContentValues cv = new ContentValues(2);
- byte[] settings = new byte[data.getDataSize()];
- try {
- data.readEntityData(settings, 0, settings.length);
- } catch (IOException ioe) {
- Log.e(TAG, "Couldn't read entity data");
- return;
- }
int pos = 0;
- while (pos < settings.length) {
+ while (pos < bytes) {
int length = readInt(settings, pos);
pos += 4;
String settingName = length > 0? new String(settings, pos, length) : null;
@@ -416,13 +531,16 @@ public class SettingsBackupAgent extends BackupAgentHelper {
private void restoreFileData(String filename, BackupDataInput data) {
byte[] bytes = new byte[data.getDataSize()];
if (bytes.length <= 0) return;
+ restoreFileData(filename, bytes, bytes.length);
+ }
+
+ private void restoreFileData(String filename, byte[] bytes, int size) {
try {
- data.readEntityData(bytes, 0, bytes.length);
File file = new File(filename);
if (file.exists()) file.delete();
OutputStream os = new BufferedOutputStream(new FileOutputStream(filename, true));
- os.write(bytes);
+ os.write(bytes, 0, size);
os.close();
} catch (IOException ioe) {
Log.w(TAG, "Couldn't restore " + filename);
@@ -471,15 +589,18 @@ public class SettingsBackupAgent extends BackupAgentHelper {
private void restoreWifiSupplicant(String filename, BackupDataInput data) {
byte[] bytes = new byte[data.getDataSize()];
if (bytes.length <= 0) return;
+ restoreWifiSupplicant(filename, bytes, bytes.length);
+ }
+
+ private void restoreWifiSupplicant(String filename, byte[] bytes, int size) {
try {
- data.readEntityData(bytes, 0, bytes.length);
File supplicantFile = new File(FILE_WIFI_SUPPLICANT);
if (supplicantFile.exists()) supplicantFile.delete();
copyWifiSupplicantTemplate();
OutputStream os = new BufferedOutputStream(new FileOutputStream(filename, true));
os.write("\n".getBytes());
- os.write(bytes);
+ os.write(bytes, 0, size);
os.close();
} catch (IOException ioe) {
Log.w(TAG, "Couldn't restore " + filename);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 0e75fbc..3e7d86a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -147,7 +147,7 @@ public class SettingsHelper {
* "ll" is the language code and "cc" is the country code.
* @param data the locale string in bytes.
*/
- void setLocaleData(byte[] data) {
+ void setLocaleData(byte[] data, int size) {
// Check if locale was set by the user:
Configuration conf = mContext.getResources().getConfiguration();
Locale loc = conf.locale;
@@ -157,9 +157,9 @@ public class SettingsHelper {
if (conf.userSetLocale) return; // Don't change if user set it in the SetupWizard
final String[] availableLocales = mContext.getAssets().getLocales();
- String localeCode = new String(data);
+ String localeCode = new String(data, 0, size);
String language = new String(data, 0, 2);
- String country = data.length > 4 ? new String(data, 3, 2) : "";
+ String country = size > 4 ? new String(data, 3, 2) : "";
loc = null;
for (int i = 0; i < availableLocales.length; i++) {
if (availableLocales[i].equals(localeCode)) {
diff --git a/packages/SharedStorageBackup/Android.mk b/packages/SharedStorageBackup/Android.mk
new file mode 100644
index 0000000..1d4f4da
--- /dev/null
+++ b/packages/SharedStorageBackup/Android.mk
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
+LOCAL_PACKAGE_NAME := SharedStorageBackup
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
+########################
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/packages/SharedStorageBackup/AndroidManifest.xml b/packages/SharedStorageBackup/AndroidManifest.xml
new file mode 100644
index 0000000..258059c
--- /dev/null
+++ b/packages/SharedStorageBackup/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 2011, 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.sharedstoragebackup" >
+
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
+
+ <application android:allowClearUserData="false"
+ android:permission="android.permission.CONFIRM_FULL_BACKUP"
+ android:fullBackupAgent=".SharedStorageAgent"
+ android:allowBackup="false" >
+ </application>
+</manifest>
diff --git a/packages/SharedStorageBackup/proguard.flags b/packages/SharedStorageBackup/proguard.flags
new file mode 100644
index 0000000..f43cb81
--- /dev/null
+++ b/packages/SharedStorageBackup/proguard.flags
@@ -0,0 +1 @@
+-keep class com.android.sharedstoragebackup.SharedStorageAgent
diff --git a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java
new file mode 100644
index 0000000..b02ca2e
--- /dev/null
+++ b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java
@@ -0,0 +1,93 @@
+package com.android.sharedstoragebackup;
+
+import android.app.backup.FullBackup;
+import android.app.backup.FullBackupAgent;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.content.Context;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+
+public class SharedStorageAgent extends FullBackupAgent {
+ static final String TAG = "SharedStorageAgent";
+ static final boolean DEBUG = true;
+
+ StorageVolume[] mVolumes;
+
+ @Override
+ public void onCreate() {
+ StorageManager mgr = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
+ if (mgr != null) {
+ mVolumes = mgr.getVolumeList();
+ } else {
+ Slog.e(TAG, "Unable to access Storage Manager");
+ }
+ }
+
+ @Override
+ public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+ ParcelFileDescriptor newState) throws IOException {
+ // If there are shared-storage volumes available, run the inherited directory-
+ // hierarchy backup process on them. By convention in the Storage Manager, the
+ // "primary" shared storage volume is first in the list.
+ if (mVolumes != null) {
+ for (int i = 0; i < mVolumes.length; i++) {
+ StorageVolume v = mVolumes[i];
+ // Express the contents of volume N this way in the tar stream:
+ // shared/N/path/to/file
+ // The restore will then extract to the given volume
+ String domain = FullBackup.SHARED_PREFIX + i;
+ processTree(null, domain, v.getPath(), null, data);
+ }
+ }
+ }
+
+ /**
+ * Incremental onRestore() implementation is not used.
+ */
+ @Override
+ public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
+ throws IOException {
+ }
+
+ /**
+ * Full restore of one file to shared storage
+ */
+ @Override
+ public void onRestoreFile(ParcelFileDescriptor data, long size,
+ int type, String domain, String relpath, long mode, long mtime)
+ throws IOException {
+ Slog.d(TAG, "Shared restore: [ " + domain + " : " + relpath + "]");
+
+ File outFile = null;
+
+ // The file path must be in the semantic form [number]/path/to/file...
+ int slash = relpath.indexOf('/');
+ if (slash > 0) {
+ try {
+ int i = Integer.parseInt(relpath.substring(0, slash));
+ if (i <= mVolumes.length) {
+ outFile = new File(mVolumes[i].getPath(), relpath.substring(slash + 1));
+ if (DEBUG) Slog.i(TAG, " => " + outFile.getAbsolutePath());
+ } else {
+ Slog.w(TAG, "Cannot restore data for unavailable volume " + i);
+ }
+ } catch (NumberFormatException e) {
+ if (DEBUG) Slog.w(TAG, "Bad volume number token: " + relpath.substring(0, slash));
+ }
+ } else {
+ if (DEBUG) Slog.i(TAG, "Can't find volume-number token");
+ }
+ if (outFile == null) {
+ Slog.e(TAG, "Skipping data with malformed path " + relpath);
+ }
+
+ FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, false);
+ }
+}
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_bg_protect_tile.png b/packages/SystemUI/res/drawable-hdpi/recents_bg_protect_tile.png
new file mode 100644
index 0000000..87c7be6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/recents_bg_protect_tile.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_blue_glow.9.png b/packages/SystemUI/res/drawable-hdpi/recents_blue_glow.9.png
new file mode 100644
index 0000000..4f4ae78
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/recents_blue_glow.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_callout_line.png b/packages/SystemUI/res/drawable-hdpi/recents_callout_line.png
new file mode 100644
index 0000000..5f4c035
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/recents_callout_line.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg.png b/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg.png
new file mode 100644
index 0000000..87a67c9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_background.9.png b/packages/SystemUI/res/drawable-hdpi/status_bar_background.9.png
deleted file mode 100644
index a4be298..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_background.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_close_on.9.png b/packages/SystemUI/res/drawable-hdpi/status_bar_close_on.9.png
new file mode 100644
index 0000000..87d1944
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_close_on.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_veto_normal.png b/packages/SystemUI/res/drawable-hdpi/status_bar_veto_normal.png
new file mode 100644
index 0000000..3b7c9c7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_veto_normal.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_veto_pressed.png b/packages/SystemUI/res/drawable-hdpi/status_bar_veto_pressed.png
new file mode 100644
index 0000000..653acbb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_veto_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/statusbar_background.9.png b/packages/SystemUI/res/drawable-hdpi/statusbar_background.9.png
new file mode 100644
index 0000000..a933833
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/statusbar_background.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/panel_notification.png b/packages/SystemUI/res/drawable-mdpi/panel_notification.png
deleted file mode 100644
index 3789f3c..0000000
--- a/packages/SystemUI/res/drawable-mdpi/panel_notification.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_bg_protect_tile.png b/packages/SystemUI/res/drawable-mdpi/recents_bg_protect_tile.png
new file mode 100644
index 0000000..87c7be6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/recents_bg_protect_tile.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_blue_glow.9.png b/packages/SystemUI/res/drawable-mdpi/recents_blue_glow.9.png
new file mode 100644
index 0000000..4f4ae78
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/recents_blue_glow.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_callout_line.png b/packages/SystemUI/res/drawable-mdpi/recents_callout_line.png
new file mode 100644
index 0000000..5f4c035
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/recents_callout_line.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg.png b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg.png
new file mode 100644
index 0000000..87a67c9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.png b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.png
new file mode 100644
index 0000000..a1c39e6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_background.9.png b/packages/SystemUI/res/drawable-mdpi/status_bar_background.9.png
deleted file mode 100644
index eb7c1a4..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_background.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/statusbar_background.9.png b/packages/SystemUI/res/drawable-mdpi/statusbar_background.9.png
new file mode 100644
index 0000000..6c588f7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/statusbar_background.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/panel_notification.png b/packages/SystemUI/res/drawable-nodpi/panel_notification.png
deleted file mode 100644
index 437deff..0000000
--- a/packages/SystemUI/res/drawable-nodpi/panel_notification.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png
new file mode 100644
index 0000000..e2584e3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png
new file mode 100644
index 0000000..58b8510
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png
new file mode 100644
index 0000000..2795c34
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png
new file mode 100644
index 0000000..bbed6a6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/panel_notification_tiled.xml b/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml
index 9d41e28..977e002 100644
--- a/packages/SystemUI/res/drawable/panel_notification_tiled.xml
+++ b/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml
@@ -4,9 +4,9 @@
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.
@@ -14,9 +14,8 @@
limitations under the License.
-->
-<bitmap
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/panel_notification"
- android:tileMode="repeat"
- />
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:drawable="@drawable/ic_sysbar_zoom_pressed" />
+ <item android:drawable="@drawable/ic_sysbar_zoom_default" />
+</selector>
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
new file mode 100644
index 0000000..ce72f04
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- android:background="@drawable/status_bar_closed_default_background" -->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="@dimen/status_bar_recents_thumbnail_view_width">
+
+ <ImageView android:id="@+id/app_thumbnail"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
+ android:scaleType="center"
+ android:background="@drawable/recents_thumbnail_bg_selector"
+ />
+
+ <ImageView android:id="@+id/app_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignLeft="@id/app_thumbnail"
+ android:layout_alignTop="@id/app_thumbnail"
+ android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_border_width"
+ android:layout_marginTop="@dimen/status_bar_recents_thumbnail_border_height"
+ android:maxWidth="@dimen/status_bar_recents_thumbnail_max_width"
+ android:maxHeight="@dimen/status_bar_recents_thumbnail_max_height"
+ android:adjustViewBounds="true"
+ />
+
+ <TextView android:id="@+id/app_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/status_bar_recents_app_label_text_size"
+ android:fadingEdge="horizontal"
+ android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+ android:scrollHorizontally="true"
+ android:layout_alignLeft="@id/app_thumbnail"
+ android:layout_below="@id/app_thumbnail"
+ android:layout_marginTop="@dimen/status_bar_recents_text_description_padding"
+ android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_border_width"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ />
+
+ <TextView android:id="@+id/app_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/status_bar_recents_app_description_text_size"
+ android:fadingEdge="horizontal"
+ android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+ android:scrollHorizontally="true"
+ android:layout_alignLeft="@id/app_thumbnail"
+ android:layout_below="@id/app_label"
+ android:layout_marginTop="@dimen/status_bar_recents_text_description_padding"
+ android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_border_width"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ />
+
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
new file mode 100644
index 0000000..75f5ee4
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<com.android.systemui.recent.RecentsPanelView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/recents_root"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content">
+
+ <FrameLayout
+ android:id="@+id/recents_bg_protect"
+ android:background="@drawable/recents_bg_protect_tile"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentRight="true"
+ android:paddingBottom="@*android:dimen/status_bar_height"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+
+ <LinearLayout android:id="@+id/recents_glow"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|right"
+ android:background="@drawable/recents_blue_glow"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ >
+ <com.android.systemui.recent.RecentsHorizontalScrollView android:id="@+id/recents_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="@dimen/status_bar_recents_right_glow_margin"
+ android:divider="@null"
+ android:stackFromBottom="true"
+ android:fadingEdge="horizontal"
+ android:scrollbars="none"
+ android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+ android:listSelector="@drawable/recents_thumbnail_bg_selector"
+ android:layout_gravity="bottom|left"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+
+ <LinearLayout android:id="@+id/recents_linear_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+ </LinearLayout>
+
+ </com.android.systemui.recent.RecentsHorizontalScrollView>
+
+ </LinearLayout>
+
+ </FrameLayout>
+
+ <View android:id="@+id/recents_dismiss_button"
+ android:layout_width="80px"
+ android:layout_height="@*android:dimen/status_bar_height"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:background="@drawable/ic_sysbar_back_ime"
+ />
+
+</com.android.systemui.recent.RecentsPanelView>
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar.xml b/packages/SystemUI/res/layout-sw600dp/status_bar.xml
index d9f3f23..707a8cb 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar.xml
@@ -75,6 +75,13 @@
systemui:keyCode="82"
android:visibility="invisible"
/>
+ <com.android.systemui.statusbar.policy.CompatModeButton
+ android:id="@+id/compat_button"
+ android:layout_width="80dip"
+ android:layout_height="match_parent"
+ android:src="@drawable/ic_sysbar_zoom"
+ android:visibility="invisible"
+ />
</LinearLayout>
<!-- fake space bar zone -->
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
index 3f172e6..cd42d7e 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
@@ -60,9 +60,9 @@
<TextView android:id="@+id/app_label"
android:layout_width="97dip"
android:layout_height="wrap_content"
- android:textSize="18dip"
+ android:textSize="@dimen/status_bar_recents_app_description_text_size"
android:fadingEdge="horizontal"
- android:fadingEdgeLength="10dip"
+ android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
android:scrollHorizontally="true"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
@@ -75,9 +75,9 @@
<TextView android:id="@+id/app_description"
android:layout_width="97dip"
android:layout_height="wrap_content"
- android:textSize="18dip"
+ android:textSize="@dimen/status_bar_recents_app_description_text_size"
android:fadingEdge="horizontal"
- android:fadingEdgeLength="10dip"
+ android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
android:scrollHorizontally="true"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml
index 42940be..75fdc67 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml
@@ -18,11 +18,13 @@
*/
-->
-<com.android.systemui.statusbar.tablet.RecentAppsPanel
+<com.android.systemui.recent.RecentsPanelView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recents_root"
android:layout_height="match_parent"
- android:layout_width="wrap_content">
+ android:layout_width="wrap_content"
+ android:clipToPadding="false"
+ android:clipChildren="false">
<FrameLayout
android:id="@+id/recents_bg_protect"
@@ -31,7 +33,8 @@
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:paddingBottom="@*android:dimen/status_bar_height"
- android:clipToPadding="false">
+ android:clipToPadding="false"
+ android:clipChildren="false">
<LinearLayout android:id="@+id/recents_glow"
android:layout_width="wrap_content"
@@ -40,20 +43,32 @@
android:layout_gravity="bottom"
android:background="@drawable/recents_blue_glow"
android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:clipChildren="false"
>
-
- <ListView android:id="@+id/recents_container"
+ <com.android.systemui.recent.RecentsVerticalScrollView android:id="@+id/recents_container"
android:layout_width="@dimen/status_bar_recents_width"
android:layout_height="wrap_content"
- android:layout_marginRight="100dip"
+ android:layout_marginRight="@dimen/status_bar_recents_right_glow_margin"
android:divider="@null"
- android:scrollingCache="true"
android:stackFromBottom="true"
android:fadingEdge="vertical"
android:scrollbars="none"
android:fadingEdgeLength="20dip"
+ android:layout_gravity="bottom|left"
android:listSelector="@drawable/recents_thumbnail_bg_selector"
- />
+ android:clipToPadding="false"
+ android:clipChildren="false">
+
+ <LinearLayout android:id="@+id/recents_linear_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+ </LinearLayout>
+
+ </com.android.systemui.recent.RecentsVerticalScrollView>
</LinearLayout>
@@ -67,4 +82,4 @@
android:background="@drawable/ic_sysbar_back_ime"
/>
-</com.android.systemui.statusbar.tablet.RecentAppsPanel>
+</com.android.systemui.recent.RecentsPanelView>
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
new file mode 100644
index 0000000..b97c6a5
--- /dev/null
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<com.android.systemui.statusbar.phone.NavigationBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ >
+
+ <FrameLayout
+ android:id="@+id/background"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:background="#FF000000"
+ >
+
+ <LinearLayout android:id="@+id/rot0"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:paddingLeft="8dip"
+ android:paddingRight="8dip"
+ android:orientation="horizontal"
+ >
+
+ <!-- navigation controls -->
+ <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:src="@drawable/ic_sysbar_back"
+ systemui:keyCode="4"
+ android:layout_weight="1"
+ />
+ <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:src="@drawable/ic_sysbar_home"
+ systemui:keyCode="3"
+ android:layout_weight="1"
+ />
+ <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:src="@drawable/ic_sysbar_menu"
+ systemui:keyCode="82"
+ android:layout_weight="1"
+ />
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/rot90"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:visibility="gone"
+ >
+
+ <!-- navigation controls -->
+ <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_sysbar_menu"
+ systemui:keyCode="82"
+ android:layout_weight="1"
+ />
+ <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_sysbar_home"
+ systemui:keyCode="3"
+ android:layout_weight="1"
+ />
+ <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_sysbar_back"
+ systemui:keyCode="4"
+ android:layout_weight="1"
+ />
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/rot270"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:visibility="gone"
+ >
+
+ <!-- navigation controls -->
+ <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_sysbar_back"
+ systemui:keyCode="4"
+ android:layout_weight="1"
+ />
+ <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_sysbar_home"
+ systemui:keyCode="3"
+ android:layout_weight="1"
+ />
+ <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_sysbar_menu"
+ systemui:keyCode="82"
+ android:layout_weight="1"
+ />
+ </LinearLayout>
+
+ </FrameLayout>
+</com.android.systemui.statusbar.phone.NavigationBarView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 88d9739..8e456b2 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -1,24 +1,45 @@
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="65sp"
- android:orientation="vertical"
+ android:layout_height="65dp"
>
+ <ImageButton
+ android:id="@+id/veto"
+ android:layout_width="48dp"
+ android:layout_height="match_parent"
+ android:layout_centerVertical="true"
+ android:layout_alignParentRight="true"
+ android:src="@drawable/status_bar_veto"
+ android:scaleType="center"
+ android:background="@null"
+ android:paddingRight="8dp"
+ android:paddingLeft="8dp"
+ />
+
+ <ImageView
+ android:id="@+id/large_icon"
+ android:layout_width="@android:dimen/notification_large_icon_width"
+ android:layout_height="@android:dimen/notification_large_icon_height"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:scaleType="center"
+ />
+
<com.android.systemui.statusbar.LatestItemView android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="64sp"
- android:background="@android:drawable/status_bar_item_background"
- android:focusable="true"
- android:clickable="true"
- android:paddingRight="6sp"
- >
- </com.android.systemui.statusbar.LatestItemView>
+ android:layout_width="match_parent"
+ android:layout_height="64dp"
+ android:layout_alignParentTop="true"
+ android:layout_toRightOf="@id/large_icon"
+ android:layout_toLeftOf="@id/veto"
+ android:focusable="true"
+ android:clickable="true"
+ />
<View
android:layout_width="match_parent"
- android:layout_height="1sp"
- android:background="@android:drawable/divider_horizontal_bright"
+ android:layout_height="1dp"
+ android:layout_alignParentBottom="true"
+ android:background="@android:drawable/divider_horizontal_dark"
/>
-</LinearLayout>
-
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_recent_item.xml b/packages/SystemUI/res/layout/status_bar_recent_item.xml
new file mode 100644
index 0000000..cd42d7e
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_recent_item.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- android:background="@drawable/status_bar_closed_default_background" -->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="@dimen/status_bar_recents_thumbnail_view_width">
+
+ <ImageView android:id="@+id/app_thumbnail"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
+ android:scaleType="center"
+ />
+
+ <ImageView android:id="@+id/app_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="131dip"
+ android:layout_marginTop="13dip"
+ android:maxWidth="@dimen/status_bar_recents_thumbnail_max_width"
+ android:maxHeight="@dimen/status_bar_recents_thumbnail_max_height"
+ android:adjustViewBounds="true"
+ />
+
+ <View android:id="@+id/recents_callout_line"
+ android:layout_width="97dip"
+ android:layout_height="1dip"
+ android:layout_alignParentTop="true"
+ android:layout_marginTop="61dip"
+ android:layout_alignParentLeft="true"
+ android:layout_marginLeft="16dip"
+ android:layout_toLeftOf="@id/app_thumbnail"
+ android:layout_marginRight="3dip"
+ android:background="@drawable/recents_callout_line"
+ />
+
+ <TextView android:id="@+id/app_label"
+ android:layout_width="97dip"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/status_bar_recents_app_description_text_size"
+ android:fadingEdge="horizontal"
+ android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+ android:scrollHorizontally="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="16dip"
+ android:layout_marginTop="32dip"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ />
+
+ <TextView android:id="@+id/app_description"
+ android:layout_width="97dip"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/status_bar_recents_app_description_text_size"
+ android:fadingEdge="horizontal"
+ android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+ android:scrollHorizontally="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="16dip"
+ android:layout_marginTop="61dip"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ />
+
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_recent_panel.xml b/packages/SystemUI/res/layout/status_bar_recent_panel.xml
new file mode 100644
index 0000000..703cbc1
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_recent_panel.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<com.android.systemui.recent.RecentsPanelView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/recents_root"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content">
+
+ <FrameLayout
+ android:id="@+id/recents_bg_protect"
+ android:background="@drawable/recents_bg_protect_tile"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentBottom="true"
+ android:paddingBottom="@*android:dimen/status_bar_height"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+
+ <LinearLayout android:id="@+id/recents_glow"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="-49dip"
+ android:layout_gravity="bottom"
+ android:background="@drawable/recents_blue_glow"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ >
+ <com.android.systemui.recent.RecentsVerticalScrollView android:id="@+id/recents_container"
+ android:layout_width="@dimen/status_bar_recents_width"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="@dimen/status_bar_recents_right_glow_margin"
+ android:divider="@null"
+ android:stackFromBottom="true"
+ android:fadingEdge="vertical"
+ android:scrollbars="none"
+ android:fadingEdgeLength="20dip"
+ android:listSelector="@drawable/recents_thumbnail_bg_selector"
+ android:layout_gravity="bottom|left"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+
+ <LinearLayout android:id="@+id/recents_linear_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+ </LinearLayout>
+
+ </com.android.systemui.recent.RecentsVerticalScrollView>
+
+
+ </LinearLayout>
+
+ </FrameLayout>
+
+ <View android:id="@+id/recents_dismiss_button"
+ android:layout_width="80px"
+ android:layout_height="@*android:dimen/status_bar_height"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:background="@drawable/ic_sysbar_back_ime"
+ />
+
+</com.android.systemui.recent.RecentsPanelView>
diff --git a/packages/SystemUI/res/layout/status_bar_tracking.xml b/packages/SystemUI/res/layout/status_bar_tracking.xml
index a0ddab5..baa45c5 100644
--- a/packages/SystemUI/res/layout/status_bar_tracking.xml
+++ b/packages/SystemUI/res/layout/status_bar_tracking.xml
@@ -26,11 +26,12 @@
android:paddingRight="0px"
>
- <com.android.systemui.statusbar.phone.TrackingPatternView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- />
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:background="#ff000000"
+ />
<com.android.systemui.statusbar.phone.CloseDragHandle android:id="@+id/close"
android:layout_width="match_parent"
@@ -42,7 +43,7 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:scaleType="fitXY"
- android:src="@drawable/shade_handlebar"
+ android:src="@drawable/status_bar_close_on"
/>
</com.android.systemui.statusbar.phone.CloseDragHandle>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
new file mode 100644
index 0000000..0c4b41f
--- /dev/null
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_label (7164937344850004466) -->
+ <skip />
+ <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Maak skoon"</string>
+ <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+ <skip />
+ <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+ <skip />
+ <!-- no translation found for status_bar_no_notifications_title (4755261167193833213) -->
+ <skip />
+ <!-- no translation found for status_bar_ongoing_events_title (1682504513316879202) -->
+ <skip />
+ <!-- no translation found for status_bar_latest_events_title (6594767438577593172) -->
+ <skip />
+ <!-- no translation found for battery_low_title (7923774589611311406) -->
+ <skip />
+ <!-- no translation found for battery_low_subtitle (1752040062087829196) -->
+ <skip />
+ <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
+ <skip />
+ <!-- no translation found for invalid_charger (4549105996740522523) -->
+ <skip />
+ <string name="battery_low_why" msgid="7279169609518386372">"Batterygebruik"</string>
+ <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_wifi_button (1733928151698311923) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_airplane (4879879698500955300) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_auto_rotation (3790482541357798421) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_mute_label (554682549917429396) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_auto_brightness_label (511453614962324674) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
+ <skip />
+ <!-- no translation found for recent_tasks_title (3691764623638127888) -->
+ <skip />
+ <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+ <skip />
+ <!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
+ <skip />
+ <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
+ <skip />
+ <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
+ <skip />
+ <!-- no translation found for status_bar_use_physical_keyboard (3695516942412442936) -->
+ <skip />
+ <!-- no translation found for usb_device_permission_prompt (3816016361969816903) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (6888598803988889959) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (5161205258635253206) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (3808984931830229888) -->
+ <skip />
+ <!-- no translation found for usb_accessory_uri_prompt (6332150684964235705) -->
+ <skip />
+ <!-- no translation found for title_usb_accessory (4966265263465181372) -->
+ <skip />
+ <!-- no translation found for label_view (6304565553218192990) -->
+ <skip />
+ <!-- no translation found for always_use_device (1450287437017315906) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (1210954576979621596) -->
+ <skip />
+</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
new file mode 100644
index 0000000..6d09e83
--- /dev/null
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_label (7164937344850004466) -->
+ <skip />
+ <string name="status_bar_clear_all_button" msgid="7774721344716731603">"አጥራ"</string>
+ <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+ <skip />
+ <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+ <skip />
+ <!-- no translation found for status_bar_no_notifications_title (4755261167193833213) -->
+ <skip />
+ <!-- no translation found for status_bar_ongoing_events_title (1682504513316879202) -->
+ <skip />
+ <!-- no translation found for status_bar_latest_events_title (6594767438577593172) -->
+ <skip />
+ <!-- no translation found for battery_low_title (7923774589611311406) -->
+ <skip />
+ <!-- no translation found for battery_low_subtitle (1752040062087829196) -->
+ <skip />
+ <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
+ <skip />
+ <!-- no translation found for invalid_charger (4549105996740522523) -->
+ <skip />
+ <string name="battery_low_why" msgid="7279169609518386372">"የባትሪ ጥቅም"</string>
+ <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_wifi_button (1733928151698311923) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_airplane (4879879698500955300) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_auto_rotation (3790482541357798421) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_mute_label (554682549917429396) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_auto_brightness_label (511453614962324674) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
+ <skip />
+ <string name="recent_tasks_title" msgid="3691764623638127888">"የቅርብ ጊዜ"</string>
+ <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+ <skip />
+ <!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
+ <skip />
+ <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
+ <skip />
+ <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
+ <skip />
+ <!-- no translation found for status_bar_use_physical_keyboard (3695516942412442936) -->
+ <skip />
+ <!-- no translation found for usb_device_permission_prompt (3816016361969816903) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (6888598803988889959) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (5161205258635253206) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (3808984931830229888) -->
+ <skip />
+ <!-- no translation found for usb_accessory_uri_prompt (6332150684964235705) -->
+ <skip />
+ <!-- no translation found for title_usb_accessory (4966265263465181372) -->
+ <skip />
+ <!-- no translation found for label_view (6304565553218192990) -->
+ <skip />
+ <!-- no translation found for always_use_device (1450287437017315906) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (1210954576979621596) -->
+ <skip />
+</resources>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
new file mode 100644
index 0000000..6f1453e
--- /dev/null
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2011, 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>
+ <!-- thickness (width) of the navigation bar on phones that require it -->
+ <dimen name="navigation_bar_size">42dp</dimen>
+
+ <!-- Recent Applications parameters -->
+ <!-- Width of a recent app view, including all content -->
+ <dimen name="status_bar_recents_thumbnail_view_width">156dp</dimen>
+ <!-- How far the thumbnail for a recent app appears from left edge -->
+ <dimen name="status_bar_recents_thumbnail_left_margin">0dp</dimen>
+ <!-- Width of scrollable area in recents -->
+ <dimen name="status_bar_recents_width">128dp</dimen>
+ <!-- Thumbnail border width -->
+ <dimen name="status_bar_recents_thumbnail_border_width">8dp</dimen>
+ <!-- Thumbnail border height -->
+ <dimen name="status_bar_recents_thumbnail_border_height">12dp</dimen>
+ <!-- Padding for text descriptions -->
+ <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
+ <!-- Margin between recents container and glow on the right -->
+ <dimen name="status_bar_recents_right_glow_margin">0dip</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values-large/dimens.xml b/packages/SystemUI/res/values-large/dimens.xml
index 9d89e21..f8a4a1c 100644
--- a/packages/SystemUI/res/values-large/dimens.xml
+++ b/packages/SystemUI/res/values-large/dimens.xml
@@ -22,6 +22,32 @@
<dimen name="status_bar_panel_bottom_offset">36dp</dimen>
<!-- gap on either side of status bar notification icons -->
<dimen name="status_bar_icon_padding">8dp</dimen>
+
+ <!-- Recent Applications parameters -->
+ <!-- Width of a recent app view, including all content -->
+ <dimen name="status_bar_recents_thumbnail_view_width">156dp</dimen>
+ <!-- How far the thumbnail for a recent app appears from left edge -->
+ <dimen name="status_bar_recents_thumbnail_left_margin">110dp</dimen>
+ <!-- Upper width limit for application icon -->
+ <dimen name="status_bar_recents_thumbnail_max_width">64dp</dimen>
+ <!-- Upper height limit for application icon -->
+ <dimen name="status_bar_recents_thumbnail_max_height">64dp</dimen>
+ <!-- Width of scrollable area in recents -->
+ <dimen name="status_bar_recents_width">356dp</dimen>
+ <!-- Thumbnail border width -->
+ <dimen name="status_bar_recents_thumbnail_border_width">12dp</dimen>
+ <!-- Thumbnail border height -->
+ <dimen name="status_bar_recents_thumbnail_border_height">12dp</dimen>
+ <!-- Padding for text descriptions -->
+ <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
+ <!-- Size of application label text -->
+ <dimen name="status_bar_recents_app_label_text_size">18dip</dimen>
+ <!-- Size of application description text -->
+ <dimen name="status_bar_recents_app_description_text_size">18dip</dimen>
+ <!-- Size of fading edge for scroll effect -->
+ <dimen name="status_bar_recents_fading_edge_length">20dip</dimen>
+ <!-- Margin between recents container and glow on the right -->
+ <dimen name="status_bar_recents_right_glow_margin">100dip</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
new file mode 100644
index 0000000..a5150e7
--- /dev/null
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_label (7164937344850004466) -->
+ <skip />
+ <!-- no translation found for status_bar_clear_all_button (7774721344716731603) -->
+ <skip />
+ <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+ <skip />
+ <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+ <skip />
+ <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Tiada pemberitahuan"</string>
+ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Sedang berlangsung"</string>
+ <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Pemberitahuan"</string>
+ <string name="battery_low_title" msgid="7923774589611311406">"Sila sambungkan pengecas"</string>
+ <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Bateri semakin lemah:"</string>
+ <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
+ <skip />
+ <!-- no translation found for invalid_charger (4549105996740522523) -->
+ <skip />
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_wifi_button (1733928151698311923) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_airplane (4879879698500955300) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_auto_rotation (3790482541357798421) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_mute_label (554682549917429396) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_auto_brightness_label (511453614962324674) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
+ <skip />
+ <!-- no translation found for recent_tasks_title (3691764623638127888) -->
+ <skip />
+ <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+ <skip />
+ <!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
+ <skip />
+ <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
+ <skip />
+ <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
+ <skip />
+ <!-- no translation found for status_bar_use_physical_keyboard (3695516942412442936) -->
+ <skip />
+ <!-- no translation found for usb_device_permission_prompt (3816016361969816903) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (6888598803988889959) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (5161205258635253206) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (3808984931830229888) -->
+ <skip />
+ <!-- no translation found for usb_accessory_uri_prompt (6332150684964235705) -->
+ <skip />
+ <!-- no translation found for title_usb_accessory (4966265263465181372) -->
+ <skip />
+ <!-- no translation found for label_view (6304565553218192990) -->
+ <skip />
+ <!-- no translation found for always_use_device (1450287437017315906) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (1210954576979621596) -->
+ <skip />
+</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
new file mode 100644
index 0000000..bf0b58e
--- /dev/null
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_label (7164937344850004466) -->
+ <skip />
+ <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Futa"</string>
+ <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+ <skip />
+ <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+ <skip />
+ <!-- no translation found for status_bar_no_notifications_title (4755261167193833213) -->
+ <skip />
+ <!-- no translation found for status_bar_ongoing_events_title (1682504513316879202) -->
+ <skip />
+ <!-- no translation found for status_bar_latest_events_title (6594767438577593172) -->
+ <skip />
+ <!-- no translation found for battery_low_title (7923774589611311406) -->
+ <skip />
+ <!-- no translation found for battery_low_subtitle (1752040062087829196) -->
+ <skip />
+ <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
+ <skip />
+ <!-- no translation found for invalid_charger (4549105996740522523) -->
+ <skip />
+ <string name="battery_low_why" msgid="7279169609518386372">"Utumiaji wa betri"</string>
+ <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_wifi_button (1733928151698311923) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_airplane (4879879698500955300) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_auto_rotation (3790482541357798421) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_mute_label (554682549917429396) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_auto_brightness_label (511453614962324674) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
+ <skip />
+ <string name="recent_tasks_title" msgid="3691764623638127888">"Za hivi karibuni"</string>
+ <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+ <skip />
+ <!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
+ <skip />
+ <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
+ <skip />
+ <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
+ <skip />
+ <!-- no translation found for status_bar_use_physical_keyboard (3695516942412442936) -->
+ <skip />
+ <!-- no translation found for usb_device_permission_prompt (3816016361969816903) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (6888598803988889959) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (5161205258635253206) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (3808984931830229888) -->
+ <skip />
+ <!-- no translation found for usb_accessory_uri_prompt (6332150684964235705) -->
+ <skip />
+ <!-- no translation found for title_usb_accessory (4966265263465181372) -->
+ <skip />
+ <!-- no translation found for label_view (6304565553218192990) -->
+ <skip />
+ <!-- no translation found for always_use_device (1450287437017315906) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (1210954576979621596) -->
+ <skip />
+</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
new file mode 100644
index 0000000..43a87c3
--- /dev/null
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_label (7164937344850004466) -->
+ <skip />
+ <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Sula"</string>
+ <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+ <skip />
+ <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+ <skip />
+ <!-- no translation found for status_bar_no_notifications_title (4755261167193833213) -->
+ <skip />
+ <!-- no translation found for status_bar_ongoing_events_title (1682504513316879202) -->
+ <skip />
+ <!-- no translation found for status_bar_latest_events_title (6594767438577593172) -->
+ <skip />
+ <!-- no translation found for battery_low_title (7923774589611311406) -->
+ <skip />
+ <!-- no translation found for battery_low_subtitle (1752040062087829196) -->
+ <skip />
+ <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
+ <skip />
+ <!-- no translation found for invalid_charger (4549105996740522523) -->
+ <skip />
+ <string name="battery_low_why" msgid="7279169609518386372">"Ukusebenzisa ibhetri"</string>
+ <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_wifi_button (1733928151698311923) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_airplane (4879879698500955300) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_auto_rotation (3790482541357798421) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_mute_label (554682549917429396) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_auto_brightness_label (511453614962324674) -->
+ <skip />
+ <!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
+ <skip />
+ <string name="recent_tasks_title" msgid="3691764623638127888">"Okwakamuva"</string>
+ <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+ <skip />
+ <!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
+ <skip />
+ <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
+ <skip />
+ <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
+ <skip />
+ <!-- no translation found for status_bar_use_physical_keyboard (3695516942412442936) -->
+ <skip />
+ <!-- no translation found for usb_device_permission_prompt (3816016361969816903) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (6888598803988889959) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (5161205258635253206) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (3808984931830229888) -->
+ <skip />
+ <!-- no translation found for usb_accessory_uri_prompt (6332150684964235705) -->
+ <skip />
+ <!-- no translation found for title_usb_accessory (4966265263465181372) -->
+ <skip />
+ <!-- no translation found for label_view (6304565553218192990) -->
+ <skip />
+ <!-- no translation found for always_use_device (1450287437017315906) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (1210954576979621596) -->
+ <skip />
+</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 964e69b..9341693 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -20,4 +20,5 @@
<drawable name="notification_number_text_color">#ffffffff</drawable>
<drawable name="notification_item_background_color">#ff000000</drawable>
<drawable name="ticker_background_color">#ff1d1d1d</drawable>
+ <drawable name="status_bar_background">#000000</drawable>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 372aa39..7a4ac5d 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -36,9 +36,12 @@
<!-- Whether or not we show the number in the bar. -->
<bool name="config_statusBarShowNumber">true</bool>
+ <!-- Whether a software navigation bar should be shown. NOTE: in the future this may be
+ autodetected from the Configuration. -->
+ <bool name="config_showNavigationBar">false</bool>
+
<!-- How many icons may be shown at once in the system bar. Includes any
slots that may be reused for things like IME control. -->
<integer name="config_maxNotificationIcons">5</integer>
-
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 88cd43c..657dc46 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -30,6 +30,22 @@
<dimen name="status_bar_recents_thumbnail_max_height">64dp</dimen>
<!-- Width of scrollable area in recents -->
<dimen name="status_bar_recents_width">356dp</dimen>
+ <!-- Thumbnail border width -->
+ <dimen name="status_bar_recents_thumbnail_border_width">12dp</dimen>
+ <!-- Thumbnail border height -->
+ <dimen name="status_bar_recents_thumbnail_border_height">12dp</dimen>
+ <!-- Padding for text descriptions -->
+ <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
+ <!-- Size of application label text -->
+ <dimen name="status_bar_recents_app_label_text_size">18dip</dimen>
+ <!-- Size of application description text -->
+ <dimen name="status_bar_recents_app_description_text_size">18dip</dimen>
+ <!-- Size of fading edge for scroll effect -->
+ <dimen name="status_bar_recents_fading_edge_length">20dip</dimen>
+ <!-- Margin between recents container and glow on the right -->
+ <dimen name="status_bar_recents_right_glow_margin">100dip</dimen>
+ <!-- thickness (height) of the navigation bar on phones that require it -->
+ <dimen name="navigation_bar_size">42dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 3401441..a549f51 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -139,7 +139,6 @@ public class PowerUI extends SystemUI {
showInvalidChargerDialog();
return;
} else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
- Slog.d(TAG, "closing invalid charger warning");
dismissInvalidChargerDialog();
} else if (mInvalidChargerDialog != null) {
// if invalid charger is showing, don't show low battery
@@ -150,10 +149,9 @@ public class PowerUI extends SystemUI {
&& (bucket < oldBucket || oldPlugged)
&& mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
&& bucket < 0) {
- Slog.d(TAG, "showing low battery warning: level=" + mBatteryLevel);
+ Slog.i(TAG, "showing low battery warning: level=" + mBatteryLevel);
showLowBatteryWarning();
} else if (plugged || (bucket > oldBucket && bucket > 0)) {
- Slog.d(TAG, "closing low battery warning: level=" + mBatteryLevel);
dismissLowBatteryWarning();
} else if (mBatteryLevelTextView != null) {
showLowBatteryWarning();
@@ -166,6 +164,7 @@ public class PowerUI extends SystemUI {
void dismissLowBatteryWarning() {
if (mLowBatteryDialog != null) {
+ Slog.i(TAG, "closing low battery warning: level=" + mBatteryLevel);
mLowBatteryDialog.dismiss();
}
}
@@ -237,6 +236,7 @@ public class PowerUI extends SystemUI {
void dismissInvalidChargerDialog() {
if (mInvalidChargerDialog != null) {
+ Slog.d(TAG, "closing invalid charger warning");
mInvalidChargerDialog.dismiss();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
new file mode 100644
index 0000000..b876075
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2011 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.systemui.recent;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.util.Log;
+import android.util.Slog;
+import android.view.View;
+
+/* package */ class Choreographer implements Animator.AnimatorListener {
+ // should group this into a multi-property animation
+ private static final int OPEN_DURATION = 136;
+ private static final int CLOSE_DURATION = 250;
+ private static final String TAG = RecentsPanelView.TAG;
+ private static final boolean DEBUG = RecentsPanelView.DEBUG;
+
+ boolean mVisible;
+ int mPanelHeight;
+ View mRootView;
+ View mScrimView;
+ View mContentView;
+ AnimatorSet mContentAnim;
+
+ // the panel will start to appear this many px from the end
+ final int HYPERSPACE_OFFRAMP = 200;
+
+ public Choreographer(View root, View scrim, View content) {
+ mRootView = root;
+ mScrimView = scrim;
+ mContentView = content;
+ }
+
+ void createAnimation(boolean appearing) {
+ float start, end;
+
+ if (RecentsPanelView.DEBUG) Log.e(TAG, "createAnimation()", new Exception());
+
+ // 0: on-screen
+ // height: off-screen
+ float y = mContentView.getTranslationY();
+ if (appearing) {
+ // we want to go from near-the-top to the top, unless we're half-open in the right
+ // general vicinity
+ start = (y < HYPERSPACE_OFFRAMP) ? y : HYPERSPACE_OFFRAMP;
+ end = 0;
+ } else {
+ start = y;
+ end = y + HYPERSPACE_OFFRAMP;
+ }
+
+ Animator posAnim = ObjectAnimator.ofFloat(mContentView, "translationY",
+ start, end);
+ posAnim.setInterpolator(appearing
+ ? new android.view.animation.DecelerateInterpolator(2.5f)
+ : new android.view.animation.AccelerateInterpolator(2.5f));
+
+ Animator glowAnim = ObjectAnimator.ofFloat(mContentView, "alpha",
+ mContentView.getAlpha(), appearing ? 1.0f : 0.0f);
+ glowAnim.setInterpolator(appearing
+ ? new android.view.animation.AccelerateInterpolator(1.0f)
+ : new android.view.animation.DecelerateInterpolator(1.0f));
+
+ Animator bgAnim = ObjectAnimator.ofInt(mScrimView.getBackground(),
+ "alpha", appearing ? 0 : 255, appearing ? 255 : 0);
+
+ mContentAnim = new AnimatorSet();
+ mContentAnim
+ .play(bgAnim)
+ .with(glowAnim)
+ .with(posAnim);
+ mContentAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
+ mContentAnim.addListener(this);
+ }
+
+ void startAnimation(boolean appearing) {
+ if (DEBUG) Slog.d(TAG, "startAnimation(appearing=" + appearing + ")");
+
+ createAnimation(appearing);
+
+ mContentView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mContentAnim.start();
+
+ mVisible = appearing;
+ }
+
+ void jumpTo(boolean appearing) {
+ mContentView.setTranslationY(appearing ? 0 : mPanelHeight);
+ }
+
+ public void setPanelHeight(int h) {
+ if (DEBUG) Slog.d(TAG, "panelHeight=" + h);
+ mPanelHeight = h;
+ }
+
+ public void onAnimationCancel(Animator animation) {
+ if (DEBUG) Slog.d(TAG, "onAnimationCancel");
+ // force this to zero so we close the window
+ mVisible = false;
+ }
+
+ public void onAnimationEnd(Animator animation) {
+ if (DEBUG) Slog.d(TAG, "onAnimationEnd");
+ if (!mVisible) {
+ mRootView.setVisibility(View.GONE);
+ }
+ mContentView.setLayerType(View.LAYER_TYPE_NONE, null);
+ mContentAnim = null;
+ }
+
+ public void onAnimationRepeat(Animator animation) {
+ }
+
+ public void onAnimationStart(Animator animation) {
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java
new file mode 100644
index 0000000..5d29e2a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 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.systemui.recent;
+
+import android.view.View;
+
+public interface RecentsCallback {
+ static final int SWIPE_LEFT = 0;
+ static final int SWIPE_RIGHT = 1;
+ static final int SWIPE_UP = 2;
+ static final int SWIPE_DOWN = 3;
+
+ void handleOnClick(View selectedView);
+ void handleSwipe(View selectedView, int direction);
+ void handleLongPress(View selectedView);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
new file mode 100644
index 0000000..194c9d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2011 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.systemui.recent;
+
+import com.android.systemui.recent.RecentsPanelView.ActvityDescriptionAdapter;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.LayoutTransition;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.content.Context;
+import android.database.DataSetObserver;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.LinearInterpolator;
+import android.widget.HorizontalScrollView;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+
+public class RecentsHorizontalScrollView extends HorizontalScrollView
+ implements View.OnClickListener, View.OnTouchListener {
+ private static final float FADE_CONSTANT = 0.5f;
+ private static final int SNAP_BACK_DURATION = 250;
+ private static final int ESCAPE_VELOCITY = 100; // speed of item required to "curate" it
+ private static final String TAG = RecentsPanelView.TAG;
+ private static final float THRESHHOLD = 50;
+ private static final boolean DEBUG_INVALIDATE = false;
+ private LinearLayout mLinearLayout;
+ private ActvityDescriptionAdapter mAdapter;
+ private RecentsCallback mCallback;
+ protected int mLastScrollPosition;
+ private View mCurrentView;
+ private float mLastY;
+ private boolean mDragging;
+ private VelocityTracker mVelocityTracker;
+
+ public RecentsHorizontalScrollView(Context context) {
+ this(context, null);
+ }
+
+ public RecentsHorizontalScrollView(Context context, AttributeSet attrs) {
+ super(context, attrs, 0);
+ }
+
+ private int scrollPositionOfMostRecent() {
+ return mLinearLayout.getWidth() - getWidth();
+ }
+
+ public void update() {
+ mLinearLayout.removeAllViews();
+ for (int i = 0; i < mAdapter.getCount(); i++) {
+ View view = mAdapter.getView(i, null, mLinearLayout);
+ view.setClickable(true);
+ view.setOnClickListener(this);
+ view.setOnTouchListener(this);
+ mLinearLayout.addView(view);
+ }
+ // Scroll to end after layout.
+ post(new Runnable() {
+ public void run() {
+ mLastScrollPosition = scrollPositionOfMostRecent();
+ scrollTo(mLastScrollPosition, 0);
+ }
+ });
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mDragging = false;
+ mLastY = ev.getY();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ float delta = ev.getY() - mLastY;
+ if (Math.abs(delta) > THRESHHOLD) {
+ mDragging = true;
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ mDragging = false;
+ break;
+ }
+ return mDragging ? true : super.onInterceptTouchEvent(ev);
+ }
+
+ private float getAlphaForOffset(View view, float thumbHeight) {
+ final float fadeHeight = FADE_CONSTANT * thumbHeight;
+ float result = 1.0f;
+ if (view.getY() >= thumbHeight) {
+ result = 1.0f - (view.getY() - thumbHeight) / fadeHeight;
+ } else if (view.getY() < 0.0f) {
+ result = 1.0f + (thumbHeight + view.getY()) / fadeHeight;
+ }
+ return result;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (!mDragging) {
+ return super.onTouchEvent(ev);
+ }
+
+ mVelocityTracker.addMovement(ev);
+
+ final View animView = mCurrentView;
+ // TODO: Cache thumbnail
+ final View thumb = animView.findViewById(R.id.app_thumbnail);
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_MOVE:
+ if (animView != null) {
+ final float delta = ev.getY() - mLastY;
+ animView.setY(animView.getY() + delta);
+ animView.setAlpha(getAlphaForOffset(animView, thumb.getHeight()));
+ invalidateGlobalRegion(animView);
+ }
+ mLastY = ev.getY();
+ break;
+
+ case MotionEvent.ACTION_UP:
+ final ObjectAnimator anim;
+ if (animView != null) {
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, 10000);
+ final float velocityX = velocityTracker.getXVelocity();
+ final float velocityY = velocityTracker.getYVelocity();
+ final float curY = animView.getY();
+ final float newY = (velocityY >= 0.0f ? 1 : -1) * animView.getHeight();
+
+ if (Math.abs(velocityY) > Math.abs(velocityX)
+ && Math.abs(velocityY) > ESCAPE_VELOCITY
+ && (velocityY >= 0.0f) == (animView.getY() >= 0)) {
+ final long duration =
+ (long) (Math.abs(newY - curY) * 1000.0f / Math.abs(velocityY));
+ anim = ObjectAnimator.ofFloat(animView, "y", curY, newY);
+ anim.setInterpolator(new LinearInterpolator());
+ final int swipeDirection = animView.getY() >= 0.0f ?
+ RecentsCallback.SWIPE_RIGHT : RecentsCallback.SWIPE_LEFT;
+ anim.addListener(new AnimatorListener() {
+ public void onAnimationStart(Animator animation) {
+ }
+ public void onAnimationRepeat(Animator animation) {
+ }
+ public void onAnimationEnd(Animator animation) {
+ mLinearLayout.removeView(mCurrentView);
+ mCallback.handleSwipe(animView, swipeDirection);
+ }
+ public void onAnimationCancel(Animator animation) {
+ mLinearLayout.removeView(mCurrentView);
+ mCallback.handleSwipe(animView, swipeDirection);
+ }
+ });
+ anim.setDuration(duration);
+ } else { // Animate back to position
+ final long duration = Math.abs(velocityY) > 0.0f ?
+ (long) (Math.abs(newY - curY) * 1000.0f / Math.abs(velocityY))
+ : SNAP_BACK_DURATION;
+ anim = ObjectAnimator.ofFloat(animView, "y", animView.getY(), 0.0f);
+ anim.setInterpolator(new DecelerateInterpolator(2.0f));
+ anim.setDuration(duration);
+ }
+
+ anim.addUpdateListener(new AnimatorUpdateListener() {
+ public void onAnimationUpdate(ValueAnimator animation) {
+ animView.setAlpha(getAlphaForOffset(animView, thumb.getHeight()));
+ invalidateGlobalRegion(animView);
+ }
+ });
+ anim.start();
+ }
+
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ break;
+ }
+ return true;
+ }
+
+ void invalidateGlobalRegion(View view) {
+ RectF childBounds
+ = new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
+ childBounds.offset(view.getX(), view.getY());
+ if (DEBUG_INVALIDATE) Log.v(TAG, "-------------");
+ while (view.getParent() != null && view.getParent() instanceof View) {
+ view = (View) view.getParent();
+ view.getMatrix().mapRect(childBounds);
+ view.invalidate((int) Math.floor(childBounds.left),
+ (int) Math.floor(childBounds.top),
+ (int) Math.ceil(childBounds.right),
+ (int) Math.ceil(childBounds.bottom));
+ if (DEBUG_INVALIDATE) {
+ Log.v(TAG, "INVALIDATE(" + (int) Math.floor(childBounds.left)
+ + "," + (int) Math.floor(childBounds.top)
+ + "," + (int) Math.ceil(childBounds.right)
+ + "," + (int) Math.ceil(childBounds.bottom));
+ }
+ }
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ LayoutInflater inflater = (LayoutInflater)
+ mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ setScrollbarFadingEnabled(true);
+
+ mLinearLayout = (LinearLayout) findViewById(R.id.recents_linear_layout);
+
+ final int leftPadding = mContext.getResources()
+ .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin);
+ setOverScrollEffectPadding(leftPadding, 0);
+ }
+
+ private void setOverScrollEffectPadding(int leftPadding, int i) {
+ // TODO Add to RecentsHorizontalScrollView
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ // Keep track of the last visible item in the list so we can restore it
+ // to the bottom when the orientation changes.
+ mLastScrollPosition = scrollPositionOfMostRecent();
+
+ // This has to happen post-layout, so run it "in the future"
+ post(new Runnable() {
+ public void run() {
+ scrollTo(0, mLastScrollPosition);
+ }
+ });
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ // scroll to bottom after reloading
+ if (visibility == View.VISIBLE && changedView == this) {
+ post(new Runnable() {
+ public void run() {
+ update();
+ }
+ });
+ }
+ }
+
+ public void setAdapter(ActvityDescriptionAdapter adapter) {
+ mAdapter = adapter;
+ mAdapter.registerDataSetObserver(new DataSetObserver() {
+ public void onChanged() {
+ update();
+ }
+
+ public void onInvalidated() {
+ update();
+ }
+ });
+ }
+
+ @Override
+ public void setLayoutTransition(LayoutTransition transition) {
+ // The layout transition applies to our embedded LinearLayout
+ mLinearLayout.setLayoutTransition(transition);
+ }
+
+ public void onClick(View view) {
+ mCallback.handleOnClick(view);
+ }
+
+ public void setCallback(RecentsCallback callback) {
+ mCallback = callback;
+ }
+
+ public boolean onTouch(View v, MotionEvent event) {
+ mCurrentView = v;
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsListView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsListView.java
new file mode 100644
index 0000000..d8b086b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsListView.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2011 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.systemui.recent;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ListView;
+
+import com.android.systemui.R;
+
+public class RecentsListView extends ListView {
+ private int mLastVisiblePosition;
+ private RecentsCallback mCallback;
+
+ public RecentsListView(Context context) {
+ this(context, null);
+ }
+
+ public RecentsListView(Context context, AttributeSet attrs) {
+ super(context, attrs, 0);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ LayoutInflater inflater = (LayoutInflater)
+ mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ View footer = inflater.inflate(R.layout.status_bar_recent_panel_footer, this, false);
+ setScrollbarFadingEnabled(true);
+ addFooterView(footer, null, false);
+ final int leftPadding = mContext.getResources()
+ .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin);
+ setOverScrollEffectPadding(leftPadding, 0);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ // Keep track of the last visible item in the list so we can restore it
+ // to the bottom when the orientation changes.
+ final int childCount = getChildCount();
+ if (childCount > 0) {
+ mLastVisiblePosition = getFirstVisiblePosition() + childCount - 1;
+ View view = getChildAt(childCount - 1);
+ final int distanceFromBottom = getHeight() - view.getTop();
+
+ // This has to happen post-layout, so run it "in the future"
+ post(new Runnable() {
+ public void run() {
+ setSelectionFromTop(mLastVisiblePosition, getHeight() - distanceFromBottom);
+ }
+ });
+ }
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ // scroll to bottom after reloading
+ int count = getAdapter().getCount();
+ mLastVisiblePosition = count - 1;
+ if (visibility == View.VISIBLE && changedView == this) {
+ post(new Runnable() {
+ public void run() {
+ setSelection(mLastVisiblePosition);
+ }
+ });
+ }
+ }
+
+ public void setCallback(RecentsCallback callback) {
+ mCallback = callback;
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index fd07a5a..e2b3446 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2011 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.
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.tablet;
+package com.android.systemui.recent;
import java.util.ArrayList;
import java.util.List;
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
+import android.animation.LayoutTransition;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
@@ -43,25 +41,25 @@ import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
-import android.widget.CheckBox;
import android.widget.ImageView;
-import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.android.systemui.R;
+import com.android.systemui.statusbar.tablet.StatusBarPanel;
+import com.android.systemui.statusbar.tablet.TabletStatusBar;
-public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, OnItemClickListener {
+public class RecentsPanelView extends RelativeLayout
+ implements OnItemClickListener, RecentsCallback, StatusBarPanel {
private static final int GLOW_PADDING = 15;
- private static final String TAG = "RecentAppsPanel";
- private static final boolean DEBUG = TabletStatusBar.DEBUG;
+ static final String TAG = "RecentsListView";
+ static final boolean DEBUG = TabletStatusBar.DEBUG;
private static final int DISPLAY_TASKS = 20;
private static final int MAX_TASKS = DISPLAY_TASKS + 1; // allow extra for non-apps
private TabletStatusBar mBar;
@@ -69,17 +67,15 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
private int mIconDpi;
private View mRecentsScrim;
private View mRecentsGlowView;
- private ListView mRecentsContainer;
- private CheckBox mCompatMode;
+ private View mRecentsContainer;
private Bitmap mGlowBitmap;
private boolean mShowing;
private Choreographer mChoreo;
private View mRecentsDismissButton;
private ActvityDescriptionAdapter mListAdapter;
- protected int mLastVisibleItem;
- static class ActivityDescription {
- int id;
+ /* package */ final static class ActivityDescription {
+ int taskId; // application task id for curating apps
Bitmap thumbnail; // generated by Activity.onCreateThumbnail()
Drawable icon; // application package icon
String label; // application package label
@@ -98,18 +94,18 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
label = _label;
description = _desc;
intent = _intent;
- id = _id;
+ taskId = _id;
position = _pos;
packageName = _packageName;
}
};
/* package */ final static class ViewHolder {
- private ImageView thumbnailView;
- private ImageView iconView;
- private TextView labelView;
- private TextView descriptionView;
- private ActivityDescription activityDescription;
+ ImageView thumbnailView;
+ ImageView iconView;
+ TextView labelView;
+ TextView descriptionView;
+ ActivityDescription activityDescription;
}
/* package */ final class ActvityDescriptionAdapter extends BaseAdapter {
@@ -205,120 +201,15 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
return mShowing;
}
- private static class Choreographer implements Animator.AnimatorListener {
- // should group this into a multi-property animation
- private static final int OPEN_DURATION = 136;
- private static final int CLOSE_DURATION = 250;
-
- boolean mVisible;
- int mPanelHeight;
- View mRootView;
- View mScrimView;
- View mContentView;
- AnimatorSet mContentAnim;
-
- // the panel will start to appear this many px from the end
- final int HYPERSPACE_OFFRAMP = 200;
-
- public Choreographer(View root, View scrim, View content) {
- mRootView = root;
- mScrimView = scrim;
- mContentView = content;
- }
-
- void createAnimation(boolean appearing) {
- float start, end;
-
- if (DEBUG) Log.e(TAG, "createAnimation()", new Exception());
-
- // 0: on-screen
- // height: off-screen
- float y = mContentView.getTranslationY();
- if (appearing) {
- // we want to go from near-the-top to the top, unless we're half-open in the right
- // general vicinity
- start = (y < HYPERSPACE_OFFRAMP) ? y : HYPERSPACE_OFFRAMP;
- end = 0;
- } else {
- start = y;
- end = y + HYPERSPACE_OFFRAMP;
- }
-
- Animator posAnim = ObjectAnimator.ofFloat(mContentView, "translationY",
- start, end);
- posAnim.setInterpolator(appearing
- ? new android.view.animation.DecelerateInterpolator(2.5f)
- : new android.view.animation.AccelerateInterpolator(2.5f));
-
- Animator glowAnim = ObjectAnimator.ofFloat(mContentView, "alpha",
- mContentView.getAlpha(), appearing ? 1.0f : 0.0f);
- glowAnim.setInterpolator(appearing
- ? new android.view.animation.AccelerateInterpolator(1.0f)
- : new android.view.animation.DecelerateInterpolator(1.0f));
-
- Animator bgAnim = ObjectAnimator.ofInt(mScrimView.getBackground(),
- "alpha", appearing ? 0 : 255, appearing ? 255 : 0);
-
- mContentAnim = new AnimatorSet();
- mContentAnim
- .play(bgAnim)
- .with(glowAnim)
- .with(posAnim);
- mContentAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
- mContentAnim.addListener(this);
- }
-
- void startAnimation(boolean appearing) {
- if (DEBUG) Slog.d(TAG, "startAnimation(appearing=" + appearing + ")");
-
- createAnimation(appearing);
-
- mContentView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- mContentAnim.start();
-
- mVisible = appearing;
- }
-
- void jumpTo(boolean appearing) {
- mContentView.setTranslationY(appearing ? 0 : mPanelHeight);
- }
-
- public void setPanelHeight(int h) {
- if (DEBUG) Slog.d(TAG, "panelHeight=" + h);
- mPanelHeight = h;
- }
-
- public void onAnimationCancel(Animator animation) {
- if (DEBUG) Slog.d(TAG, "onAnimationCancel");
- // force this to zero so we close the window
- mVisible = false;
- }
-
- public void onAnimationEnd(Animator animation) {
- if (DEBUG) Slog.d(TAG, "onAnimationEnd");
- if (!mVisible) {
- mRootView.setVisibility(View.GONE);
- }
- mContentView.setLayerType(View.LAYER_TYPE_NONE, null);
- mContentAnim = null;
- }
-
- public void onAnimationRepeat(Animator animation) {
- }
-
- public void onAnimationStart(Animator animation) {
- }
- }
-
public void setBar(TabletStatusBar bar) {
mBar = bar;
}
- public RecentAppsPanel(Context context, AttributeSet attrs) {
+ public RecentsPanelView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
- public RecentAppsPanel(Context context, AttributeSet attrs, int defStyle) {
+ public RecentsPanelView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
Resources res = context.getResources();
@@ -330,43 +221,34 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
}
@Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- // Keep track of the last visible item in the list so we can restore it
- // to the bottom when the orientation changes.
- int childCount = mRecentsContainer.getChildCount();
- if (childCount > 0) {
- mLastVisibleItem = mRecentsContainer.getFirstVisiblePosition() + childCount - 1;
- View view = mRecentsContainer.getChildAt(childCount - 1);
- final int distanceFromBottom = mRecentsContainer.getHeight() - view.getTop();
- //final int distanceFromBottom = view.getHeight() + BOTTOM_OFFSET;
-
- // This has to happen post-layout, so run it "in the future"
- post(new Runnable() {
- public void run() {
- mRecentsContainer.setSelectionFromTop(mLastVisibleItem,
- mRecentsContainer.getHeight() - distanceFromBottom);
- }
- });
- }
- }
-
- @Override
protected void onFinishInflate() {
super.onFinishInflate();
- LayoutInflater inflater = (LayoutInflater)
mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mRecentsContainer = findViewById(R.id.recents_container);
+ mListAdapter = new ActvityDescriptionAdapter(mContext);
+ if (mRecentsContainer instanceof RecentsListView) {
+ RecentsListView listView = (RecentsListView) mRecentsContainer;
+ listView.setAdapter(mListAdapter);
+ listView.setOnItemClickListener(this);
+ listView.setCallback(this);
+ } else if (mRecentsContainer instanceof RecentsHorizontalScrollView){
+ RecentsHorizontalScrollView scrollView
+ = (RecentsHorizontalScrollView) mRecentsContainer;
+ scrollView.setAdapter(mListAdapter);
+ scrollView.setCallback(this);
+ } else if (mRecentsContainer instanceof RecentsVerticalScrollView){
+ RecentsVerticalScrollView scrollView
+ = (RecentsVerticalScrollView) mRecentsContainer;
+ scrollView.setAdapter(mListAdapter);
+ scrollView.setCallback(this);
+ }
+ else {
+ throw new IllegalArgumentException("missing RecentsListView/RecentsScrollView");
+ }
- mRecentsContainer = (ListView) findViewById(R.id.recents_container);
- View footer = inflater.inflate(R.layout.status_bar_recent_panel_footer,
- mRecentsContainer, false);
- mRecentsContainer.setScrollbarFadingEnabled(true);
- mRecentsContainer.addFooterView(footer, null, false);
- mRecentsContainer.setAdapter(mListAdapter = new ActvityDescriptionAdapter(mContext));
- mRecentsContainer.setOnItemClickListener(this);
- final int leftPadding = mContext.getResources()
- .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin);
- mRecentsContainer.setOverScrollEffectPadding(leftPadding, 0);
+ final LayoutTransition transitioner = new LayoutTransition();
+ ((ViewGroup)mRecentsContainer).setLayoutTransition(transitioner);
+ createCustomAnimations(transitioner);
mRecentsGlowView = findViewById(R.id.recents_glow);
mRecentsScrim = (View) findViewById(R.id.recents_bg_protect);
@@ -384,17 +266,16 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
}
}
+ private void createCustomAnimations(LayoutTransition transitioner) {
+ transitioner.setDuration(LayoutTransition.DISAPPEARING, 250);
+ }
+
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + changedView + ", " + visibility + ")");
if (visibility == View.VISIBLE && changedView == this) {
refreshApplicationList();
- post(new Runnable() {
- public void run() {
- mRecentsContainer.setSelection(mActivityDescriptions.size() - 1);
- }
- });
}
}
@@ -431,8 +312,7 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
mContext.getSystemService(Context.ACTIVITY_SERVICE);
final List<ActivityManager.RecentTaskInfo> recentTasks =
- am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE
- | ActivityManager.TASKS_GET_THUMBNAILS);
+ am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
.resolveActivityInfo(pm, 0);
@@ -467,8 +347,10 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
int id = recentTasks.get(i).id;
if (title != null && title.length() > 0 && icon != null) {
if (DEBUG) Log.v(TAG, "creating activity desc for id=" + id + ", label=" + title);
+ ActivityManager.TaskThumbnails thumbs = am.getTaskThumbnails(
+ recentInfo.persistentId);
ActivityDescription item = new ActivityDescription(
- am.getTaskThumbnail(recentInfo.persistentId),
+ thumbs != null ? thumbs.mainThumbnail : null,
icon, title, recentInfo.description, intent, id,
index, info.packageName);
activityDescriptions.add(item);
@@ -486,7 +368,7 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
ActivityDescription desc = null;
for (int i = 0; i < mActivityDescriptions.size(); i++) {
ActivityDescription item = mActivityDescriptions.get(i);
- if (item != null && item.id == id) {
+ if (item != null && item.taskId == id) {
desc = item;
break;
}
@@ -494,37 +376,15 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
return desc;
}
- private void updateShownCompatMode() {
- if (mCompatMode == null) {
- return;
- }
- final ActivityManager am = (ActivityManager)
- mContext.getSystemService(Context.ACTIVITY_SERVICE);
- int mode = am.getFrontActivityScreenCompatMode();
- switch (mode) {
- case ActivityManager.COMPAT_MODE_DISABLED:
- mCompatMode.setVisibility(View.VISIBLE);
- mCompatMode.setChecked(true);
- break;
- case ActivityManager.COMPAT_MODE_ENABLED:
- mCompatMode.setVisibility(View.VISIBLE);
- mCompatMode.setChecked(false);
- break;
- default:
- mCompatMode.setVisibility(View.GONE);
- break;
- }
- }
-
private void refreshApplicationList() {
mActivityDescriptions = getRecentTasks();
mListAdapter.notifyDataSetInvalidated();
if (mActivityDescriptions.size() > 0) {
- mLastVisibleItem = mActivityDescriptions.size() - 1; // scroll to bottom after reloading
+ Log.v(TAG, "Showing " + mActivityDescriptions.size() + " apps");
updateUiElements(getResources().getConfiguration());
- updateShownCompatMode();
} else {
// Immediately hide this panel
+ Log.v(TAG, "Nothing to show");
hide(false);
}
}
@@ -539,6 +399,7 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
paint.setAlpha(255);
final int srcWidth = thumbnail.getWidth();
final int srcHeight = thumbnail.getHeight();
+ Log.v(TAG, "Source thumb: " + srcWidth + "x" + srcHeight);
canvas.drawBitmap(thumbnail,
new Rect(0, 0, srcWidth-1, srcHeight-1),
new RectF(GLOW_PADDING,
@@ -556,27 +417,57 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
mRecentsGlowView.setVisibility(items > 0 ? View.VISIBLE : View.GONE);
}
- private void hide(boolean animate) {
+ public void hide(boolean animate) {
if (!animate) {
setVisibility(View.GONE);
}
- mBar.animateCollapse();
+ if (mBar != null) {
+ mBar.animateCollapse();
+ }
}
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ public void handleOnClick(View view) {
ActivityDescription ad = ((ViewHolder) view.getTag()).activityDescription;
+ final Context context = view.getContext();
final ActivityManager am = (ActivityManager)
- getContext().getSystemService(Context.ACTIVITY_SERVICE);
- if (ad.id >= 0) {
+ context.getSystemService(Context.ACTIVITY_SERVICE);
+ if (ad.taskId >= 0) {
// This is an active task; it should just go to the foreground.
- am.moveTaskToFront(ad.id, ActivityManager.MOVE_TASK_WITH_HOME);
+ am.moveTaskToFront(ad.taskId, ActivityManager.MOVE_TASK_WITH_HOME);
} else {
Intent intent = ad.intent;
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
| Intent.FLAG_ACTIVITY_TASK_ON_HOME);
if (DEBUG) Log.v(TAG, "Starting activity " + intent);
- getContext().startActivity(intent);
+ context.startActivity(intent);
}
hide(true);
}
+
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ handleOnClick(view);
+ }
+
+ public void handleSwipe(View view, int direction) {
+ ActivityDescription ad = ((ViewHolder) view.getTag()).activityDescription;
+ Log.v(TAG, "Jettison " + ad.label);
+ mActivityDescriptions.remove(ad);
+
+ // Handled by widget containers to enable LayoutTransitions properly
+ // mListAdapter.notifyDataSetChanged();
+
+ if (mActivityDescriptions.size() == 0) {
+ hide(false);
+ }
+
+ // Currently, either direction means the same thing, so ignore direction and remove
+ // the task.
+ final ActivityManager am = (ActivityManager)
+ mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ am.removeTask(ad.taskId, 0);
+ }
+
+ public void handleLongPress(View selectedView) {
+ // TODO show context menu : "Remove from list", "Show properties"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
new file mode 100644
index 0000000..54ec6b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2011 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.systemui.recent;
+
+import com.android.systemui.recent.RecentsPanelView.ActvityDescriptionAdapter;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.LayoutTransition;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.content.Context;
+import android.database.DataSetObserver;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.LinearInterpolator;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+
+import com.android.systemui.R;
+
+public class RecentsVerticalScrollView extends ScrollView
+ implements View.OnClickListener, View.OnTouchListener {
+ private static final float FADE_CONSTANT = 0.5f;
+ private static final int SNAP_BACK_DURATION = 250;
+ private static final int ESCAPE_VELOCITY = 100; // speed of item required to "curate" it
+ private static final String TAG = RecentsPanelView.TAG;
+ private static final float THRESHHOLD = 50;
+ private static final boolean DEBUG_INVALIDATE = false;
+ private LinearLayout mLinearLayout;
+ private ActvityDescriptionAdapter mAdapter;
+ private RecentsCallback mCallback;
+ protected int mLastScrollPosition;
+ private View mCurrentView;
+ private float mLastX;
+ private boolean mDragging;
+ private VelocityTracker mVelocityTracker;
+
+ public RecentsVerticalScrollView(Context context) {
+ this(context, null);
+ }
+
+ public RecentsVerticalScrollView(Context context, AttributeSet attrs) {
+ super(context, attrs, 0);
+ }
+
+ private int scrollPositionOfMostRecent() {
+ return mLinearLayout.getHeight() - getHeight();
+ }
+
+ public void update() {
+ mLinearLayout.removeAllViews();
+ for (int i = 0; i < mAdapter.getCount(); i++) {
+ View view = mAdapter.getView(i, null, mLinearLayout);
+ view.setClickable(true);
+ view.setOnClickListener(this);
+ view.setOnTouchListener(this);
+ mLinearLayout.addView(view);
+ }
+ // Scroll to end after layout.
+ post(new Runnable() {
+ public void run() {
+ mLastScrollPosition = scrollPositionOfMostRecent();
+ scrollTo(0, mLastScrollPosition);
+ }
+ });
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mDragging = false;
+ mLastX = ev.getX();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ float delta = ev.getX() - mLastX;
+ Log.v(TAG, "ACTION_MOVE : " + delta);
+ if (Math.abs(delta) > THRESHHOLD) {
+ mDragging = true;
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ mDragging = false;
+ break;
+ }
+ return mDragging ? true : super.onInterceptTouchEvent(ev);
+ }
+
+ private float getAlphaForOffset(View view, float thumbWidth) {
+ final float fadeWidth = FADE_CONSTANT * thumbWidth;
+ float result = 1.0f;
+ if (view.getX() >= thumbWidth) {
+ result = 1.0f - (view.getX() - thumbWidth) / fadeWidth;
+ } else if (view.getX() < 0.0f) {
+ result = 1.0f + (thumbWidth + view.getX()) / fadeWidth;
+ }
+ Log.v(TAG, "FADE AMOUNT: " + result);
+ return result;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (!mDragging) {
+ return super.onTouchEvent(ev);
+ }
+
+ mVelocityTracker.addMovement(ev);
+
+ final View animView = mCurrentView;
+ // TODO: Cache thumbnail
+ final View thumb = animView.findViewById(R.id.app_thumbnail);
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_MOVE:
+ if (animView != null) {
+ final float delta = ev.getX() - mLastX;
+ animView.setX(animView.getX() + delta);
+ animView.setAlpha(getAlphaForOffset(animView, thumb.getWidth()));
+ invalidateGlobalRegion(animView);
+ }
+ mLastX = ev.getX();
+ break;
+
+ case MotionEvent.ACTION_UP:
+ final ObjectAnimator anim;
+ if (animView != null) {
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, 10000);
+ final float velocityX = velocityTracker.getXVelocity();
+ final float velocityY = velocityTracker.getYVelocity();
+ final float curX = animView.getX();
+ final float newX = (velocityX >= 0.0f ? 1 : -1) * animView.getWidth();
+
+ if (Math.abs(velocityX) > Math.abs(velocityY)
+ && Math.abs(velocityX) > ESCAPE_VELOCITY
+ && (velocityX > 0.0f) == (animView.getX() >= 0)) {
+ final long duration =
+ (long) (Math.abs(newX-curX) * 1000.0f / Math.abs(velocityX));
+ anim = ObjectAnimator.ofFloat(animView, "x", curX, newX);
+ anim.setInterpolator(new LinearInterpolator());
+ final int swipeDirection = animView.getX() >= 0.0f ?
+ RecentsCallback.SWIPE_RIGHT : RecentsCallback.SWIPE_LEFT;
+ anim.addListener(new AnimatorListener() {
+ public void onAnimationStart(Animator animation) {
+ }
+ public void onAnimationRepeat(Animator animation) {
+ }
+ public void onAnimationEnd(Animator animation) {
+ mLinearLayout.removeView(mCurrentView);
+ mCallback.handleSwipe(animView, swipeDirection);
+ }
+ public void onAnimationCancel(Animator animation) {
+ mLinearLayout.removeView(mCurrentView);
+ mCallback.handleSwipe(animView, swipeDirection);
+ }
+ });
+ anim.setDuration(duration);
+ } else { // Animate back to position
+ final long duration = Math.abs(velocityX) > 0.0f ?
+ (long) (Math.abs(newX-curX) * 1000.0f / Math.abs(velocityX))
+ : SNAP_BACK_DURATION;
+ anim = ObjectAnimator.ofFloat(animView, "x", animView.getX(), 0.0f);
+ anim.setInterpolator(new DecelerateInterpolator(4.0f));
+ anim.setDuration(duration);
+ }
+
+ anim.addUpdateListener(new AnimatorUpdateListener() {
+ public void onAnimationUpdate(ValueAnimator animation) {
+ animView.setAlpha(getAlphaForOffset(animView, thumb.getWidth()));
+ invalidateGlobalRegion(animView);
+ }
+ });
+ anim.start();
+ }
+
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ break;
+ }
+ return true;
+ }
+
+ void invalidateGlobalRegion(View view) {
+ RectF childBounds
+ = new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
+ childBounds.offset(view.getX(), view.getY());
+ if (DEBUG_INVALIDATE) Log.v(TAG, "-------------");
+ while (view.getParent() != null && view.getParent() instanceof View) {
+ view = (View) view.getParent();
+ view.getMatrix().mapRect(childBounds);
+ view.invalidate((int) Math.floor(childBounds.left),
+ (int) Math.floor(childBounds.top),
+ (int) Math.ceil(childBounds.right),
+ (int) Math.ceil(childBounds.bottom));
+ if (DEBUG_INVALIDATE) {
+ Log.v(TAG, "INVALIDATE(" + (int) Math.floor(childBounds.left)
+ + "," + (int) Math.floor(childBounds.top)
+ + "," + (int) Math.ceil(childBounds.right)
+ + "," + (int) Math.ceil(childBounds.bottom));
+ }
+ }
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ LayoutInflater inflater = (LayoutInflater)
+ mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ setScrollbarFadingEnabled(true);
+
+ mLinearLayout = (LinearLayout) findViewById(R.id.recents_linear_layout);
+
+ final int leftPadding = mContext.getResources()
+ .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin);
+ setOverScrollEffectPadding(leftPadding, 0);
+ }
+
+ private void setOverScrollEffectPadding(int leftPadding, int i) {
+ // TODO Add to (Vertical)ScrollView
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ // Keep track of the last visible item in the list so we can restore it
+ // to the bottom when the orientation changes.
+ mLastScrollPosition = scrollPositionOfMostRecent();
+
+ // This has to happen post-layout, so run it "in the future"
+ post(new Runnable() {
+ public void run() {
+ scrollTo(0, mLastScrollPosition);
+ }
+ });
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ // scroll to bottom after reloading
+ if (visibility == View.VISIBLE && changedView == this) {
+ post(new Runnable() {
+ public void run() {
+ update();
+ }
+ });
+ }
+ }
+
+ public void setAdapter(ActvityDescriptionAdapter adapter) {
+ mAdapter = adapter;
+ mAdapter.registerDataSetObserver(new DataSetObserver() {
+ public void onChanged() {
+ update();
+ }
+
+ public void onInvalidated() {
+ update();
+ }
+ });
+ }
+
+ @Override
+ public void setLayoutTransition(LayoutTransition transition) {
+ // The layout transition applies to our embedded LinearLayout
+ mLinearLayout.setLayoutTransition(transition);
+ }
+
+ public void onClick(View view) {
+ mCallback.handleOnClick(view);
+ }
+
+ public void setCallback(RecentsCallback callback) {
+ mCallback = callback;
+ }
+
+ public boolean onTouch(View v, MotionEvent event) {
+ mCurrentView = v;
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/carousel/RecentApplicationsActivity.java
index 8c6eefb..45e230f 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/carousel/RecentApplicationsActivity.java
@@ -15,7 +15,7 @@
*/
-package com.android.systemui.recent;
+package com.android.systemui.recent.carousel;
import com.android.systemui.R;
@@ -410,7 +410,7 @@ public class RecentApplicationsActivity extends Activity {
void updateRunningTasks() {
mRunningTaskList = mActivityManager.getRunningTasks(MAX_TASKS,
- ActivityManager.TASKS_GET_THUMBNAILS, mThumbnailReceiver);
+ 0, mThumbnailReceiver);
if (DBG) Log.v(TAG, "Portrait: " + mPortraitMode);
for (RunningTaskInfo r : mRunningTaskList) {
if (r.thumbnail != null) {
@@ -441,8 +441,7 @@ public class RecentApplicationsActivity extends Activity {
final ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
final List<ActivityManager.RecentTaskInfo> recentTasks =
- am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE
- | ActivityManager.TASKS_GET_THUMBNAILS);
+ am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
.resolveActivityInfo(pm, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsCarouselView.java b/packages/SystemUI/src/com/android/systemui/recent/carousel/RecentApplicationsCarouselView.java
index 1c8ec95..1afb086 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsCarouselView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/carousel/RecentApplicationsCarouselView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.recent;
+package com.android.systemui.recent.carousel;
import android.content.Context;
import android.util.AttributeSet;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index d55a7c2..da1e1c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -35,28 +35,33 @@ import com.android.internal.statusbar.StatusBarNotification;
public class CommandQueue extends IStatusBar.Stub {
private static final String TAG = "StatusBar.CommandQueue";
- private static final int MSG_MASK = 0xffff0000;
- private static final int INDEX_MASK = 0x0000ffff;
+ private static final int INDEX_MASK = 0xffff;
+ private static final int MSG_SHIFT = 16;
+ private static final int MSG_MASK = 0xffff << MSG_SHIFT;
- private static final int MSG_ICON = 0x00010000;
- private static final int OP_SET_ICON = 1;
+
+ private static final int MSG_ICON = 1 << MSG_SHIFT;
+ private static final int OP_SET_ICON = 1;
private static final int OP_REMOVE_ICON = 2;
- private static final int MSG_ADD_NOTIFICATION = 0x00020000;
- private static final int MSG_UPDATE_NOTIFICATION = 0x00030000;
- private static final int MSG_REMOVE_NOTIFICATION = 0x00040000;
+ private static final int MSG_ADD_NOTIFICATION = 2 << MSG_SHIFT;
+ private static final int MSG_UPDATE_NOTIFICATION = 3 << MSG_SHIFT;
+ private static final int MSG_REMOVE_NOTIFICATION = 4 << MSG_SHIFT;
+
+ private static final int MSG_DISABLE = 5 << MSG_SHIFT;
- private static final int MSG_DISABLE = 0x00050000;
+ private static final int MSG_SET_VISIBILITY = 6 << MSG_SHIFT;
+ private static final int OP_EXPAND = 1;
+ private static final int OP_COLLAPSE = 2;
- private static final int MSG_SET_VISIBILITY = 0x00060000;
- private static final int OP_EXPAND = 1;
- private static final int OP_COLLAPSE = 2;
+ private static final int MSG_SET_LIGHTS_ON = 7 << MSG_SHIFT;
- private static final int MSG_SET_LIGHTS_ON = 0x00070000;
+ private static final int MSG_SHOW_MENU = 8 << MSG_SHIFT;
+ private static final int MSG_SHOW_IME_BUTTON = 9 << MSG_SHIFT;
+ private static final int MSG_SET_HARD_KEYBOARD_STATUS = 10 << MSG_SHIFT;
+
+ private static final int MSG_USER_ACTIVITY = 11 << MSG_SHIFT;
- private static final int MSG_SHOW_MENU = 0x00080000;
- private static final int MSG_SHOW_IME_BUTTON = 0x00090000;
- private static final int MSG_SET_HARD_KEYBOARD_STATUS = 0x000a0000;
private StatusBarIconList mList;
private Callbacks mCallbacks;
@@ -85,6 +90,7 @@ public class CommandQueue extends IStatusBar.Stub {
public void setMenuKeyVisible(boolean visible);
public void setImeWindowStatus(IBinder token, int vis, int backDisposition);
public void setHardKeyboardStatus(boolean available, boolean enabled);
+ public void userActivity();
}
public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
@@ -183,6 +189,13 @@ public class CommandQueue extends IStatusBar.Stub {
}
}
+ public void userActivity() {
+ synchronized (mList) {
+ mHandler.removeMessages(MSG_USER_ACTIVITY);
+ mHandler.obtainMessage(MSG_USER_ACTIVITY, 0, 0, null).sendToTarget();
+ }
+ }
+
private final class H extends Handler {
public void handleMessage(Message msg) {
final int what = msg.what & MSG_MASK;
@@ -249,6 +262,9 @@ public class CommandQueue extends IStatusBar.Stub {
case MSG_SET_HARD_KEYBOARD_STATUS:
mCallbacks.setHardKeyboardStatus(msg.arg1 != 0, msg.arg2 != 0);
break;
+ case MSG_USER_ACTIVITY:
+ mCallbacks.userActivity();
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
new file mode 100644
index 0000000..7dafb89
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.os.ServiceManager;
+import android.util.AttributeSet;
+import android.view.Display;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.Surface;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.content.res.Configuration;
+
+import com.android.internal.statusbar.IStatusBarService;
+
+import com.android.systemui.R;
+
+public class NavigationBarView extends LinearLayout {
+ final static boolean NAVBAR_ALWAYS_AT_RIGHT = true;
+
+ protected IStatusBarService mBarService;
+ final Display mDisplay;
+ View[] mRotatedViews = new View[4];
+ View mBackground;
+ Animator mLastAnimator = null;
+
+ public NavigationBarView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mDisplay = ((WindowManager)context.getSystemService(
+ Context.WINDOW_SERVICE)).getDefaultDisplay();
+ mBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+
+ //setLayerType(View.LAYER_TYPE_HARDWARE, null);
+
+ setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
+ @Override
+ public void onSystemUiVisibilityChange(int visibility) {
+ boolean on = (visibility == View.STATUS_BAR_VISIBLE);
+ android.util.Log.d("NavigationBarView", "LIGHTS "
+ + (on ? "ON" : "OUT"));
+ setLights(on);
+ }
+ });
+ }
+
+ private void setLights(final boolean on) {
+ float oldAlpha = mBackground.getAlpha();
+ android.util.Log.d("NavigationBarView", "animating alpha: " + oldAlpha + " -> "
+ + (on ? 1f : 0f));
+
+ if (mLastAnimator != null && mLastAnimator.isRunning()) mLastAnimator.cancel();
+
+ mLastAnimator = ObjectAnimator.ofFloat(mBackground, "alpha", oldAlpha, on ? 1f : 0f)
+ .setDuration(on ? 250 : 1500);
+ mLastAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator _a) {
+ mLastAnimator = null;
+ }
+ });
+ mLastAnimator.start();
+ }
+
+ public void onFinishInflate() {
+ mBackground = findViewById(R.id.background);
+
+ mRotatedViews[Surface.ROTATION_0] =
+ mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
+
+ mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);
+
+ mRotatedViews[Surface.ROTATION_270] = NAVBAR_ALWAYS_AT_RIGHT
+ ? findViewById(R.id.rot90)
+ : findViewById(R.id.rot270);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ // immediately bring up the lights
+ setLights(true);
+ return false; // pass it on
+ }
+
+ public void reorient() {
+ final int rot = mDisplay.getRotation();
+ for (int i=0; i<4; i++) {
+ mRotatedViews[i].setVisibility(View.GONE);
+ }
+ mRotatedViews[rot].setVisibility(View.VISIBLE);
+
+ android.util.Log.d("NavigationBarView", "reorient(): rot=" + mDisplay.getRotation());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 1e46246..00de920 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -36,15 +36,18 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.Handler;
import android.os.Message;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Slog;
import android.util.Log;
import android.view.Display;
import android.view.Gravity;
+import android.view.IWindowManager;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
+import android.view.Surface;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
@@ -100,6 +103,8 @@ public class PhoneStatusBar extends StatusBar {
int mIconSize;
Display mDisplay;
+ IWindowManager mWindowManager;
+
PhoneStatusBarView mStatusBarView;
int mPixelFormat;
H mHandler = new H();
@@ -141,6 +146,9 @@ public class PhoneStatusBar extends StatusBar {
// for immersive activities
private View mIntruderAlertView;
+ // on-screen navigation buttons
+ private NavigationBarView mNavigationBarView = null;
+
// the tracker view
TrackingView mTrackingView;
WindowManager.LayoutParams mTrackingParams;
@@ -197,9 +205,14 @@ public class PhoneStatusBar extends StatusBar {
mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
+ mWindowManager = IWindowManager.Stub.asInterface(
+ ServiceManager.getService(Context.WINDOW_SERVICE));
+
super.start();
- addIntruderView();
+ addNavigationBar();
+
+ //addIntruderView();
// Lastly, call to the icon policy to install/update all the icons.
mIconPolicy = new PhoneStatusBarPolicy(mContext);
@@ -223,6 +236,16 @@ public class PhoneStatusBar extends StatusBar {
mIntruderAlertView.setVisibility(View.GONE);
mIntruderAlertView.setClickable(true);
+ try {
+ boolean showNav = res.getBoolean(R.bool.config_showNavigationBar);
+ if (showNav) {
+ mNavigationBarView =
+ (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
+ }
+ } catch (Resources.NotFoundException ex) {
+ // no nav bar for you
+ }
+
PhoneStatusBarView sb = (PhoneStatusBarView)View.inflate(context,
R.layout.status_bar, null);
sb.mService = this;
@@ -292,6 +315,64 @@ public class PhoneStatusBar extends StatusBar {
return res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
}
+ // For small-screen devices (read: phones) that lack hardware navigation buttons
+ private void addNavigationBar() {
+ if (mNavigationBarView == null) return;
+
+ mNavigationBarView.reorient();
+ WindowManagerImpl.getDefault().addView(
+ mNavigationBarView, getNavigationBarLayoutParams());
+ }
+
+ private void repositionNavigationBar() {
+ if (mNavigationBarView == null) return;
+
+ mNavigationBarView.reorient();
+ WindowManagerImpl.getDefault().updateViewLayout(
+ mNavigationBarView, getNavigationBarLayoutParams());
+ }
+
+ private WindowManager.LayoutParams getNavigationBarLayoutParams() {
+ final int rotation = mDisplay.getRotation();
+ final boolean sideways =
+ (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
+
+ final Resources res = mContext.getResources();
+ final int size = res.getDimensionPixelSize(R.dimen.navigation_bar_size);
+
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ sideways ? size : ViewGroup.LayoutParams.MATCH_PARENT,
+ sideways ? ViewGroup.LayoutParams.MATCH_PARENT : size,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+ 0
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+ PixelFormat.TRANSLUCENT);
+
+ lp.setTitle("NavigationBar");
+ switch (rotation) {
+ case Surface.ROTATION_90:
+ // device has been turned 90deg counter-clockwise
+ lp.gravity = Gravity.RIGHT | Gravity.FILL_VERTICAL;
+ break;
+ case Surface.ROTATION_270:
+ // device has been turned 90deg clockwise
+ lp.gravity = (NavigationBarView.NAVBAR_ALWAYS_AT_RIGHT ? Gravity.RIGHT
+ : Gravity.LEFT)
+ | Gravity.FILL_VERTICAL;
+ break;
+ default:
+ lp.gravity = Gravity.BOTTOM | Gravity.FILL_HORIZONTAL;
+ break;
+ }
+ lp.windowAnimations = 0;
+
+ return lp;
+ }
+
private void addIntruderView() {
final int height = getStatusBarHeight();
@@ -511,6 +592,38 @@ public class PhoneStatusBar extends StatusBar {
Context.LAYOUT_INFLATER_SERVICE);
View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
+ // wire up the veto button
+ View vetoButton = row.findViewById(R.id.veto);
+ if (notification.isClearable()) {
+ final String _pkg = notification.pkg;
+ final String _tag = notification.tag;
+ final int _id = notification.id;
+ vetoButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ try {
+ mBarService.onNotificationClear(_pkg, _tag, _id);
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+ }
+ });
+ } else {
+ if ((notification.notification.flags & Notification.FLAG_ONGOING_EVENT) == 0) {
+ vetoButton.setVisibility(View.INVISIBLE);
+ } else {
+ vetoButton.setVisibility(View.GONE);
+ }
+ }
+
+ // the large icon
+ ImageView largeIcon = (ImageView)row.findViewById(R.id.large_icon);
+ if (notification.notification.largeIcon != null) {
+ largeIcon.setImageBitmap(notification.notification.largeIcon);
+ } else {
+ largeIcon.getLayoutParams().width = 0;
+ largeIcon.setVisibility(View.INVISIBLE);
+ }
+
// bind the click event to the content area
ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
@@ -1010,12 +1123,23 @@ public class PhoneStatusBar extends StatusBar {
}
public void setLightsOn(boolean on) {
+ Log.v(TAG, "lights " + (on ? "on" : "off"));
if (!on) {
// All we do for "lights out" mode on a phone is hide the status bar,
// which the window manager does. But we do need to hide the windowshade
// on our own.
animateCollapse();
}
+ notifyLightsChanged(on);
+ }
+
+ private void notifyLightsChanged(boolean shown) {
+ try {
+ Slog.d(TAG, "lights " + (shown?"on":"out"));
+ mWindowManager.statusBarVisibilityChanged(
+ shown ? View.STATUS_BAR_VISIBLE : View.STATUS_BAR_HIDDEN);
+ } catch (RemoteException ex) {
+ }
}
// Not supported
@@ -1426,6 +1550,12 @@ public class PhoneStatusBar extends StatusBar {
}
}
+ public void userActivity() {
+ try {
+ mBarService.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
+ } catch (RemoteException ex) { }
+ }
+
/**
* The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
* This was added last-minute and is inconsistent with the way the rest of the notifications
@@ -1497,6 +1627,7 @@ public class PhoneStatusBar extends StatusBar {
animateCollapse();
}
else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
+ repositionNavigationBar();
updateResources();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java
new file mode 100644
index 0000000..9b44f78
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.RemoteException;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+public class CompatModeButton extends ImageView implements View.OnClickListener {
+ private static final String TAG = "StatusBar.CompatModeButton";
+
+ private ActivityManager mAM;
+
+ public CompatModeButton(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CompatModeButton(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs);
+
+ setClickable(true);
+
+ mAM = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+
+ setOnClickListener(this);
+
+ refresh();
+ }
+
+ @Override
+ public void onClick(View v) {
+ mAM.setFrontActivityScreenCompatMode(ActivityManager.COMPAT_MODE_TOGGLE);
+ }
+
+ public void refresh() {
+ setVisibility(
+ (mAM.getFrontActivityScreenCompatMode() == ActivityManager.COMPAT_MODE_NEVER)
+ ? View.GONE
+ : View.VISIBLE
+ );
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java
index e9db998..339e3f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java
@@ -422,9 +422,8 @@ public class InputMethodsPanel extends LinearLayout implements StatusBarPanel,
Log.d(TAG, "Get text from: " + imi.getPackageName() + subtype.getNameResId()
+ imi.getServiceInfo().applicationInfo);
}
- // TODO: Change the language of subtype name according to subtype's locale.
- return mPackageManager.getText(
- imi.getPackageName(), subtype.getNameResId(), imi.getServiceInfo().applicationInfo);
+ return subtype.getDisplayName(
+ mContext, imi.getPackageName(), imi.getServiceInfo().applicationInfo);
}
private Drawable getSubtypeIcon(InputMethodInfo imi, InputMethodSubtype subtype) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 1b73e29..ffb45ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -69,10 +69,12 @@ import com.android.systemui.R;
import com.android.systemui.statusbar.*;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.CompatModeButton;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.Prefs;
-import com.android.systemui.recent.RecentApplicationsActivity;
+import com.android.systemui.recent.RecentsPanelView;
+import com.android.systemui.recent.carousel.RecentApplicationsActivity;
public class TabletStatusBar extends StatusBar implements
HeightReceiver.OnBarHeightChangedListener,
@@ -162,7 +164,7 @@ public class TabletStatusBar extends StatusBar implements
// for disabling the status bar
int mDisabled = 0;
- private RecentAppsPanel mRecentsPanel;
+ private RecentsPanelView mRecentsPanel;
private InputMethodsPanel mInputMethodsPanel;
public Context getContext() { return mContext; }
@@ -255,7 +257,7 @@ public class TabletStatusBar extends StatusBar implements
WindowManagerImpl.getDefault().addView(mNotificationPeekWindow, lp);
// Recents Panel
- mRecentsPanel = (RecentAppsPanel) View.inflate(context,
+ mRecentsPanel = (RecentsPanelView) View.inflate(context,
R.layout.status_bar_recent_panel, null);
mRecentsPanel.setVisibility(View.GONE);
mRecentsPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL,
@@ -962,6 +964,10 @@ public class TabletStatusBar extends StatusBar implements
// See above re: lights-out policy for legacy apps.
if (visible) setLightsOn(true);
+
+ // XXX: HACK: not sure if this is the best way to catch a new activity that might require a
+ // change in compatibility features, but it's a start.
+ ((CompatModeButton) mBarContents.findViewById(R.id.compat_button)).refresh();
}
public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
@@ -1214,7 +1220,7 @@ public class TabletStatusBar extends StatusBar implements
if (mVT != null) {
if (action == MotionEvent.ACTION_UP
// was this a sloppy tap?
- && Math.abs(event.getX() - mInitialTouchX) < mTouchSlop
+ && Math.abs(event.getX() - mInitialTouchX) < mTouchSlop
&& Math.abs(event.getY() - mInitialTouchY) < (mTouchSlop / 3)
// dragging off the bottom doesn't count
&& (int)event.getY() < v.getBottom()) {
@@ -1317,7 +1323,7 @@ public class TabletStatusBar extends StatusBar implements
if (!peeking) {
if (action == MotionEvent.ACTION_UP
// was this a sloppy tap?
- && Math.abs(event.getX() - mInitialTouchX) < mTouchSlop
+ && Math.abs(event.getX() - mInitialTouchX) < mTouchSlop
&& Math.abs(event.getY() - mInitialTouchY) < (mTouchSlop / 3)
// dragging off the bottom doesn't count
&& (int)event.getY() < v.getBottom()) {
@@ -1586,6 +1592,9 @@ public class TabletStatusBar extends StatusBar implements
return true;
}
+ public void userActivity() {
+ }
+
public class TouchOutsideListener implements View.OnTouchListener {
private int mMsg;
private StatusBarPanel mPanel;
diff --git a/packages/TtsService/Android.mk b/packages/TtsService/Android.mk
deleted file mode 100644
index a1a3b9f..0000000
--- a/packages/TtsService/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files) \
-
-LOCAL_PACKAGE_NAME := TtsService
-LOCAL_CERTIFICATE := platform
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/TtsService/AndroidManifest.xml b/packages/TtsService/AndroidManifest.xml
deleted file mode 100755
index 46e0ad1..0000000
--- a/packages/TtsService/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.tts">
- <application android:label="TTS Service"
- android:icon="@drawable/ic_launcher_text_to_speech">
- <service android:enabled="true"
- android:name=".TtsService"
- android:label="TTS Service">
- <intent-filter>
- <action android:name="android.intent.action.START_TTS_SERVICE"/>
- <category android:name="android.intent.category.TTS"/>
- </intent-filter>
- </service>
- </application>
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-</manifest>
diff --git a/packages/TtsService/MODULE_LICENSE_APACHE2 b/packages/TtsService/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/packages/TtsService/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/packages/TtsService/NOTICE b/packages/TtsService/NOTICE
deleted file mode 100644
index 64aaa8d..0000000
--- a/packages/TtsService/NOTICE
+++ /dev/null
@@ -1,190 +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.
-
- 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/packages/TtsService/jni/Android.mk b/packages/TtsService/jni/Android.mk
deleted file mode 100755
index 5dc0c30..0000000
--- a/packages/TtsService/jni/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- android_tts_SynthProxy.cpp
-
-LOCAL_C_INCLUDES += \
- frameworks/base/native/include \
- $(JNI_H_INCLUDE)
-
-LOCAL_SHARED_LIBRARIES := \
- libandroid_runtime \
- libnativehelper \
- libmedia \
- libutils \
- libcutils
-
-ifeq ($(TARGET_SIMULATOR),true)
- LOCAL_LDLIBS += -ldl
-else
- LOCAL_SHARED_LIBRARIES += libdl
-endif
-
-
-LOCAL_MODULE:= libttssynthproxy
-
-LOCAL_ARM_MODE := arm
-
-include $(BUILD_SHARED_LIBRARY)
-
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp
deleted file mode 100644
index 27d1fc0..0000000
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ /dev/null
@@ -1,1073 +0,0 @@
-/*
- * Copyright (C) 2009-2010 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.
- */
-
-#include <stdio.h>
-#include <unistd.h>
-
-#define LOG_TAG "SynthProxyJNI"
-
-#include <utils/Log.h>
-#include <nativehelper/jni.h>
-#include <nativehelper/JNIHelp.h>
-#include <android_runtime/AndroidRuntime.h>
-#include <android/tts.h>
-#include <media/AudioTrack.h>
-#include <math.h>
-
-#include <dlfcn.h>
-
-#define DEFAULT_TTS_RATE 16000
-#define DEFAULT_TTS_FORMAT AudioSystem::PCM_16_BIT
-#define DEFAULT_TTS_NB_CHANNELS 1
-#define DEFAULT_TTS_BUFFERSIZE 2048
-#define DEFAULT_TTS_STREAM_TYPE AudioSystem::MUSIC
-#define DEFAULT_VOLUME 1.0f
-
-// EQ + BOOST parameters
-#define FILTER_LOWSHELF_ATTENUATION -18.0f // in dB
-#define FILTER_TRANSITION_FREQ 1100.0f // in Hz
-#define FILTER_SHELF_SLOPE 1.0f // Q
-#define FILTER_GAIN 5.5f // linear gain
-
-#define USAGEMODE_PLAY_IMMEDIATELY 0
-#define USAGEMODE_WRITE_TO_FILE 1
-
-#define SYNTHPLAYSTATE_IS_STOPPED 0
-#define SYNTHPLAYSTATE_IS_PLAYING 1
-
-using namespace android;
-
-// ----------------------------------------------------------------------------
-struct fields_t {
- jfieldID synthProxyFieldJniData;
- jclass synthProxyClass;
- jmethodID synthProxyMethodPost;
-};
-
-// structure to hold the data that is used each time the TTS engine has synthesized more data
-struct afterSynthData_t {
- jint jniStorage;
- int usageMode;
- FILE* outputFile;
- AudioSystem::stream_type streamType;
-};
-
-// ----------------------------------------------------------------------------
-// EQ data
-double amp;
-double w;
-double sinw;
-double cosw;
-double beta;
-double a0, a1, a2, b0, b1, b2;
-double m_fa, m_fb, m_fc, m_fd, m_fe;
-double x0; // x[n]
-double x1; // x[n-1]
-double x2; // x[n-2]
-double out0;// y[n]
-double out1;// y[n-1]
-double out2;// y[n-2]
-
-static float fFilterLowshelfAttenuation = FILTER_LOWSHELF_ATTENUATION;
-static float fFilterTransitionFreq = FILTER_TRANSITION_FREQ;
-static float fFilterShelfSlope = FILTER_SHELF_SLOPE;
-static float fFilterGain = FILTER_GAIN;
-static bool bUseFilter = false;
-
-void initializeEQ() {
-
- amp = float(pow(10.0, fFilterLowshelfAttenuation / 40.0));
- w = 2.0 * M_PI * (fFilterTransitionFreq / DEFAULT_TTS_RATE);
- sinw = float(sin(w));
- cosw = float(cos(w));
- beta = float(sqrt(amp)/fFilterShelfSlope);
-
- // initialize low-shelf parameters
- b0 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) + (beta*sinw));
- b1 = 2.0F * amp * ((amp-1.0F) - ((amp+1.0F)*cosw));
- b2 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) - (beta*sinw));
- a0 = (amp+1.0F) + ((amp-1.0F)*cosw) + (beta*sinw);
- a1 = 2.0F * ((amp-1.0F) + ((amp+1.0F)*cosw));
- a2 = -((amp+1.0F) + ((amp-1.0F)*cosw) - (beta*sinw));
-
- m_fa = fFilterGain * b0/a0;
- m_fb = fFilterGain * b1/a0;
- m_fc = fFilterGain * b2/a0;
- m_fd = a1/a0;
- m_fe = a2/a0;
-}
-
-void initializeFilter() {
- x0 = 0.0f;
- x1 = 0.0f;
- x2 = 0.0f;
- out0 = 0.0f;
- out1 = 0.0f;
- out2 = 0.0f;
-}
-
-void applyFilter(int16_t* buffer, size_t sampleCount) {
-
- for (size_t i=0 ; i<sampleCount ; i++) {
-
- x0 = (double) buffer[i];
-
- out0 = (m_fa*x0) + (m_fb*x1) + (m_fc*x2) + (m_fd*out1) + (m_fe*out2);
-
- x2 = x1;
- x1 = x0;
-
- out2 = out1;
- out1 = out0;
-
- if (out0 > 32767.0f) {
- buffer[i] = 32767;
- } else if (out0 < -32768.0f) {
- buffer[i] = -32768;
- } else {
- buffer[i] = (int16_t) out0;
- }
- }
-}
-
-
-// ----------------------------------------------------------------------------
-static fields_t javaTTSFields;
-
-// TODO move to synth member once we have multiple simultaneous engines running
-static Mutex engineMutex;
-
-// ----------------------------------------------------------------------------
-class SynthProxyJniStorage {
- public :
- jobject tts_ref;
- android_tts_engine_t* mEngine;
- void* mEngineLibHandle;
- AudioTrack* mAudioOut;
- int8_t mPlayState;
- Mutex mPlayLock;
- AudioSystem::stream_type mStreamType;
- uint32_t mSampleRate;
- uint32_t mAudFormat;
- int mNbChannels;
- int8_t * mBuffer;
- size_t mBufferSize;
- float mVolume[2];
-
- SynthProxyJniStorage() {
- tts_ref = NULL;
- mEngine = NULL;
- mEngineLibHandle = NULL;
- mAudioOut = NULL;
- mPlayState = SYNTHPLAYSTATE_IS_STOPPED;
- mStreamType = DEFAULT_TTS_STREAM_TYPE;
- mSampleRate = DEFAULT_TTS_RATE;
- mAudFormat = DEFAULT_TTS_FORMAT;
- mNbChannels = DEFAULT_TTS_NB_CHANNELS;
- mBufferSize = DEFAULT_TTS_BUFFERSIZE;
- mBuffer = new int8_t[mBufferSize];
- memset(mBuffer, 0, mBufferSize);
- mVolume[AudioTrack::LEFT] = DEFAULT_VOLUME;
- mVolume[AudioTrack::RIGHT] = DEFAULT_VOLUME;
- }
-
- ~SynthProxyJniStorage() {
- //LOGV("entering ~SynthProxyJniStorage()");
- killAudio();
- if (mEngine) {
- mEngine->funcs->shutdown(mEngine);
- mEngine = NULL;
- }
- if (mEngineLibHandle) {
- //LOGV("~SynthProxyJniStorage(): before close library");
- int res = dlclose(mEngineLibHandle);
- LOGE_IF( res != 0, "~SynthProxyJniStorage(): dlclose returned %d", res);
- }
- delete mBuffer;
- }
-
- void killAudio() {
- if (mAudioOut) {
- mAudioOut->stop();
- delete mAudioOut;
- mAudioOut = NULL;
- }
- }
-
- void createAudioOut(AudioSystem::stream_type streamType, uint32_t rate,
- AudioSystem::audio_format format, int channel) {
- mSampleRate = rate;
- mAudFormat = format;
- mNbChannels = channel;
- mStreamType = streamType;
-
- // retrieve system properties to ensure successful creation of the
- // AudioTrack object for playback
- int afSampleRate;
- if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) {
- afSampleRate = 44100;
- }
- int afFrameCount;
- if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) {
- afFrameCount = 2048;
- }
- uint32_t afLatency;
- if (AudioSystem::getOutputLatency(&afLatency, mStreamType) != NO_ERROR) {
- afLatency = 500;
- }
- uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate);
- if (minBufCount < 2) minBufCount = 2;
- int minFrameCount = (afFrameCount * rate * minBufCount)/afSampleRate;
-
- mPlayLock.lock();
- mAudioOut = new AudioTrack(mStreamType, rate, format,
- (channel == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,
- minFrameCount > 4096 ? minFrameCount : 4096,
- 0, 0, 0, 0); // not using an AudioTrack callback
-
- if (mAudioOut->initCheck() != NO_ERROR) {
- LOGE("createAudioOut(): AudioTrack error");
- delete mAudioOut;
- mAudioOut = NULL;
- } else {
- //LOGI("AudioTrack OK");
- mAudioOut->setVolume(mVolume[AudioTrack::LEFT], mVolume[AudioTrack::RIGHT]);
- LOGV("AudioTrack ready");
- }
- mPlayLock.unlock();
- }
-};
-
-
-// ----------------------------------------------------------------------------
-void prepAudioTrack(SynthProxyJniStorage* pJniData, AudioSystem::stream_type streamType,
- uint32_t rate, AudioSystem::audio_format format, int channel) {
- // Don't bother creating a new audiotrack object if the current
- // object is already initialized with the same audio parameters.
- if ( pJniData->mAudioOut &&
- (rate == pJniData->mSampleRate) &&
- (format == pJniData->mAudFormat) &&
- (channel == pJniData->mNbChannels) &&
- (streamType == pJniData->mStreamType) ){
- return;
- }
- if (pJniData->mAudioOut){
- pJniData->killAudio();
- }
- pJniData->createAudioOut(streamType, rate, format, channel);
-}
-
-
-// ----------------------------------------------------------------------------
-/*
- * Callback from TTS engine.
- * Directly speaks using AudioTrack or write to file
- */
-extern "C" android_tts_callback_status_t
-__ttsSynthDoneCB(void ** pUserdata, uint32_t rate,
- android_tts_audio_format_t format, int channel,
- int8_t **pWav, size_t *pBufferSize,
- android_tts_synth_status_t status)
-{
- //LOGV("ttsSynthDoneCallback: %d bytes", bufferSize);
- AudioSystem::audio_format encoding;
-
- if (*pUserdata == NULL){
- LOGE("userdata == NULL");
- return ANDROID_TTS_CALLBACK_HALT;
- }
- switch (format) {
- case ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT:
- encoding = AudioSystem::PCM_8_BIT;
- break;
- case ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT:
- encoding = AudioSystem::PCM_16_BIT;
- break;
- default:
- LOGE("Can't play, bad format");
- return ANDROID_TTS_CALLBACK_HALT;
- }
- afterSynthData_t* pForAfter = (afterSynthData_t*) *pUserdata;
- SynthProxyJniStorage* pJniData = (SynthProxyJniStorage*)(pForAfter->jniStorage);
-
- if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY){
- //LOGV("Direct speech");
-
- if (*pWav == NULL) {
- delete pForAfter;
- pForAfter = NULL;
- LOGV("Null: speech has completed");
- return ANDROID_TTS_CALLBACK_HALT;
- }
-
- if (*pBufferSize > 0) {
- prepAudioTrack(pJniData, pForAfter->streamType, rate, encoding, channel);
- if (pJniData->mAudioOut) {
- pJniData->mPlayLock.lock();
- if(pJniData->mAudioOut->stopped()
- && (pJniData->mPlayState == SYNTHPLAYSTATE_IS_PLAYING)) {
- pJniData->mAudioOut->start();
- }
- pJniData->mPlayLock.unlock();
- if (bUseFilter) {
- applyFilter((int16_t*)*pWav, *pBufferSize/2);
- }
- pJniData->mAudioOut->write(*pWav, *pBufferSize);
- memset(*pWav, 0, *pBufferSize);
- //LOGV("AudioTrack wrote: %d bytes", bufferSize);
- } else {
- LOGE("Can't play, null audiotrack");
- delete pForAfter;
- pForAfter = NULL;
- return ANDROID_TTS_CALLBACK_HALT;
- }
- }
- } else if (pForAfter->usageMode == USAGEMODE_WRITE_TO_FILE) {
- //LOGV("Save to file");
- if (*pWav == NULL) {
- delete pForAfter;
- LOGV("Null: speech has completed");
- return ANDROID_TTS_CALLBACK_HALT;
- }
- if (*pBufferSize > 0){
- if (bUseFilter) {
- applyFilter((int16_t*)*pWav, *pBufferSize/2);
- }
- fwrite(*pWav, 1, *pBufferSize, pForAfter->outputFile);
- memset(*pWav, 0, *pBufferSize);
- }
- }
- // Future update:
- // For sync points in the speech, call back into the SynthProxy class through the
- // javaTTSFields.synthProxyMethodPost methode to notify
- // playback has completed if the synthesis is done or if a marker has been reached.
-
- if (status == ANDROID_TTS_SYNTH_DONE) {
- // this struct was allocated in the original android_tts_SynthProxy_speak call,
- // all processing matching this call is now done.
- LOGV("Speech synthesis done.");
- if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY) {
- // only delete for direct playback. When writing to a file, we still have work to do
- // in android_tts_SynthProxy_synthesizeToFile. The struct will be deleted there.
- delete pForAfter;
- pForAfter = NULL;
- }
- return ANDROID_TTS_CALLBACK_HALT;
- }
-
- // we don't update the wav (output) parameter as we'll let the next callback
- // write at the same location, we've consumed the data already, but we need
- // to update bufferSize to let the TTS engine know how much it can write the
- // next time it calls this function.
- *pBufferSize = pJniData->mBufferSize;
-
- return ANDROID_TTS_CALLBACK_CONTINUE;
-}
-
-
-// ----------------------------------------------------------------------------
-static int
-android_tts_SynthProxy_setLowShelf(JNIEnv *env, jobject thiz, jboolean applyFilter,
- jfloat filterGain, jfloat attenuationInDb, jfloat freqInHz, jfloat slope)
-{
- int result = ANDROID_TTS_SUCCESS;
-
- bUseFilter = applyFilter;
- if (applyFilter) {
- fFilterLowshelfAttenuation = attenuationInDb;
- fFilterTransitionFreq = freqInHz;
- fFilterShelfSlope = slope;
- fFilterGain = filterGain;
-
- if (fFilterShelfSlope != 0.0f) {
- initializeEQ();
- } else {
- LOGE("Invalid slope, can't be null");
- result = ANDROID_TTS_FAILURE;
- }
- }
-
- return result;
-}
-
-// ----------------------------------------------------------------------------
-static int
-android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
- jobject weak_this, jstring nativeSoLib, jstring engConfig)
-{
- int result = ANDROID_TTS_FAILURE;
-
- bUseFilter = false;
-
- SynthProxyJniStorage* pJniStorage = new SynthProxyJniStorage();
-
- prepAudioTrack(pJniStorage,
- DEFAULT_TTS_STREAM_TYPE, DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS);
-
- const char *nativeSoLibNativeString = env->GetStringUTFChars(nativeSoLib, 0);
- const char *engConfigString = env->GetStringUTFChars(engConfig, 0);
-
- void *engine_lib_handle = dlopen(nativeSoLibNativeString,
- RTLD_NOW | RTLD_LOCAL);
- if (engine_lib_handle == NULL) {
- LOGE("android_tts_SynthProxy_native_setup(): engine_lib_handle == NULL");
- } else {
- android_tts_engine_t * (*get_TtsEngine)() =
- reinterpret_cast<android_tts_engine_t* (*)()>(dlsym(engine_lib_handle, "android_getTtsEngine"));
-
- // Support obsolete/legacy binary modules
- if (get_TtsEngine == NULL) {
- get_TtsEngine =
- reinterpret_cast<android_tts_engine_t* (*)()>(dlsym(engine_lib_handle, "getTtsEngine"));
- }
-
- pJniStorage->mEngine = (*get_TtsEngine)();
- pJniStorage->mEngineLibHandle = engine_lib_handle;
-
- android_tts_engine_t *engine = pJniStorage->mEngine;
- if (engine) {
- Mutex::Autolock l(engineMutex);
- engine->funcs->init(
- engine,
- __ttsSynthDoneCB,
- engConfigString);
- }
-
- result = ANDROID_TTS_SUCCESS;
- }
-
- // we use a weak reference so the SynthProxy object can be garbage collected.
- pJniStorage->tts_ref = env->NewGlobalRef(weak_this);
-
- // save the JNI resources so we can use them (and free them) later
- env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData, (int)pJniStorage);
-
- env->ReleaseStringUTFChars(nativeSoLib, nativeSoLibNativeString);
- env->ReleaseStringUTFChars(engConfig, engConfigString);
-
- return result;
-}
-
-
-static void
-android_tts_SynthProxy_native_finalize(JNIEnv *env, jobject thiz, jint jniData)
-{
- //LOGV("entering android_tts_SynthProxy_finalize()");
- if (jniData == 0) {
- //LOGE("android_tts_SynthProxy_native_finalize(): invalid JNI data");
- return;
- }
-
- Mutex::Autolock l(engineMutex);
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- env->DeleteGlobalRef(pSynthData->tts_ref);
- delete pSynthData;
-
- env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData, 0);
-}
-
-
-static void
-android_tts_SynthProxy_shutdown(JNIEnv *env, jobject thiz, jint jniData)
-{
- //LOGV("entering android_tts_SynthProxy_shutdown()");
-
- // do everything a call to finalize would
- android_tts_SynthProxy_native_finalize(env, thiz, jniData);
-}
-
-
-static int
-android_tts_SynthProxy_isLanguageAvailable(JNIEnv *env, jobject thiz, jint jniData,
- jstring language, jstring country, jstring variant)
-{
- int result = ANDROID_TTS_LANG_NOT_SUPPORTED;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_isLanguageAvailable(): invalid JNI data");
- return result;
- }
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- const char *langNativeString = env->GetStringUTFChars(language, 0);
- const char *countryNativeString = env->GetStringUTFChars(country, 0);
- const char *variantNativeString = env->GetStringUTFChars(variant, 0);
-
- android_tts_engine_t *engine = pSynthData->mEngine;
-
- if (engine) {
- result = engine->funcs->isLanguageAvailable(engine,langNativeString,
- countryNativeString, variantNativeString);
- }
- env->ReleaseStringUTFChars(language, langNativeString);
- env->ReleaseStringUTFChars(country, countryNativeString);
- env->ReleaseStringUTFChars(variant, variantNativeString);
- return result;
-}
-
-static int
-android_tts_SynthProxy_setConfig(JNIEnv *env, jobject thiz, jint jniData, jstring engineConfig)
-{
- int result = ANDROID_TTS_FAILURE;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_setConfig(): invalid JNI data");
- return result;
- }
-
- Mutex::Autolock l(engineMutex);
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- const char *engineConfigNativeString = env->GetStringUTFChars(engineConfig, 0);
- android_tts_engine_t *engine = pSynthData->mEngine;
-
- if (engine) {
- result = engine->funcs->setProperty(engine,ANDROID_TTS_ENGINE_PROPERTY_CONFIG,
- engineConfigNativeString, strlen(engineConfigNativeString));
- }
- env->ReleaseStringUTFChars(engineConfig, engineConfigNativeString);
-
- return result;
-}
-
-static int
-android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData,
- jstring language, jstring country, jstring variant)
-{
- int result = ANDROID_TTS_LANG_NOT_SUPPORTED;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_setLanguage(): invalid JNI data");
- return result;
- }
-
- Mutex::Autolock l(engineMutex);
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- const char *langNativeString = env->GetStringUTFChars(language, 0);
- const char *countryNativeString = env->GetStringUTFChars(country, 0);
- const char *variantNativeString = env->GetStringUTFChars(variant, 0);
- android_tts_engine_t *engine = pSynthData->mEngine;
-
- if (engine) {
- result = engine->funcs->setLanguage(engine, langNativeString,
- countryNativeString, variantNativeString);
- }
- env->ReleaseStringUTFChars(language, langNativeString);
- env->ReleaseStringUTFChars(country, countryNativeString);
- env->ReleaseStringUTFChars(variant, variantNativeString);
- return result;
-}
-
-
-static int
-android_tts_SynthProxy_loadLanguage(JNIEnv *env, jobject thiz, jint jniData,
- jstring language, jstring country, jstring variant)
-{
- int result = ANDROID_TTS_LANG_NOT_SUPPORTED;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_loadLanguage(): invalid JNI data");
- return result;
- }
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- const char *langNativeString = env->GetStringUTFChars(language, 0);
- const char *countryNativeString = env->GetStringUTFChars(country, 0);
- const char *variantNativeString = env->GetStringUTFChars(variant, 0);
- android_tts_engine_t *engine = pSynthData->mEngine;
-
- if (engine) {
- result = engine->funcs->loadLanguage(engine, langNativeString,
- countryNativeString, variantNativeString);
- }
- env->ReleaseStringUTFChars(language, langNativeString);
- env->ReleaseStringUTFChars(country, countryNativeString);
- env->ReleaseStringUTFChars(variant, variantNativeString);
-
- return result;
-}
-
-
-static int
-android_tts_SynthProxy_setSpeechRate(JNIEnv *env, jobject thiz, jint jniData,
- jint speechRate)
-{
- int result = ANDROID_TTS_FAILURE;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_setSpeechRate(): invalid JNI data");
- return result;
- }
-
- int bufSize = 12;
- char buffer [bufSize];
- sprintf(buffer, "%d", speechRate);
-
- Mutex::Autolock l(engineMutex);
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- //LOGI("setting speech rate to %d", speechRate);
- android_tts_engine_t *engine = pSynthData->mEngine;
-
- if (engine) {
- result = engine->funcs->setProperty(engine, "rate", buffer, bufSize);
- }
-
- return result;
-}
-
-
-static int
-android_tts_SynthProxy_setPitch(JNIEnv *env, jobject thiz, jint jniData,
- jint pitch)
-{
- int result = ANDROID_TTS_FAILURE;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_setPitch(): invalid JNI data");
- return result;
- }
-
- Mutex::Autolock l(engineMutex);
-
- int bufSize = 12;
- char buffer [bufSize];
- sprintf(buffer, "%d", pitch);
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- //LOGI("setting pitch to %d", pitch);
- android_tts_engine_t *engine = pSynthData->mEngine;
-
- if (engine) {
- result = engine->funcs->setProperty(engine, "pitch", buffer, bufSize);
- }
-
- return result;
-}
-
-
-static int
-android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
- jstring textJavaString, jstring filenameJavaString)
-{
- int result = ANDROID_TTS_FAILURE;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid JNI data");
- return result;
- }
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- if (!pSynthData->mEngine) {
- LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid engine handle");
- return result;
- }
-
- initializeFilter();
-
- Mutex::Autolock l(engineMutex);
-
- // Retrieve audio parameters before writing the file header
- AudioSystem::audio_format encoding;
- uint32_t rate = DEFAULT_TTS_RATE;
- int channels = DEFAULT_TTS_NB_CHANNELS;
- android_tts_engine_t *engine = pSynthData->mEngine;
- android_tts_audio_format_t format = ANDROID_TTS_AUDIO_FORMAT_DEFAULT;
-
- engine->funcs->setAudioFormat(engine, &format, &rate, &channels);
-
- switch (format) {
- case ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT:
- encoding = AudioSystem::PCM_16_BIT;
- break;
- case ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT:
- encoding = AudioSystem::PCM_8_BIT;
- break;
- default:
- LOGE("android_tts_SynthProxy_synthesizeToFile(): engine uses invalid format");
- return result;
- }
-
- const char *filenameNativeString =
- env->GetStringUTFChars(filenameJavaString, 0);
- const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
-
- afterSynthData_t* pForAfter = new (afterSynthData_t);
- pForAfter->jniStorage = jniData;
- pForAfter->usageMode = USAGEMODE_WRITE_TO_FILE;
-
- pForAfter->outputFile = fopen(filenameNativeString, "wb");
-
- if (pForAfter->outputFile == NULL) {
- LOGE("android_tts_SynthProxy_synthesizeToFile(): error creating output file");
- delete pForAfter;
- return result;
- }
-
- // Write 44 blank bytes for WAV header, then come back and fill them in
- // after we've written the audio data
- char header[44];
- fwrite(header, 1, 44, pForAfter->outputFile);
-
- unsigned int unique_identifier;
-
- memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);
-
- result = engine->funcs->synthesizeText(engine, textNativeString,
- pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter);
-
- long filelen = ftell(pForAfter->outputFile);
-
- int samples = (((int)filelen) - 44) / 2;
- header[0] = 'R';
- header[1] = 'I';
- header[2] = 'F';
- header[3] = 'F';
- ((uint32_t *)(&header[4]))[0] = filelen - 8;
- header[8] = 'W';
- header[9] = 'A';
- header[10] = 'V';
- header[11] = 'E';
-
- header[12] = 'f';
- header[13] = 'm';
- header[14] = 't';
- header[15] = ' ';
-
- ((uint32_t *)(&header[16]))[0] = 16; // size of fmt
-
- int sampleSizeInByte = (encoding == AudioSystem::PCM_16_BIT ? 2 : 1);
-
- ((unsigned short *)(&header[20]))[0] = 1; // format
- ((unsigned short *)(&header[22]))[0] = channels; // channels
- ((uint32_t *)(&header[24]))[0] = rate; // samplerate
- ((uint32_t *)(&header[28]))[0] = rate * sampleSizeInByte * channels;// byterate
- ((unsigned short *)(&header[32]))[0] = sampleSizeInByte * channels; // block align
- ((unsigned short *)(&header[34]))[0] = sampleSizeInByte * 8; // bits per sample
-
- header[36] = 'd';
- header[37] = 'a';
- header[38] = 't';
- header[39] = 'a';
-
- ((uint32_t *)(&header[40]))[0] = samples * 2; // size of data
-
- // Skip back to the beginning and rewrite the header
- fseek(pForAfter->outputFile, 0, SEEK_SET);
- fwrite(header, 1, 44, pForAfter->outputFile);
-
- fflush(pForAfter->outputFile);
- fclose(pForAfter->outputFile);
-
- delete pForAfter;
- pForAfter = NULL;
-
- env->ReleaseStringUTFChars(textJavaString, textNativeString);
- env->ReleaseStringUTFChars(filenameJavaString, filenameNativeString);
-
- return result;
-}
-
-
-static int
-android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
- jstring textJavaString, jint javaStreamType, jfloat volume, jfloat pan)
-{
- int result = ANDROID_TTS_FAILURE;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_speak(): invalid JNI data");
- return result;
- }
-
- initializeFilter();
-
- Mutex::Autolock l(engineMutex);
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-
- {//scope for lock on mPlayLock
- Mutex::Autolock _l(pSynthData->mPlayLock);
-
- pSynthData->mPlayState = SYNTHPLAYSTATE_IS_PLAYING;
-
- // clip volume and pan
- float vol = (volume > 1.0f) ? 1.0f : (volume < 0.0f) ? 0.0f : volume;
- float panning = (pan > 1.0f) ? 1.0f : (pan < -1.0f) ? -1.0f : pan;
- // compute playback volume based on volume and pan, using balance rule, in order to avoid
- // lowering volume when panning in center
- pSynthData->mVolume[AudioTrack::LEFT] = vol;
- pSynthData->mVolume[AudioTrack::RIGHT] = vol;
- if (panning > 0.0f) {
- pSynthData->mVolume[AudioTrack::LEFT] *= (1.0f - panning);
- } else if (panning < 0.0f) {
- pSynthData->mVolume[AudioTrack::RIGHT] *= (1.0f + panning);
- }
-
- // apply the volume if there is an output
- if (NULL != pSynthData->mAudioOut) {
- pSynthData->mAudioOut->setVolume(pSynthData->mVolume[AudioTrack::LEFT],
- pSynthData->mVolume[AudioTrack::RIGHT]);
- }
-
- //LOGV("android_tts_SynthProxy_speak() vol=%.3f pan=%.3f, mVolume=[%.1f %.1f]",
- // volume, pan,
- // pSynthData->mVolume[AudioTrack::LEFT], pSynthData->mVolume[AudioTrack::RIGHT]);
- }
-
- afterSynthData_t* pForAfter = new (afterSynthData_t);
- pForAfter->jniStorage = jniData;
- pForAfter->usageMode = USAGEMODE_PLAY_IMMEDIATELY;
- pForAfter->streamType = (AudioSystem::stream_type) javaStreamType;
-
- if (pSynthData->mEngine) {
- const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
- memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);
- android_tts_engine_t *engine = pSynthData->mEngine;
-
- result = engine->funcs->synthesizeText(engine, textNativeString,
- pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter);
- env->ReleaseStringUTFChars(textJavaString, textNativeString);
- }
-
- return result;
-}
-
-
-static int
-android_tts_SynthProxy_stop(JNIEnv *env, jobject thiz, jint jniData)
-{
- int result = ANDROID_TTS_FAILURE;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_stop(): invalid JNI data");
- return result;
- }
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-
- pSynthData->mPlayLock.lock();
- pSynthData->mPlayState = SYNTHPLAYSTATE_IS_STOPPED;
- if (pSynthData->mAudioOut) {
- pSynthData->mAudioOut->stop();
- }
- pSynthData->mPlayLock.unlock();
-
- android_tts_engine_t *engine = pSynthData->mEngine;
- if (engine) {
- result = engine->funcs->stop(engine);
- }
-
- return result;
-}
-
-
-static int
-android_tts_SynthProxy_stopSync(JNIEnv *env, jobject thiz, jint jniData)
-{
- int result = ANDROID_TTS_FAILURE;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_stop(): invalid JNI data");
- return result;
- }
-
- // perform a regular stop
- result = android_tts_SynthProxy_stop(env, thiz, jniData);
- // but wait on the engine having released the engine mutex which protects
- // the synthesizer resources.
- engineMutex.lock();
- engineMutex.unlock();
-
- return result;
-}
-
-
-static jobjectArray
-android_tts_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData)
-{
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_getLanguage(): invalid JNI data");
- return NULL;
- }
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-
- if (pSynthData->mEngine) {
- size_t bufSize = 100;
- char lang[bufSize];
- char country[bufSize];
- char variant[bufSize];
- memset(lang, 0, bufSize);
- memset(country, 0, bufSize);
- memset(variant, 0, bufSize);
- jobjectArray retLocale = (jobjectArray)env->NewObjectArray(3,
- env->FindClass("java/lang/String"), env->NewStringUTF(""));
-
- android_tts_engine_t *engine = pSynthData->mEngine;
- engine->funcs->getLanguage(engine, lang, country, variant);
- env->SetObjectArrayElement(retLocale, 0, env->NewStringUTF(lang));
- env->SetObjectArrayElement(retLocale, 1, env->NewStringUTF(country));
- env->SetObjectArrayElement(retLocale, 2, env->NewStringUTF(variant));
- return retLocale;
- } else {
- return NULL;
- }
-}
-
-
-JNIEXPORT int JNICALL
-android_tts_SynthProxy_getRate(JNIEnv *env, jobject thiz, jint jniData)
-{
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_getRate(): invalid JNI data");
- return 0;
- }
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- size_t bufSize = 100;
-
- char buf[bufSize];
- memset(buf, 0, bufSize);
- // TODO check return codes
- android_tts_engine_t *engine = pSynthData->mEngine;
- if (engine) {
- engine->funcs->getProperty(engine,"rate", buf, &bufSize);
- }
- return atoi(buf);
-}
-
-// Dalvik VM type signatures
-static JNINativeMethod gMethods[] = {
- { "native_stop",
- "(I)I",
- (void*)android_tts_SynthProxy_stop
- },
- { "native_stopSync",
- "(I)I",
- (void*)android_tts_SynthProxy_stopSync
- },
- { "native_speak",
- "(ILjava/lang/String;IFF)I",
- (void*)android_tts_SynthProxy_speak
- },
- { "native_synthesizeToFile",
- "(ILjava/lang/String;Ljava/lang/String;)I",
- (void*)android_tts_SynthProxy_synthesizeToFile
- },
- { "native_isLanguageAvailable",
- "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
- (void*)android_tts_SynthProxy_isLanguageAvailable
- },
- { "native_setConfig",
- "(ILjava/lang/String;)I",
- (void*)android_tts_SynthProxy_setConfig
- },
- { "native_setLanguage",
- "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
- (void*)android_tts_SynthProxy_setLanguage
- },
- { "native_loadLanguage",
- "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
- (void*)android_tts_SynthProxy_loadLanguage
- },
- { "native_setSpeechRate",
- "(II)I",
- (void*)android_tts_SynthProxy_setSpeechRate
- },
- { "native_setPitch",
- "(II)I",
- (void*)android_tts_SynthProxy_setPitch
- },
- { "native_getLanguage",
- "(I)[Ljava/lang/String;",
- (void*)android_tts_SynthProxy_getLanguage
- },
- { "native_getRate",
- "(I)I",
- (void*)android_tts_SynthProxy_getRate
- },
- { "native_shutdown",
- "(I)V",
- (void*)android_tts_SynthProxy_shutdown
- },
- { "native_setup",
- "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)I",
- (void*)android_tts_SynthProxy_native_setup
- },
- { "native_setLowShelf",
- "(ZFFFF)I",
- (void*)android_tts_SynthProxy_setLowShelf
- },
- { "native_finalize",
- "(I)V",
- (void*)android_tts_SynthProxy_native_finalize
- }
-};
-
-#define SP_JNIDATA_FIELD_NAME "mJniData"
-#define SP_POSTSPEECHSYNTHESIZED_METHOD_NAME "postNativeSpeechSynthesizedInJava"
-
-static const char* const kClassPathName = "android/tts/SynthProxy";
-
-jint JNI_OnLoad(JavaVM* vm, void* reserved)
-{
- JNIEnv* env = NULL;
- jint result = -1;
- jclass clazz;
-
- if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- LOGE("ERROR: GetEnv failed\n");
- goto bail;
- }
- assert(env != NULL);
-
- clazz = env->FindClass(kClassPathName);
- if (clazz == NULL) {
- LOGE("Can't find %s", kClassPathName);
- goto bail;
- }
-
- javaTTSFields.synthProxyClass = clazz;
- javaTTSFields.synthProxyFieldJniData = NULL;
- javaTTSFields.synthProxyMethodPost = NULL;
-
- javaTTSFields.synthProxyFieldJniData = env->GetFieldID(clazz,
- SP_JNIDATA_FIELD_NAME, "I");
- if (javaTTSFields.synthProxyFieldJniData == NULL) {
- LOGE("Can't find %s.%s field", kClassPathName, SP_JNIDATA_FIELD_NAME);
- goto bail;
- }
-
- javaTTSFields.synthProxyMethodPost = env->GetStaticMethodID(clazz,
- SP_POSTSPEECHSYNTHESIZED_METHOD_NAME, "(Ljava/lang/Object;II)V");
- if (javaTTSFields.synthProxyMethodPost == NULL) {
- LOGE("Can't find %s.%s method", kClassPathName, SP_POSTSPEECHSYNTHESIZED_METHOD_NAME);
- goto bail;
- }
-
- if (jniRegisterNativeMethods(
- env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
- goto bail;
-
- /* success -- return valid version number */
- result = JNI_VERSION_1_4;
-
- bail:
- return result;
-}
diff --git a/packages/TtsService/proguard.flags b/packages/TtsService/proguard.flags
deleted file mode 100644
index e8bee6b..0000000
--- a/packages/TtsService/proguard.flags
+++ /dev/null
@@ -1,5 +0,0 @@
--keep class android.tts.SynthProxy {
- int mJniData;
- # keep all declarations for native methods
- <methods>;
-}
diff --git a/packages/TtsService/res/drawable-hdpi/ic_launcher_text_to_speech.png b/packages/TtsService/res/drawable-hdpi/ic_launcher_text_to_speech.png
deleted file mode 100644
index f075e0f..0000000
--- a/packages/TtsService/res/drawable-hdpi/ic_launcher_text_to_speech.png
+++ /dev/null
Binary files differ
diff --git a/packages/TtsService/res/drawable-mdpi/ic_launcher_text_to_speech.png b/packages/TtsService/res/drawable-mdpi/ic_launcher_text_to_speech.png
deleted file mode 100644
index cbae7de..0000000
--- a/packages/TtsService/res/drawable-mdpi/ic_launcher_text_to_speech.png
+++ /dev/null
Binary files differ
diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java
deleted file mode 100755
index f5f5fcf..0000000
--- a/packages/TtsService/src/android/tts/SynthProxy.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package android.tts;
-
-import android.media.AudioManager;
-import android.media.AudioSystem;
-import android.util.Log;
-import java.lang.ref.WeakReference;
-
-/**
- * @hide
- *
- * The SpeechSynthesis class provides a high-level api to create and play
- * synthesized speech. This class is used internally to talk to a native
- * TTS library that implements the interface defined in
- * frameworks/base/include/tts/TtsEngine.h
- *
- */
-@SuppressWarnings("unused")
-public class SynthProxy {
-
- // Default parameters of a filter to be applied when using the Pico engine.
- // Such a huge filter gain is justified by how much energy in the low frequencies is "wasted" at
- // the output of the synthesis. The low shelving filter removes it, leaving room for
- // amplification.
- private final static float PICO_FILTER_GAIN = 5.0f; // linear gain
- private final static float PICO_FILTER_LOWSHELF_ATTENUATION = -18.0f; // in dB
- private final static float PICO_FILTER_TRANSITION_FREQ = 1100.0f; // in Hz
- private final static float PICO_FILTER_SHELF_SLOPE = 1.0f; // Q
-
- //
- // External API
- //
-
- /**
- * Constructor; pass the location of the native TTS .so to use.
- */
- public SynthProxy(String nativeSoLib, String engineConfig) {
- boolean applyFilter = nativeSoLib.toLowerCase().contains("pico");
- Log.v(TtsService.SERVICE_TAG, "About to load "+ nativeSoLib + ", applyFilter="+applyFilter);
- native_setup(new WeakReference<SynthProxy>(this), nativeSoLib, engineConfig);
- native_setLowShelf(applyFilter, PICO_FILTER_GAIN, PICO_FILTER_LOWSHELF_ATTENUATION,
- PICO_FILTER_TRANSITION_FREQ, PICO_FILTER_SHELF_SLOPE);
- }
-
- /**
- * Stops and clears the AudioTrack.
- */
- public int stop() {
- return native_stop(mJniData);
- }
-
- /**
- * Synchronous stop of the synthesizer. This method returns when the synth
- * has completed the stop procedure and doesn't use any of the resources it
- * was using while synthesizing.
- *
- * @return {@link android.speech.tts.TextToSpeech.SUCCESS} or
- * {@link android.speech.tts.TextToSpeech.ERROR}
- */
- public int stopSync() {
- return native_stopSync(mJniData);
- }
-
- /**
- * Synthesize speech and speak it directly using AudioTrack.
- */
- public int speak(String text, int streamType, float volume, float pan) {
- Log.i(TAG, "speak() on stream "+ streamType);
- if ((streamType > -1) && (streamType < AudioSystem.getNumStreamTypes())) {
- return native_speak(mJniData, text, streamType, volume, pan);
- } else {
- Log.e("SynthProxy", "Trying to speak with invalid stream type " + streamType);
- return native_speak(mJniData, text, AudioManager.STREAM_MUSIC, volume, pan);
- }
- }
-
- /**
- * Synthesize speech to a file. The current implementation writes a valid
- * WAV file to the given path, assuming it is writable. Something like
- * "/sdcard/???.wav" is recommended.
- */
- public int synthesizeToFile(String text, String filename) {
- Log.i(TAG, "synthesizeToFile() to file "+ filename);
- return native_synthesizeToFile(mJniData, text, filename);
- }
-
- /**
- * Queries for language support.
- * Return codes are defined in android.speech.tts.TextToSpeech
- */
- public int isLanguageAvailable(String language, String country, String variant) {
- return native_isLanguageAvailable(mJniData, language, country, variant);
- }
-
- /**
- * Updates the engine configuration.
- */
- public int setConfig(String engineConfig) {
- return native_setConfig(mJniData, engineConfig);
- }
-
- /**
- * Sets the language.
- */
- public int setLanguage(String language, String country, String variant) {
- return native_setLanguage(mJniData, language, country, variant);
- }
-
- /**
- * Loads the language: it's not set, but prepared for use later.
- */
- public int loadLanguage(String language, String country, String variant) {
- return native_loadLanguage(mJniData, language, country, variant);
- }
-
- /**
- * Sets the speech rate.
- */
- public final int setSpeechRate(int speechRate) {
- return native_setSpeechRate(mJniData, speechRate);
- }
-
- /**
- * Sets the pitch of the synthesized voice.
- */
- public final int setPitch(int pitch) {
- return native_setPitch(mJniData, pitch);
- }
-
- /**
- * Returns the currently set language, country and variant information.
- */
- public String[] getLanguage() {
- return native_getLanguage(mJniData);
- }
-
- /**
- * Gets the currently set rate.
- */
- public int getRate() {
- return native_getRate(mJniData);
- }
-
- /**
- * Shuts down the native synthesizer.
- */
- public void shutdown() {
- native_shutdown(mJniData);
- }
-
- //
- // Internal
- //
-
- protected void finalize() {
- native_finalize(mJniData);
- mJniData = 0;
- }
-
- static {
- System.loadLibrary("ttssynthproxy");
- }
-
- private final static String TAG = "SynthProxy";
-
- /**
- * Accessed by native methods
- */
- private int mJniData = 0;
-
- private native final int native_setup(Object weak_this, String nativeSoLib,
- String engineConfig);
-
- private native final int native_setLowShelf(boolean applyFilter, float filterGain,
- float attenuationInDb, float freqInHz, float slope);
-
- private native final void native_finalize(int jniData);
-
- private native final int native_stop(int jniData);
-
- private native final int native_stopSync(int jniData);
-
- private native final int native_speak(int jniData, String text, int streamType, float volume,
- float pan);
-
- private native final int native_synthesizeToFile(int jniData, String text, String filename);
-
- private native final int native_isLanguageAvailable(int jniData, String language,
- String country, String variant);
-
- private native final int native_setLanguage(int jniData, String language, String country,
- String variant);
-
- private native final int native_loadLanguage(int jniData, String language, String country,
- String variant);
-
- private native final int native_setConfig(int jniData, String engineConfig);
-
- private native final int native_setSpeechRate(int jniData, int speechRate);
-
- private native final int native_setPitch(int jniData, int speechRate);
-
- private native final String[] native_getLanguage(int jniData);
-
- private native final int native_getRate(int jniData);
-
- private native final void native_shutdown(int jniData);
-
-
- /**
- * Callback from the C layer
- */
- @SuppressWarnings("unused")
- private static void postNativeSpeechSynthesizedInJava(Object tts_ref,
- int bufferPointer, int bufferSize) {
-
- Log.i("TTS plugin debug", "bufferPointer: " + bufferPointer
- + " bufferSize: " + bufferSize);
-
- SynthProxy nativeTTS = (SynthProxy)((WeakReference)tts_ref).get();
- // TODO notify TTS service of synthesis/playback completion,
- // method definition to be changed.
- }
-}
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
deleted file mode 100755
index c562327..0000000
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ /dev/null
@@ -1,1503 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package android.tts;
-
-import android.app.Service;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.database.Cursor;
-import android.media.AudioManager;
-import android.media.MediaPlayer;
-import android.media.MediaPlayer.OnCompletionListener;
-import android.net.Uri;
-import android.os.IBinder;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.preference.PreferenceManager;
-import android.speech.tts.ITts.Stub;
-import android.speech.tts.ITtsCallback;
-import android.speech.tts.TextToSpeech;
-import android.util.Log;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.TimeUnit;
-
-
-/**
- * @hide Synthesizes speech from text. This is implemented as a service so that
- * other applications can call the TTS without needing to bundle the TTS
- * in the build.
- *
- */
-public class TtsService extends Service implements OnCompletionListener {
-
- private static class SpeechItem {
- public static final int TEXT = 0;
- public static final int EARCON = 1;
- public static final int SILENCE = 2;
- public static final int TEXT_TO_FILE = 3;
- public String mText = "";
- public ArrayList<String> mParams = null;
- public int mType = TEXT;
- public long mDuration = 0;
- public String mFilename = null;
- public String mCallingApp = "";
-
- public SpeechItem(String source, String text, ArrayList<String> params, int itemType) {
- mText = text;
- mParams = params;
- mType = itemType;
- mCallingApp = source;
- }
-
- public SpeechItem(String source, long silenceTime, ArrayList<String> params) {
- mDuration = silenceTime;
- mParams = params;
- mType = SILENCE;
- mCallingApp = source;
- }
-
- public SpeechItem(String source, String text, ArrayList<String> params,
- int itemType, String filename) {
- mText = text;
- mParams = params;
- mType = itemType;
- mFilename = filename;
- mCallingApp = source;
- }
-
- }
-
- /**
- * Contains the information needed to access a sound resource; the name of
- * the package that contains the resource and the resID of the resource
- * within that package.
- */
- private static class SoundResource {
- public String mSourcePackageName = null;
- public int mResId = -1;
- public String mFilename = null;
-
- public SoundResource(String packageName, int id) {
- mSourcePackageName = packageName;
- mResId = id;
- mFilename = null;
- }
-
- public SoundResource(String file) {
- mSourcePackageName = null;
- mResId = -1;
- mFilename = file;
- }
- }
- // If the speech queue is locked for more than 5 seconds, something has gone
- // very wrong with processSpeechQueue.
- private static final int SPEECHQUEUELOCK_TIMEOUT = 5000;
- private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000;
- private static final int MAX_FILENAME_LENGTH = 250;
- private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_MUSIC;
- // TODO use TextToSpeech.DEFAULT_SYNTH once it is unhidden
- private static final String DEFAULT_SYNTH = "com.svox.pico";
- private static final String ACTION = "android.intent.action.START_TTS_SERVICE";
- private static final String CATEGORY = "android.intent.category.TTS";
- private static final String PKGNAME = "android.tts";
- protected static final String SERVICE_TAG = "TtsService";
-
- private final RemoteCallbackList<ITtsCallback> mCallbacks
- = new RemoteCallbackList<ITtsCallback>();
-
- private HashMap<String, ITtsCallback> mCallbacksMap;
-
- private Boolean mIsSpeaking;
- private Boolean mSynthBusy;
- private ArrayList<SpeechItem> mSpeechQueue;
- private HashMap<String, SoundResource> mEarcons;
- private HashMap<String, SoundResource> mUtterances;
- private MediaPlayer mPlayer;
- private SpeechItem mCurrentSpeechItem;
- private HashMap<SpeechItem, Boolean> mKillList; // Used to ensure that in-flight synth calls
- // are killed when stop is used.
- private TtsService mSelf;
-
- private ContentResolver mResolver;
-
- // lock for the speech queue (mSpeechQueue) and the current speech item (mCurrentSpeechItem)
- private final ReentrantLock speechQueueLock = new ReentrantLock();
- private final ReentrantLock synthesizerLock = new ReentrantLock();
-
- private static SynthProxy sNativeSynth = null;
- private String currentSpeechEngineSOFile = "";
-
- @Override
- public void onCreate() {
- super.onCreate();
- Log.v("TtsService", "TtsService.onCreate()");
-
- mResolver = getContentResolver();
-
- currentSpeechEngineSOFile = "";
- setEngine(getDefaultEngine());
-
- mSelf = this;
- mIsSpeaking = false;
- mSynthBusy = false;
-
- mEarcons = new HashMap<String, SoundResource>();
- mUtterances = new HashMap<String, SoundResource>();
- mCallbacksMap = new HashMap<String, android.speech.tts.ITtsCallback>();
-
- mSpeechQueue = new ArrayList<SpeechItem>();
- mPlayer = null;
- mCurrentSpeechItem = null;
- mKillList = new HashMap<SpeechItem, Boolean>();
-
- setDefaultSettings();
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
-
- killAllUtterances();
-
- // Don't hog the media player
- cleanUpPlayer();
-
- if (sNativeSynth != null) {
- sNativeSynth.shutdown();
- }
- sNativeSynth = null;
-
- // Unregister all callbacks.
- mCallbacks.kill();
-
- Log.v(SERVICE_TAG, "onDestroy() completed");
- }
-
-
- private int setEngine(String enginePackageName) {
- String soFilename = "";
- if (isDefaultEnforced()) {
- enginePackageName = getDefaultEngine();
- }
-
- // Make sure that the engine has been allowed by the user
- if (!enginePackageName.equals(DEFAULT_SYNTH)) {
- String[] enabledEngines = android.provider.Settings.Secure.getString(mResolver,
- android.provider.Settings.Secure.TTS_ENABLED_PLUGINS).split(" ");
- boolean isEnabled = false;
- for (int i=0; i<enabledEngines.length; i++) {
- if (enabledEngines[i].equals(enginePackageName)) {
- isEnabled = true;
- break;
- }
- }
- if (!isEnabled) {
- // Do not use an engine that the user has not enabled; fall back
- // to using the default synthesizer.
- enginePackageName = DEFAULT_SYNTH;
- }
- }
-
- // The SVOX TTS is an exception to how the TTS packaging scheme works
- // because it is part of the system and not a 3rd party add-on; thus
- // its binary is actually located under /system/lib/
- if (enginePackageName.equals(DEFAULT_SYNTH)) {
- soFilename = "/system/lib/libttspico.so";
- } else {
- // Find the package
- Intent intent = new Intent("android.intent.action.START_TTS_ENGINE");
- intent.setPackage(enginePackageName);
- ResolveInfo[] enginesArray = new ResolveInfo[0];
- PackageManager pm = getPackageManager();
- List <ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
- if ((resolveInfos == null) || resolveInfos.isEmpty()) {
- Log.e(SERVICE_TAG, "Invalid TTS Engine Package: " + enginePackageName);
- return TextToSpeech.ERROR;
- }
- enginesArray = resolveInfos.toArray(enginesArray);
- // Generate the TTS .so filename from the package
- ActivityInfo aInfo = enginesArray[0].activityInfo;
- soFilename = aInfo.name.replace(aInfo.packageName + ".", "") + ".so";
- soFilename = soFilename.toLowerCase();
- soFilename = "/data/data/" + aInfo.packageName + "/lib/libtts" + soFilename;
- }
-
- if (currentSpeechEngineSOFile.equals(soFilename)) {
- return TextToSpeech.SUCCESS;
- }
-
- File f = new File(soFilename);
- if (!f.exists()) {
- Log.e(SERVICE_TAG, "Invalid TTS Binary: " + soFilename);
- return TextToSpeech.ERROR;
- }
-
- if (sNativeSynth != null) {
- sNativeSynth.stopSync();
- sNativeSynth.shutdown();
- sNativeSynth = null;
- }
-
- // Load the engineConfig from the plugin if it has any special configuration
- // to be loaded. By convention, if an engine wants the TTS framework to pass
- // in any configuration, it must put it into its content provider which has the URI:
- // content://<packageName>.providers.SettingsProvider
- // That content provider must provide a Cursor which returns the String that
- // is to be passed back to the native .so file for the plugin when getString(0) is
- // called on it.
- // Note that the TTS framework does not care what this String data is: it is something
- // that comes from the engine plugin and is consumed only by the engine plugin itself.
- String engineConfig = "";
- Cursor c = getContentResolver().query(Uri.parse("content://" + enginePackageName
- + ".providers.SettingsProvider"), null, null, null, null);
- if (c != null){
- c.moveToFirst();
- engineConfig = c.getString(0);
- c.close();
- }
- sNativeSynth = new SynthProxy(soFilename, engineConfig);
- currentSpeechEngineSOFile = soFilename;
- return TextToSpeech.SUCCESS;
- }
-
-
-
- private void setDefaultSettings() {
- setLanguage("", this.getDefaultLanguage(), getDefaultCountry(), getDefaultLocVariant());
-
- // speech rate
- setSpeechRate("", getDefaultRate());
- }
-
-
- private boolean isDefaultEnforced() {
- return (android.provider.Settings.Secure.getInt(mResolver,
- android.provider.Settings.Secure.TTS_USE_DEFAULTS,
- TextToSpeech.Engine.USE_DEFAULTS)
- == 1 );
- }
-
- private String getDefaultEngine() {
- String defaultEngine = android.provider.Settings.Secure.getString(mResolver,
- android.provider.Settings.Secure.TTS_DEFAULT_SYNTH);
- if (defaultEngine == null) {
- return TextToSpeech.Engine.DEFAULT_SYNTH;
- } else {
- return defaultEngine;
- }
- }
-
- private int getDefaultRate() {
- return android.provider.Settings.Secure.getInt(mResolver,
- android.provider.Settings.Secure.TTS_DEFAULT_RATE,
- TextToSpeech.Engine.DEFAULT_RATE);
- }
-
- private int getDefaultPitch() {
- // Pitch is not user settable; the default pitch is always 100.
- return 100;
- }
-
- private String getDefaultLanguage() {
- String defaultLang = android.provider.Settings.Secure.getString(mResolver,
- android.provider.Settings.Secure.TTS_DEFAULT_LANG);
- if (defaultLang == null) {
- // no setting found, use the current Locale to determine the default language
- return Locale.getDefault().getISO3Language();
- } else {
- return defaultLang;
- }
- }
-
-
- private String getDefaultCountry() {
- String defaultCountry = android.provider.Settings.Secure.getString(mResolver,
- android.provider.Settings.Secure.TTS_DEFAULT_COUNTRY);
- if (defaultCountry == null) {
- // no setting found, use the current Locale to determine the default country
- return Locale.getDefault().getISO3Country();
- } else {
- return defaultCountry;
- }
- }
-
-
- private String getDefaultLocVariant() {
- String defaultVar = android.provider.Settings.Secure.getString(mResolver,
- android.provider.Settings.Secure.TTS_DEFAULT_VARIANT);
- if (defaultVar == null) {
- // no setting found, use the current Locale to determine the default variant
- return Locale.getDefault().getVariant();
- } else {
- return defaultVar;
- }
- }
-
-
- private int setSpeechRate(String callingApp, int rate) {
- int res = TextToSpeech.ERROR;
- try {
- if (isDefaultEnforced()) {
- res = sNativeSynth.setSpeechRate(getDefaultRate());
- } else {
- res = sNativeSynth.setSpeechRate(rate);
- }
- } catch (NullPointerException e) {
- // synth will become null during onDestroy()
- res = TextToSpeech.ERROR;
- }
- return res;
- }
-
-
- private int setPitch(String callingApp, int pitch) {
- int res = TextToSpeech.ERROR;
- try {
- res = sNativeSynth.setPitch(pitch);
- } catch (NullPointerException e) {
- // synth will become null during onDestroy()
- res = TextToSpeech.ERROR;
- }
- return res;
- }
-
-
- private int isLanguageAvailable(String lang, String country, String variant) {
- int res = TextToSpeech.LANG_NOT_SUPPORTED;
- try {
- res = sNativeSynth.isLanguageAvailable(lang, country, variant);
- } catch (NullPointerException e) {
- // synth will become null during onDestroy()
- res = TextToSpeech.LANG_NOT_SUPPORTED;
- }
- return res;
- }
-
-
- private String[] getLanguage() {
- try {
- return sNativeSynth.getLanguage();
- } catch (Exception e) {
- return null;
- }
- }
-
-
- private int setLanguage(String callingApp, String lang, String country, String variant) {
- Log.v(SERVICE_TAG, "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")");
- int res = TextToSpeech.ERROR;
- try {
- if (isDefaultEnforced()) {
- res = sNativeSynth.setLanguage(getDefaultLanguage(), getDefaultCountry(),
- getDefaultLocVariant());
- } else {
- res = sNativeSynth.setLanguage(lang, country, variant);
- }
- } catch (NullPointerException e) {
- // synth will become null during onDestroy()
- res = TextToSpeech.ERROR;
- }
- return res;
- }
-
-
- /**
- * Adds a sound resource to the TTS.
- *
- * @param text
- * The text that should be associated with the sound resource
- * @param packageName
- * The name of the package which has the sound resource
- * @param resId
- * The resource ID of the sound within its package
- */
- private void addSpeech(String callingApp, String text, String packageName, int resId) {
- mUtterances.put(text, new SoundResource(packageName, resId));
- }
-
- /**
- * Adds a sound resource to the TTS.
- *
- * @param text
- * The text that should be associated with the sound resource
- * @param filename
- * The filename of the sound resource. This must be a complete
- * path like: (/sdcard/mysounds/mysoundbite.mp3).
- */
- private void addSpeech(String callingApp, String text, String filename) {
- mUtterances.put(text, new SoundResource(filename));
- }
-
- /**
- * Adds a sound resource to the TTS as an earcon.
- *
- * @param earcon
- * The text that should be associated with the sound resource
- * @param packageName
- * The name of the package which has the sound resource
- * @param resId
- * The resource ID of the sound within its package
- */
- private void addEarcon(String callingApp, String earcon, String packageName, int resId) {
- mEarcons.put(earcon, new SoundResource(packageName, resId));
- }
-
- /**
- * Adds a sound resource to the TTS as an earcon.
- *
- * @param earcon
- * The text that should be associated with the sound resource
- * @param filename
- * The filename of the sound resource. This must be a complete
- * path like: (/sdcard/mysounds/mysoundbite.mp3).
- */
- private void addEarcon(String callingApp, String earcon, String filename) {
- mEarcons.put(earcon, new SoundResource(filename));
- }
-
- /**
- * Speaks the given text using the specified queueing mode and parameters.
- *
- * @param text
- * The text that should be spoken
- * @param queueMode
- * TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances),
- * TextToSpeech.TTS_QUEUE_ADD for queued
- * @param params
- * An ArrayList of parameters. This is not implemented for all
- * engines.
- */
- private int speak(String callingApp, String text, int queueMode, ArrayList<String> params) {
- // Log.v(SERVICE_TAG, "TTS service received " + text);
- if (queueMode == TextToSpeech.QUEUE_FLUSH) {
- stop(callingApp);
- } else if (queueMode == 2) {
- stopAll(callingApp);
- }
- mSpeechQueue.add(new SpeechItem(callingApp, text, params, SpeechItem.TEXT));
- if (!mIsSpeaking) {
- processSpeechQueue();
- }
- return TextToSpeech.SUCCESS;
- }
-
- /**
- * Plays the earcon using the specified queueing mode and parameters.
- *
- * @param earcon
- * The earcon that should be played
- * @param queueMode
- * TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances),
- * TextToSpeech.TTS_QUEUE_ADD for queued
- * @param params
- * An ArrayList of parameters. This is not implemented for all
- * engines.
- */
- private int playEarcon(String callingApp, String earcon, int queueMode,
- ArrayList<String> params) {
- if (queueMode == TextToSpeech.QUEUE_FLUSH) {
- stop(callingApp);
- } else if (queueMode == 2) {
- stopAll(callingApp);
- }
- mSpeechQueue.add(new SpeechItem(callingApp, earcon, params, SpeechItem.EARCON));
- if (!mIsSpeaking) {
- processSpeechQueue();
- }
- return TextToSpeech.SUCCESS;
- }
-
- /**
- * Stops all speech output and removes any utterances still in the queue for the calling app.
- */
- private int stop(String callingApp) {
- int result = TextToSpeech.ERROR;
- boolean speechQueueAvailable = false;
- try{
- speechQueueAvailable =
- speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT, TimeUnit.MILLISECONDS);
- if (speechQueueAvailable) {
- Log.i(SERVICE_TAG, "Stopping");
- for (int i = mSpeechQueue.size() - 1; i > -1; i--){
- if (mSpeechQueue.get(i).mCallingApp.equals(callingApp)){
- mSpeechQueue.remove(i);
- }
- }
- if ((mCurrentSpeechItem != null) &&
- mCurrentSpeechItem.mCallingApp.equals(callingApp)) {
- try {
- result = sNativeSynth.stop();
- } catch (NullPointerException e1) {
- // synth will become null during onDestroy()
- result = TextToSpeech.ERROR;
- }
- mKillList.put(mCurrentSpeechItem, true);
- if (mPlayer != null) {
- try {
- mPlayer.stop();
- } catch (IllegalStateException e) {
- // Do nothing, the player is already stopped.
- }
- }
- mIsSpeaking = false;
- mCurrentSpeechItem = null;
- } else {
- result = TextToSpeech.SUCCESS;
- }
- Log.i(SERVICE_TAG, "Stopped");
- } else {
- Log.e(SERVICE_TAG, "TTS stop(): queue locked longer than expected");
- result = TextToSpeech.ERROR;
- }
- } catch (InterruptedException e) {
- Log.e(SERVICE_TAG, "TTS stop: tryLock interrupted");
- e.printStackTrace();
- } finally {
- // This check is needed because finally will always run; even if the
- // method returns somewhere in the try block.
- if (speechQueueAvailable) {
- speechQueueLock.unlock();
- }
- return result;
- }
- }
-
-
- /**
- * Stops all speech output, both rendered to a file and directly spoken, and removes any
- * utterances still in the queue globally. Files that were being written are deleted.
- */
- @SuppressWarnings("finally")
- private int killAllUtterances() {
- int result = TextToSpeech.ERROR;
- boolean speechQueueAvailable = false;
-
- try {
- speechQueueAvailable = speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT,
- TimeUnit.MILLISECONDS);
- if (speechQueueAvailable) {
- // remove every single entry in the speech queue
- mSpeechQueue.clear();
-
- // clear the current speech item
- if (mCurrentSpeechItem != null) {
- result = sNativeSynth.stopSync();
- mKillList.put(mCurrentSpeechItem, true);
- mIsSpeaking = false;
-
- // was the engine writing to a file?
- if (mCurrentSpeechItem.mType == SpeechItem.TEXT_TO_FILE) {
- // delete the file that was being written
- if (mCurrentSpeechItem.mFilename != null) {
- File tempFile = new File(mCurrentSpeechItem.mFilename);
- Log.v(SERVICE_TAG, "Leaving behind " + mCurrentSpeechItem.mFilename);
- if (tempFile.exists()) {
- Log.v(SERVICE_TAG, "About to delete "
- + mCurrentSpeechItem.mFilename);
- if (tempFile.delete()) {
- Log.v(SERVICE_TAG, "file successfully deleted");
- }
- }
- }
- }
-
- mCurrentSpeechItem = null;
- }
- } else {
- Log.e(SERVICE_TAG, "TTS killAllUtterances(): queue locked longer than expected");
- result = TextToSpeech.ERROR;
- }
- } catch (InterruptedException e) {
- Log.e(SERVICE_TAG, "TTS killAllUtterances(): tryLock interrupted");
- result = TextToSpeech.ERROR;
- } finally {
- // This check is needed because finally will always run, even if the
- // method returns somewhere in the try block.
- if (speechQueueAvailable) {
- speechQueueLock.unlock();
- }
- return result;
- }
- }
-
-
- /**
- * Stops all speech output and removes any utterances still in the queue globally, except
- * those intended to be synthesized to file.
- */
- private int stopAll(String callingApp) {
- int result = TextToSpeech.ERROR;
- boolean speechQueueAvailable = false;
- try{
- speechQueueAvailable =
- speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT, TimeUnit.MILLISECONDS);
- if (speechQueueAvailable) {
- for (int i = mSpeechQueue.size() - 1; i > -1; i--){
- if (mSpeechQueue.get(i).mType != SpeechItem.TEXT_TO_FILE){
- mSpeechQueue.remove(i);
- }
- }
- if ((mCurrentSpeechItem != null) &&
- ((mCurrentSpeechItem.mType != SpeechItem.TEXT_TO_FILE) ||
- mCurrentSpeechItem.mCallingApp.equals(callingApp))) {
- try {
- result = sNativeSynth.stop();
- } catch (NullPointerException e1) {
- // synth will become null during onDestroy()
- result = TextToSpeech.ERROR;
- }
- mKillList.put(mCurrentSpeechItem, true);
- if (mPlayer != null) {
- try {
- mPlayer.stop();
- } catch (IllegalStateException e) {
- // Do nothing, the player is already stopped.
- }
- }
- mIsSpeaking = false;
- mCurrentSpeechItem = null;
- } else {
- result = TextToSpeech.SUCCESS;
- }
- Log.i(SERVICE_TAG, "Stopped all");
- } else {
- Log.e(SERVICE_TAG, "TTS stopAll(): queue locked longer than expected");
- result = TextToSpeech.ERROR;
- }
- } catch (InterruptedException e) {
- Log.e(SERVICE_TAG, "TTS stopAll: tryLock interrupted");
- e.printStackTrace();
- } finally {
- // This check is needed because finally will always run; even if the
- // method returns somewhere in the try block.
- if (speechQueueAvailable) {
- speechQueueLock.unlock();
- }
- return result;
- }
- }
-
- public void onCompletion(MediaPlayer arg0) {
- // mCurrentSpeechItem may become null if it is stopped at the same
- // time it completes.
- SpeechItem currentSpeechItemCopy = mCurrentSpeechItem;
- if (currentSpeechItemCopy != null) {
- String callingApp = currentSpeechItemCopy.mCallingApp;
- ArrayList<String> params = currentSpeechItemCopy.mParams;
- String utteranceId = "";
- if (params != null) {
- for (int i = 0; i < params.size() - 1; i = i + 2) {
- String param = params.get(i);
- if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)) {
- utteranceId = params.get(i + 1);
- }
- }
- }
- if (utteranceId.length() > 0) {
- dispatchUtteranceCompletedCallback(utteranceId, callingApp);
- }
- }
- processSpeechQueue();
- }
-
- private int playSilence(String callingApp, long duration, int queueMode,
- ArrayList<String> params) {
- if (queueMode == TextToSpeech.QUEUE_FLUSH) {
- stop(callingApp);
- }
- mSpeechQueue.add(new SpeechItem(callingApp, duration, params));
- if (!mIsSpeaking) {
- processSpeechQueue();
- }
- return TextToSpeech.SUCCESS;
- }
-
- private void silence(final SpeechItem speechItem) {
- class SilenceThread implements Runnable {
- public void run() {
- String utteranceId = "";
- if (speechItem.mParams != null){
- for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){
- String param = speechItem.mParams.get(i);
- if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)){
- utteranceId = speechItem.mParams.get(i+1);
- }
- }
- }
- try {
- Thread.sleep(speechItem.mDuration);
- } catch (InterruptedException e) {
- e.printStackTrace();
- } finally {
- if (utteranceId.length() > 0){
- dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
- }
- processSpeechQueue();
- }
- }
- }
- Thread slnc = (new Thread(new SilenceThread()));
- slnc.setPriority(Thread.MIN_PRIORITY);
- slnc.start();
- }
-
- private void speakInternalOnly(final SpeechItem speechItem) {
- class SynthThread implements Runnable {
- public void run() {
- boolean synthAvailable = false;
- String utteranceId = "";
- try {
- synthAvailable = synthesizerLock.tryLock();
- if (!synthAvailable) {
- mSynthBusy = true;
- Thread.sleep(100);
- Thread synth = (new Thread(new SynthThread()));
- synth.start();
- mSynthBusy = false;
- return;
- }
- int streamType = DEFAULT_STREAM_TYPE;
- String language = "";
- String country = "";
- String variant = "";
- String speechRate = "";
- String engine = "";
- String pitch = "";
- float volume = TextToSpeech.Engine.DEFAULT_VOLUME;
- float pan = TextToSpeech.Engine.DEFAULT_PAN;
- if (speechItem.mParams != null){
- for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){
- String param = speechItem.mParams.get(i);
- if (param != null) {
- if (param.equals(TextToSpeech.Engine.KEY_PARAM_RATE)) {
- speechRate = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_LANGUAGE)){
- language = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_COUNTRY)){
- country = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_VARIANT)){
- variant = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)){
- utteranceId = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_STREAM)) {
- try {
- streamType
- = Integer.parseInt(speechItem.mParams.get(i + 1));
- } catch (NumberFormatException e) {
- streamType = DEFAULT_STREAM_TYPE;
- }
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_ENGINE)) {
- engine = speechItem.mParams.get(i + 1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_PITCH)) {
- pitch = speechItem.mParams.get(i + 1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_VOLUME)) {
- try {
- volume = Float.parseFloat(speechItem.mParams.get(i + 1));
- } catch (NumberFormatException e) {
- volume = TextToSpeech.Engine.DEFAULT_VOLUME;
- }
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_PAN)) {
- try {
- pan = Float.parseFloat(speechItem.mParams.get(i + 1));
- } catch (NumberFormatException e) {
- pan = TextToSpeech.Engine.DEFAULT_PAN;
- }
- }
- }
- }
- }
- // Only do the synthesis if it has not been killed by a subsequent utterance.
- if (mKillList.get(speechItem) == null) {
- if (engine.length() > 0) {
- setEngine(engine);
- } else {
- setEngine(getDefaultEngine());
- }
- if (language.length() > 0){
- setLanguage("", language, country, variant);
- } else {
- setLanguage("", getDefaultLanguage(), getDefaultCountry(),
- getDefaultLocVariant());
- }
- if (speechRate.length() > 0){
- setSpeechRate("", Integer.parseInt(speechRate));
- } else {
- setSpeechRate("", getDefaultRate());
- }
- if (pitch.length() > 0){
- setPitch("", Integer.parseInt(pitch));
- } else {
- setPitch("", getDefaultPitch());
- }
- try {
- sNativeSynth.speak(speechItem.mText, streamType, volume, pan);
- } catch (NullPointerException e) {
- // synth will become null during onDestroy()
- Log.v(SERVICE_TAG, " null synth, can't speak");
- }
- }
- } catch (InterruptedException e) {
- Log.e(SERVICE_TAG, "TTS speakInternalOnly(): tryLock interrupted");
- e.printStackTrace();
- } finally {
- // This check is needed because finally will always run;
- // even if the
- // method returns somewhere in the try block.
- if (utteranceId.length() > 0){
- dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
- }
- if (synthAvailable) {
- synthesizerLock.unlock();
- processSpeechQueue();
- }
- }
- }
- }
- Thread synth = (new Thread(new SynthThread()));
- synth.setPriority(Thread.MAX_PRIORITY);
- synth.start();
- }
-
- private void synthToFileInternalOnly(final SpeechItem speechItem) {
- class SynthThread implements Runnable {
- public void run() {
- boolean synthAvailable = false;
- String utteranceId = "";
- Log.i(SERVICE_TAG, "Synthesizing to " + speechItem.mFilename);
- try {
- synthAvailable = synthesizerLock.tryLock();
- if (!synthAvailable) {
- synchronized (this) {
- mSynthBusy = true;
- }
- Thread.sleep(100);
- Thread synth = (new Thread(new SynthThread()));
- synth.start();
- synchronized (this) {
- mSynthBusy = false;
- }
- return;
- }
- String language = "";
- String country = "";
- String variant = "";
- String speechRate = "";
- String engine = "";
- String pitch = "";
- if (speechItem.mParams != null){
- for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){
- String param = speechItem.mParams.get(i);
- if (param != null) {
- if (param.equals(TextToSpeech.Engine.KEY_PARAM_RATE)) {
- speechRate = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_LANGUAGE)){
- language = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_COUNTRY)){
- country = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_VARIANT)){
- variant = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)){
- utteranceId = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_ENGINE)) {
- engine = speechItem.mParams.get(i + 1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_PITCH)) {
- pitch = speechItem.mParams.get(i + 1);
- }
- }
- }
- }
- // Only do the synthesis if it has not been killed by a subsequent utterance.
- if (mKillList.get(speechItem) == null){
- if (engine.length() > 0) {
- setEngine(engine);
- } else {
- setEngine(getDefaultEngine());
- }
- if (language.length() > 0){
- setLanguage("", language, country, variant);
- } else {
- setLanguage("", getDefaultLanguage(), getDefaultCountry(),
- getDefaultLocVariant());
- }
- if (speechRate.length() > 0){
- setSpeechRate("", Integer.parseInt(speechRate));
- } else {
- setSpeechRate("", getDefaultRate());
- }
- if (pitch.length() > 0){
- setPitch("", Integer.parseInt(pitch));
- } else {
- setPitch("", getDefaultPitch());
- }
- try {
- sNativeSynth.synthesizeToFile(speechItem.mText, speechItem.mFilename);
- } catch (NullPointerException e) {
- // synth will become null during onDestroy()
- Log.v(SERVICE_TAG, " null synth, can't synthesize to file");
- }
- }
- } catch (InterruptedException e) {
- Log.e(SERVICE_TAG, "TTS synthToFileInternalOnly(): tryLock interrupted");
- e.printStackTrace();
- } finally {
- // This check is needed because finally will always run;
- // even if the
- // method returns somewhere in the try block.
- if (utteranceId.length() > 0){
- dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
- }
- if (synthAvailable) {
- synthesizerLock.unlock();
- processSpeechQueue();
- }
- }
- }
- }
- Thread synth = (new Thread(new SynthThread()));
- synth.setPriority(Thread.MAX_PRIORITY);
- synth.start();
- }
-
- private SoundResource getSoundResource(SpeechItem speechItem) {
- SoundResource sr = null;
- String text = speechItem.mText;
- if (speechItem.mType == SpeechItem.SILENCE) {
- // Do nothing if this is just silence
- } else if (speechItem.mType == SpeechItem.EARCON) {
- sr = mEarcons.get(text);
- } else {
- sr = mUtterances.get(text);
- }
- return sr;
- }
-
- private void broadcastTtsQueueProcessingCompleted(){
- Intent i = new Intent(TextToSpeech.ACTION_TTS_QUEUE_PROCESSING_COMPLETED);
- sendBroadcast(i);
- }
-
-
- private void dispatchUtteranceCompletedCallback(String utteranceId, String packageName) {
- ITtsCallback cb = mCallbacksMap.get(packageName);
- if (cb == null){
- return;
- }
- Log.v(SERVICE_TAG, "TTS callback: dispatch started");
- // Broadcast to all clients the new value.
- final int N = mCallbacks.beginBroadcast();
- try {
- cb.utteranceCompleted(utteranceId);
- } catch (RemoteException e) {
- // The RemoteCallbackList will take care of removing
- // the dead object for us.
- }
- mCallbacks.finishBroadcast();
- Log.v(SERVICE_TAG, "TTS callback: dispatch completed to " + N);
- }
-
- private SpeechItem splitCurrentTextIfNeeded(SpeechItem currentSpeechItem){
- if (currentSpeechItem.mText.length() < MAX_SPEECH_ITEM_CHAR_LENGTH){
- return currentSpeechItem;
- } else {
- String callingApp = currentSpeechItem.mCallingApp;
- ArrayList<SpeechItem> splitItems = new ArrayList<SpeechItem>();
- int start = 0;
- int end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1;
- String splitText;
- SpeechItem splitItem;
- while (end < currentSpeechItem.mText.length()){
- splitText = currentSpeechItem.mText.substring(start, end);
- splitItem = new SpeechItem(callingApp, splitText, null, SpeechItem.TEXT);
- splitItems.add(splitItem);
- start = end;
- end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1;
- }
- splitText = currentSpeechItem.mText.substring(start);
- splitItem = new SpeechItem(callingApp, splitText, null, SpeechItem.TEXT);
- splitItems.add(splitItem);
- mSpeechQueue.remove(0);
- for (int i = splitItems.size() - 1; i >= 0; i--){
- mSpeechQueue.add(0, splitItems.get(i));
- }
- return mSpeechQueue.get(0);
- }
- }
-
- private void processSpeechQueue() {
- boolean speechQueueAvailable = false;
- synchronized (this) {
- if (mSynthBusy){
- // There is already a synth thread waiting to run.
- return;
- }
- }
- try {
- speechQueueAvailable =
- speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT, TimeUnit.MILLISECONDS);
- if (!speechQueueAvailable) {
- Log.e(SERVICE_TAG, "processSpeechQueue - Speech queue is unavailable.");
- return;
- }
- if (mSpeechQueue.size() < 1) {
- mIsSpeaking = false;
- mKillList.clear();
- broadcastTtsQueueProcessingCompleted();
- return;
- }
-
- mCurrentSpeechItem = mSpeechQueue.get(0);
- mIsSpeaking = true;
- SoundResource sr = getSoundResource(mCurrentSpeechItem);
- // Synth speech as needed - synthesizer should call
- // processSpeechQueue to continue running the queue
- // Log.v(SERVICE_TAG, "TTS processing: " + mCurrentSpeechItem.mText);
- if (sr == null) {
- if (mCurrentSpeechItem.mType == SpeechItem.TEXT) {
- mCurrentSpeechItem = splitCurrentTextIfNeeded(mCurrentSpeechItem);
- speakInternalOnly(mCurrentSpeechItem);
- } else if (mCurrentSpeechItem.mType == SpeechItem.TEXT_TO_FILE) {
- synthToFileInternalOnly(mCurrentSpeechItem);
- } else {
- // This is either silence or an earcon that was missing
- silence(mCurrentSpeechItem);
- }
- } else {
- cleanUpPlayer();
- if (sr.mSourcePackageName == PKGNAME) {
- // Utterance is part of the TTS library
- mPlayer = MediaPlayer.create(this, sr.mResId);
- } else if (sr.mSourcePackageName != null) {
- // Utterance is part of the app calling the library
- Context ctx;
- try {
- ctx = this.createPackageContext(sr.mSourcePackageName, 0);
- } catch (NameNotFoundException e) {
- e.printStackTrace();
- mSpeechQueue.remove(0); // Remove it from the queue and
- // move on
- mIsSpeaking = false;
- return;
- }
- mPlayer = MediaPlayer.create(ctx, sr.mResId);
- } else {
- // Utterance is coming from a file
- mPlayer = MediaPlayer.create(this, Uri.parse(sr.mFilename));
- }
-
- // Check if Media Server is dead; if it is, clear the queue and
- // give up for now - hopefully, it will recover itself.
- if (mPlayer == null) {
- mSpeechQueue.clear();
- mIsSpeaking = false;
- return;
- }
- mPlayer.setOnCompletionListener(this);
- try {
- mPlayer.setAudioStreamType(getStreamTypeFromParams(mCurrentSpeechItem.mParams));
- mPlayer.start();
- } catch (IllegalStateException e) {
- mSpeechQueue.clear();
- mIsSpeaking = false;
- cleanUpPlayer();
- return;
- }
- }
- if (mSpeechQueue.size() > 0) {
- mSpeechQueue.remove(0);
- }
- } catch (InterruptedException e) {
- Log.e(SERVICE_TAG, "TTS processSpeechQueue: tryLock interrupted");
- e.printStackTrace();
- } finally {
- // This check is needed because finally will always run; even if the
- // method returns somewhere in the try block.
- if (speechQueueAvailable) {
- speechQueueLock.unlock();
- }
- }
- }
-
- private int getStreamTypeFromParams(ArrayList<String> paramList) {
- int streamType = DEFAULT_STREAM_TYPE;
- if (paramList == null) {
- return streamType;
- }
- for (int i = 0; i < paramList.size() - 1; i = i + 2) {
- String param = paramList.get(i);
- if ((param != null) && (param.equals(TextToSpeech.Engine.KEY_PARAM_STREAM))) {
- try {
- streamType = Integer.parseInt(paramList.get(i + 1));
- } catch (NumberFormatException e) {
- streamType = DEFAULT_STREAM_TYPE;
- }
- }
- }
- return streamType;
- }
-
- private void cleanUpPlayer() {
- if (mPlayer != null) {
- mPlayer.release();
- mPlayer = null;
- }
- }
-
- /**
- * Synthesizes the given text to a file using the specified parameters.
- *
- * @param text
- * The String of text that should be synthesized
- * @param params
- * An ArrayList of parameters. The first element of this array
- * controls the type of voice to use.
- * @param filename
- * The string that gives the full output filename; it should be
- * something like "/sdcard/myappsounds/mysound.wav".
- * @return A boolean that indicates if the synthesis can be started
- */
- private boolean synthesizeToFile(String callingApp, String text, ArrayList<String> params,
- String filename) {
- // Don't allow a filename that is too long
- if (filename.length() > MAX_FILENAME_LENGTH) {
- return false;
- }
- // Don't allow anything longer than the max text length; since this
- // is synthing to a file, don't even bother splitting it.
- if (text.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH){
- return false;
- }
- // Check that the output file can be created
- try {
- File tempFile = new File(filename);
- if (tempFile.exists()) {
- Log.v("TtsService", "File " + filename + " exists, deleting.");
- tempFile.delete();
- }
- if (!tempFile.createNewFile()) {
- Log.e("TtsService", "Unable to synthesize to file: can't create " + filename);
- return false;
- }
- tempFile.delete();
- } catch (IOException e) {
- Log.e("TtsService", "Can't create " + filename + " due to exception " + e);
- return false;
- }
- mSpeechQueue.add(new SpeechItem(callingApp, text, params, SpeechItem.TEXT_TO_FILE, filename));
- if (!mIsSpeaking) {
- processSpeechQueue();
- }
- return true;
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- if (ACTION.equals(intent.getAction())) {
- for (String category : intent.getCategories()) {
- if (category.equals(CATEGORY)) {
- return mBinder;
- }
- }
- }
- return null;
- }
-
- private final android.speech.tts.ITts.Stub mBinder = new Stub() {
-
- public int registerCallback(String packageName, ITtsCallback cb) {
- if (cb != null) {
- mCallbacks.register(cb);
- mCallbacksMap.put(packageName, cb);
- return TextToSpeech.SUCCESS;
- }
- return TextToSpeech.ERROR;
- }
-
- public int unregisterCallback(String packageName, ITtsCallback cb) {
- if (cb != null) {
- mCallbacksMap.remove(packageName);
- mCallbacks.unregister(cb);
- return TextToSpeech.SUCCESS;
- }
- return TextToSpeech.ERROR;
- }
-
- /**
- * Speaks the given text using the specified queueing mode and
- * parameters.
- *
- * @param text
- * The text that should be spoken
- * @param queueMode
- * TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances)
- * TextToSpeech.TTS_QUEUE_ADD for queued
- * @param params
- * An ArrayList of parameters. The first element of this
- * array controls the type of voice to use.
- */
- public int speak(String callingApp, String text, int queueMode, String[] params) {
- ArrayList<String> speakingParams = new ArrayList<String>();
- if (params != null) {
- speakingParams = new ArrayList<String>(Arrays.asList(params));
- }
- return mSelf.speak(callingApp, text, queueMode, speakingParams);
- }
-
- /**
- * Plays the earcon using the specified queueing mode and parameters.
- *
- * @param earcon
- * The earcon that should be played
- * @param queueMode
- * TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances)
- * TextToSpeech.TTS_QUEUE_ADD for queued
- * @param params
- * An ArrayList of parameters.
- */
- public int playEarcon(String callingApp, String earcon, int queueMode, String[] params) {
- ArrayList<String> speakingParams = new ArrayList<String>();
- if (params != null) {
- speakingParams = new ArrayList<String>(Arrays.asList(params));
- }
- return mSelf.playEarcon(callingApp, earcon, queueMode, speakingParams);
- }
-
- /**
- * Plays the silence using the specified queueing mode and parameters.
- *
- * @param duration
- * The duration of the silence that should be played
- * @param queueMode
- * TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances)
- * TextToSpeech.TTS_QUEUE_ADD for queued
- * @param params
- * An ArrayList of parameters.
- */
- public int playSilence(String callingApp, long duration, int queueMode, String[] params) {
- ArrayList<String> speakingParams = new ArrayList<String>();
- if (params != null) {
- speakingParams = new ArrayList<String>(Arrays.asList(params));
- }
- return mSelf.playSilence(callingApp, duration, queueMode, speakingParams);
- }
-
- /**
- * Stops all speech output and removes any utterances still in the
- * queue.
- */
- public int stop(String callingApp) {
- return mSelf.stop(callingApp);
- }
-
- /**
- * Returns whether or not the TTS is speaking.
- *
- * @return Boolean to indicate whether or not the TTS is speaking
- */
- public boolean isSpeaking() {
- return (mSelf.mIsSpeaking && (mSpeechQueue.size() < 1));
- }
-
- /**
- * Adds a sound resource to the TTS.
- *
- * @param text
- * The text that should be associated with the sound resource
- * @param packageName
- * The name of the package which has the sound resource
- * @param resId
- * The resource ID of the sound within its package
- */
- public void addSpeech(String callingApp, String text, String packageName, int resId) {
- mSelf.addSpeech(callingApp, text, packageName, resId);
- }
-
- /**
- * Adds a sound resource to the TTS.
- *
- * @param text
- * The text that should be associated with the sound resource
- * @param filename
- * The filename of the sound resource. This must be a
- * complete path like: (/sdcard/mysounds/mysoundbite.mp3).
- */
- public void addSpeechFile(String callingApp, String text, String filename) {
- mSelf.addSpeech(callingApp, text, filename);
- }
-
- /**
- * Adds a sound resource to the TTS as an earcon.
- *
- * @param earcon
- * The text that should be associated with the sound resource
- * @param packageName
- * The name of the package which has the sound resource
- * @param resId
- * The resource ID of the sound within its package
- */
- public void addEarcon(String callingApp, String earcon, String packageName, int resId) {
- mSelf.addEarcon(callingApp, earcon, packageName, resId);
- }
-
- /**
- * Adds a sound resource to the TTS as an earcon.
- *
- * @param earcon
- * The text that should be associated with the sound resource
- * @param filename
- * The filename of the sound resource. This must be a
- * complete path like: (/sdcard/mysounds/mysoundbite.mp3).
- */
- public void addEarconFile(String callingApp, String earcon, String filename) {
- mSelf.addEarcon(callingApp, earcon, filename);
- }
-
- /**
- * Sets the speech rate for the TTS. Note that this will only have an
- * effect on synthesized speech; it will not affect pre-recorded speech.
- *
- * @param speechRate
- * The speech rate that should be used
- */
- public int setSpeechRate(String callingApp, int speechRate) {
- return mSelf.setSpeechRate(callingApp, speechRate);
- }
-
- /**
- * Sets the pitch for the TTS. Note that this will only have an
- * effect on synthesized speech; it will not affect pre-recorded speech.
- *
- * @param pitch
- * The pitch that should be used for the synthesized voice
- */
- public int setPitch(String callingApp, int pitch) {
- return mSelf.setPitch(callingApp, pitch);
- }
-
- /**
- * Returns the level of support for the specified language.
- *
- * @param lang the three letter ISO language code.
- * @param country the three letter ISO country code.
- * @param variant the variant code associated with the country and language pair.
- * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE,
- * TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE as defined in
- * android.speech.tts.TextToSpeech.
- */
- public int isLanguageAvailable(String lang, String country, String variant,
- String[] params) {
- for (int i = 0; i < params.length - 1; i = i + 2){
- String param = params[i];
- if (param != null) {
- if (param.equals(TextToSpeech.Engine.KEY_PARAM_ENGINE)) {
- mSelf.setEngine(params[i + 1]);
- break;
- }
- }
- }
- return mSelf.isLanguageAvailable(lang, country, variant);
- }
-
- /**
- * Returns the currently set language / country / variant strings representing the
- * language used by the TTS engine.
- * @return null is no language is set, or an array of 3 string containing respectively
- * the language, country and variant.
- */
- public String[] getLanguage() {
- return mSelf.getLanguage();
- }
-
- /**
- * Sets the speech rate for the TTS, which affects the synthesized voice.
- *
- * @param lang the three letter ISO language code.
- * @param country the three letter ISO country code.
- * @param variant the variant code associated with the country and language pair.
- */
- public int setLanguage(String callingApp, String lang, String country, String variant) {
- return mSelf.setLanguage(callingApp, lang, country, variant);
- }
-
- /**
- * Synthesizes the given text to a file using the specified
- * parameters.
- *
- * @param text
- * The String of text that should be synthesized
- * @param params
- * An ArrayList of parameters. The first element of this
- * array controls the type of voice to use.
- * @param filename
- * The string that gives the full output filename; it should
- * be something like "/sdcard/myappsounds/mysound.wav".
- * @return A boolean that indicates if the synthesis succeeded
- */
- public boolean synthesizeToFile(String callingApp, String text, String[] params,
- String filename) {
- ArrayList<String> speakingParams = new ArrayList<String>();
- if (params != null) {
- speakingParams = new ArrayList<String>(Arrays.asList(params));
- }
- return mSelf.synthesizeToFile(callingApp, text, speakingParams, filename);
- }
-
- /**
- * Sets the speech synthesis engine for the TTS by specifying its packagename
- *
- * @param packageName the packageName of the speech synthesis engine (ie, "com.svox.pico")
- *
- * @return SUCCESS or ERROR as defined in android.speech.tts.TextToSpeech.
- */
- public int setEngineByPackageName(String packageName) {
- return mSelf.setEngine(packageName);
- }
-
- /**
- * Returns the packagename of the default speech synthesis engine.
- *
- * @return Packagename of the TTS engine that the user has chosen as their default.
- */
- public String getDefaultEngine() {
- return mSelf.getDefaultEngine();
- }
-
- /**
- * Returns whether or not the user is forcing their defaults to override the
- * Text-To-Speech settings set by applications.
- *
- * @return Whether or not defaults are enforced.
- */
- public boolean areDefaultsEnforced() {
- return mSelf.isDefaultEnforced();
- }
-
- };
-
-}
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
index 9b0a36b..f436cb4 100644
--- a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
@@ -26,7 +26,6 @@ import android.os.RemoteException;
import android.provider.Telephony.Sms.Intents;
import android.test.ServiceTestCase;
import android.util.Log;
-import android.util.Config;
import com.android.internal.telephony.IccUtils;
import com.android.internal.telephony.IWapPushManager;
@@ -2051,7 +2050,7 @@ public class WapPushTest extends ServiceTestCase<WapPushManager> {
*/
public int dispatchWapPdu(byte[] pdu, IWapPushManager wapPushMan) {
- if (Config.DEBUG) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu));
+ if (false) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu));
int index = 0;
int transactionId = pdu[index++] & 0xFF;
@@ -2060,7 +2059,7 @@ public class WapPushTest extends ServiceTestCase<WapPushManager> {
if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) &&
(pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
- if (Config.DEBUG) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
+ if (false) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
return Intents.RESULT_SMS_HANDLED;
}
@@ -2073,7 +2072,7 @@ public class WapPushTest extends ServiceTestCase<WapPushManager> {
* So it will be encoded in no more than 5 octets.
*/
if (pduDecoder.decodeUintvarInteger(index) == false) {
- if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Length error.");
+ if (false) Log.w(LOG_TAG, "Received PDU. Header Length error.");
return Intents.RESULT_SMS_GENERIC_ERROR;
}
headerLength = (int) pduDecoder.getValue32();
@@ -2094,7 +2093,7 @@ public class WapPushTest extends ServiceTestCase<WapPushManager> {
* Length = Uintvar-integer
*/
if (pduDecoder.decodeContentType(index) == false) {
- if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Content-Type error.");
+ if (false) Log.w(LOG_TAG, "Received PDU. Header Content-Type error.");
return Intents.RESULT_SMS_GENERIC_ERROR;
}
@@ -2131,13 +2130,13 @@ public class WapPushTest extends ServiceTestCase<WapPushManager> {
String contentType = ((mimeType == null) ?
Long.toString(binaryContentType) : mimeType);
- if (Config.DEBUG) Log.v(LOG_TAG, "appid found: " + wapAppId + ":" + contentType);
+ if (false) Log.v(LOG_TAG, "appid found: " + wapAppId + ":" + contentType);
try {
boolean processFurther = true;
// IWapPushManager wapPushMan = mWapConn.getWapPushManager();
if (wapPushMan == null) {
- if (Config.DEBUG) Log.w(LOG_TAG, "wap push manager not found!");
+ if (false) Log.w(LOG_TAG, "wap push manager not found!");
} else {
Intent intent = new Intent();
intent.putExtra("transactionId", transactionId);
@@ -2148,7 +2147,7 @@ public class WapPushTest extends ServiceTestCase<WapPushManager> {
pduDecoder.getContentParameters());
int procRet = wapPushMan.processMessage(wapAppId, contentType, intent);
- if (Config.DEBUG) Log.v(LOG_TAG, "procRet:" + procRet);
+ if (false) Log.v(LOG_TAG, "procRet:" + procRet);
if ((procRet & WapPushManagerParams.MESSAGE_HANDLED) > 0
&& (procRet & WapPushManagerParams.FURTHER_PROCESSING) == 0) {
processFurther = false;
@@ -2158,10 +2157,10 @@ public class WapPushTest extends ServiceTestCase<WapPushManager> {
return Intents.RESULT_SMS_HANDLED;
}
} catch (RemoteException e) {
- if (Config.DEBUG) Log.w(LOG_TAG, "remote func failed...");
+ if (false) Log.w(LOG_TAG, "remote func failed...");
}
}
- if (Config.DEBUG) Log.v(LOG_TAG, "fall back to existing handler");
+ if (false) Log.v(LOG_TAG, "fall back to existing handler");
return Activity.RESULT_OK;
}