summaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2013-12-05 13:10:46 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2013-12-05 13:10:46 -0800
commitebcb32f58a6220802ca129ea33f47b4b69931a10 (patch)
tree32b57c1d6ba9180ae63979e06d7421e107a9aa6c /packages
parent6e2d0c1d91f644ab50e0c0b7cae4306262a4ca41 (diff)
parentbac61807d3bcfff957b358cb9ad77850bd373689 (diff)
downloadframeworks_base-ebcb32f58a6220802ca129ea33f47b4b69931a10.zip
frameworks_base-ebcb32f58a6220802ca129ea33f47b4b69931a10.tar.gz
frameworks_base-ebcb32f58a6220802ca129ea33f47b4b69931a10.tar.bz2
Merge commit 'bac61807d3bcfff957b358cb9ad77850bd373689' into HEAD
Change-Id: I29374270c8e0c2f2859efaf1d55af9f73da0f8d7
Diffstat (limited to 'packages')
-rw-r--r--packages/DocumentsUI/AndroidManifest.xml8
-rw-r--r--packages/DocumentsUI/res/layout/fragment_save.xml32
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java7
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java23
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java21
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java1
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java8
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java46
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java51
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java133
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RootsCache.java5
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java35
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java7
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java10
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java14
-rw-r--r--packages/ExternalStorageProvider/AndroidManifest.xml9
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java245
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/MountReceiver.java35
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java66
-rw-r--r--packages/Keyguard/AndroidManifest.xml1
-rw-r--r--packages/Keyguard/res/drawable-hdpi/ic_lockscreen_forgotpassword_normal.pngbin757 -> 0 bytes
-rw-r--r--packages/Keyguard/res/drawable-hdpi/ic_lockscreen_forgotpassword_pressed.pngbin1403 -> 0 bytes
-rw-r--r--packages/Keyguard/res/drawable-mdpi/ic_lockscreen_forgotpassword_normal.pngbin591 -> 0 bytes
-rw-r--r--packages/Keyguard/res/drawable-mdpi/ic_lockscreen_forgotpassword_pressed.pngbin947 -> 0 bytes
-rw-r--r--packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_forgotpassword_normal.pngbin957 -> 0 bytes
-rw-r--r--packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_forgotpassword_pressed.pngbin1907 -> 0 bytes
-rw-r--r--packages/Keyguard/res/layout-land/keyguard_host_view.xml2
-rw-r--r--packages/Keyguard/res/layout-port/keyguard_host_view.xml5
-rw-r--r--packages/Keyguard/res/layout-sw600dp-port/keyguard_host_view.xml2
-rw-r--r--packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml8
-rw-r--r--packages/Keyguard/res/layout/keyguard_presentation.xml63
-rw-r--r--packages/Keyguard/res/layout/keyguard_status_view.xml2
-rw-r--r--packages/Keyguard/res/values-sw540dp-port/dimens.xml24
-rw-r--r--packages/Keyguard/res/values-sw600dp/dimens.xml3
-rw-r--r--packages/Keyguard/res/values/dimens.xml4
-rw-r--r--packages/Keyguard/res/values/strings.xml36
-rw-r--r--packages/Keyguard/src/com/android/keyguard/CameraWidgetFrame.java16
-rw-r--r--packages/Keyguard/src/com/android/keyguard/CarrierText.java27
-rw-r--r--packages/Keyguard/src/com/android/keyguard/ChallengeLayout.java2
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardActivityLauncher.java5
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardDisplayManager.java171
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java52
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java1
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardService.java4
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java103
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java94
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java58
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java227
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java7
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java15
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java7
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java31
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java36
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java4
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java7
-rw-r--r--packages/Keyguard/src/com/android/keyguard/PagedView.java46
-rw-r--r--packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java10
-rw-r--r--packages/PrintSpooler/res/layout/print_job_config_activity_container.xml2
-rw-r--r--packages/PrintSpooler/res/layout/print_job_config_activity_content_editing.xml88
-rw-r--r--packages/PrintSpooler/res/layout/print_job_config_activity_content_error.xml31
-rw-r--r--packages/PrintSpooler/res/layout/print_job_config_activity_content_generating.xml30
-rw-r--r--packages/PrintSpooler/res/layout/printer_dropdown_item.xml16
-rw-r--r--packages/PrintSpooler/res/layout/printer_list_item.xml71
-rw-r--r--packages/PrintSpooler/res/layout/select_printer_fragment.xml26
-rw-r--r--packages/PrintSpooler/res/values-ja/arrays.xml14
-rw-r--r--packages/PrintSpooler/res/values-land/constants.xml (renamed from packages/Keyguard/res/drawable/lockscreen_forgot_password_button.xml)13
-rw-r--r--packages/PrintSpooler/res/values/colors.xml6
-rw-r--r--packages/PrintSpooler/res/values/constants.xml5
-rw-r--r--packages/PrintSpooler/res/values/strings.xml9
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java109
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java318
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java86
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java11
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java132
-rw-r--r--packages/Shell/AndroidManifest.xml2
-rw-r--r--packages/SystemUI/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/res/drawable-hdpi/bg_protect.9.pngbin0 -> 226 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.pngbin928 -> 1821 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_qs_cast_available.pngbin0 -> 1325 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connected.pngbin0 -> 1277 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_0.pngbin0 -> 1275 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_1.pngbin0 -> 1288 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_2.pngbin0 -> 1301 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_qs_ime.pngbin2001 -> 1296 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_qs_location.pngbin1762 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_qs_remote_display.pngbin1101 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_qs_remote_display_connected.pngbin1064 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_qs_settings.pngbin1728 -> 1805 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_sysbar_camera.pngbin1507 -> 1517 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/search_light.pngbin953 -> 685 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/search_light_land.pngbin0 -> 695 bytes
-rw-r--r--packages/SystemUI/res/drawable-land-hdpi/bg_protect.9.pngbin0 -> 222 bytes
-rw-r--r--packages/SystemUI/res/drawable-land-mdpi/bg_protect.9.pngbin0 -> 201 bytes
-rw-r--r--packages/SystemUI/res/drawable-land-xhdpi/bg_protect.9.pngbin0 -> 235 bytes
-rw-r--r--packages/SystemUI/res/drawable-land-xxhdpi/bg_protect.9.pngbin0 -> 1117 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/bg_protect.9.pngbin0 -> 213 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.pngbin746 -> 1251 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_qs_cast_available.pngbin0 -> 933 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connected.pngbin0 -> 942 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_0.pngbin0 -> 921 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_1.pngbin0 -> 949 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_2.pngbin0 -> 958 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_qs_ime.pngbin1271 -> 965 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_qs_location.pngbin1139 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_qs_remote_display.pngbin846 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_qs_remote_display_connected.pngbin806 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_qs_settings.pngbin1189 -> 1261 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_sysbar_camera.pngbin1066 -> 1060 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/search_light.pngbin701 -> 537 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/search_light_land.pngbin0 -> 555 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/bg_protect.9.pngbin0 -> 255 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.pngbin1131 -> 2568 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_available.pngbin0 -> 796 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connected.pngbin0 -> 742 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_0.pngbin0 -> 743 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_1.pngbin0 -> 765 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_2.pngbin0 -> 812 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_qs_ime.pngbin3101 -> 1590 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_qs_location.pngbin2360 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_qs_remote_display.pngbin1327 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_qs_remote_display_connected.pngbin1290 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_qs_settings.pngbin2370 -> 2569 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_sysbar_camera.pngbin1979 -> 1998 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/search_light.pngbin1343 -> 939 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/search_light_land.pngbin0 -> 959 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/bg_protect.9.pngbin0 -> 1126 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.pngbin1575 -> 3247 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_available.pngbin0 -> 1202 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connected.pngbin0 -> 1169 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_0.pngbin0 -> 1139 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_1.pngbin0 -> 1159 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_2.pngbin0 -> 1197 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_qs_ime.pngbin2857 -> 1474 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_qs_location.pngbin2883 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_qs_remote_display.pngbin1748 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_qs_remote_display_connected.pngbin1542 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_qs_settings.pngbin2848 -> 3220 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_camera.pngbin4806 -> 4172 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/search_light.pngbin4248 -> 3651 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/search_light_land.pngbin0 -> 2356 bytes
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_cast_connecting.xml26
-rw-r--r--packages/SystemUI/res/layout-land/status_bar_recent_panel.xml1
-rw-r--r--packages/SystemUI/res/layout/navigation_bar.xml2
-rw-r--r--packages/SystemUI/res/layout/status_bar.xml4
-rw-r--r--packages/SystemUI/res/layout/status_bar_recent_panel.xml1
-rw-r--r--packages/SystemUI/res/values/strings.xml8
-rwxr-xr-xpackages/SystemUI/src/com/android/systemui/BatteryMeterView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/DessertCase.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/DessertCaseView.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java218
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java165
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java87
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java114
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsTileView.java73
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java4
-rw-r--r--packages/WallpaperCropper/res/values/strings.xml5
-rw-r--r--packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java408
-rw-r--r--packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java184
-rw-r--r--packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java4
169 files changed, 3449 insertions, 891 deletions
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 6faf7f8..179bcd1 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -50,6 +50,14 @@
android:authorities="com.android.documentsui.recents"
android:exported="false" />
+ <receiver android:name=".PackageReceiver">
+ <intent-filter>
+ <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
+ <action android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
+ <data android:scheme="package" />
+ </intent-filter>
+ </receiver>
+
<!-- TODO: remove when we have real clients -->
<activity android:name=".TestActivity" android:enabled="false">
<intent-filter>
diff --git a/packages/DocumentsUI/res/layout/fragment_save.xml b/packages/DocumentsUI/res/layout/fragment_save.xml
index 891f0a0..d601194 100644
--- a/packages/DocumentsUI/res/layout/fragment_save.xml
+++ b/packages/DocumentsUI/res/layout/fragment_save.xml
@@ -51,15 +51,31 @@
android:singleLine="true"
android:selectAllOnFocus="true" />
- <Button
- android:id="@android:id/button1"
+ <FrameLayout
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/menu_save"
- android:textAllCaps="true"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:padding="8dp" />
+ android:layout_height="match_parent">
+
+ <Button
+ android:id="@android:id/button1"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:background="?android:attr/selectableItemBackground"
+ android:text="@string/menu_save"
+ android:textAllCaps="true"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:padding="8dp" />
+
+ <ProgressBar
+ android:id="@android:id/progress"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:visibility="gone"
+ android:indeterminate="true"
+ android:padding="8dp"
+ style="?android:attr/progressBarStyle" />
+
+ </FrameLayout>
</LinearLayout>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
index 90be197..ba8c35f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
@@ -95,6 +95,11 @@ public class CreateDirectoryFragment extends DialogFragment {
}
@Override
+ protected void onPreExecute() {
+ mActivity.setPending(true);
+ }
+
+ @Override
protected DocumentInfo doInBackground(Void... params) {
final ContentResolver resolver = mActivity.getContentResolver();
ContentProviderClient client = null;
@@ -120,6 +125,8 @@ public class CreateDirectoryFragment extends DialogFragment {
} else {
Toast.makeText(mActivity, R.string.create_error, Toast.LENGTH_SHORT).show();
}
+
+ mActivity.setPending(false);
}
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 1f3901c..b2b2bd8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -47,6 +47,7 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.CancellationSignal;
+import android.os.OperationCanceledException;
import android.os.Parcelable;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
@@ -76,6 +77,7 @@ import android.widget.TextView;
import android.widget.Toast;
import com.android.documentsui.DocumentsActivity.State;
+import com.android.documentsui.ProviderExecutor.Preemptable;
import com.android.documentsui.RecentsProvider.StateColumns;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.RootInfo;
@@ -83,7 +85,6 @@ import com.google.android.collect.Lists;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* Display the documents inside a single directory.
@@ -126,9 +127,7 @@ public class DirectoryFragment extends Fragment {
private static final String EXTRA_QUERY = "query";
private static final String EXTRA_IGNORE_STATE = "ignoreState";
- private static AtomicInteger sLoaderId = new AtomicInteger(4000);
-
- private final int mLoaderId = sLoaderId.incrementAndGet();
+ private final int mLoaderId = 42;
public static void showNormal(FragmentManager fm, RootInfo root, DocumentInfo doc, int anim) {
show(fm, TYPE_NORMAL, root, doc, null, anim);
@@ -528,7 +527,7 @@ public class DirectoryFragment extends Fragment {
if (iconThumb != null) {
final ThumbnailAsyncTask oldTask = (ThumbnailAsyncTask) iconThumb.getTag();
if (oldTask != null) {
- oldTask.reallyCancel();
+ oldTask.preempt();
iconThumb.setTag(null);
}
}
@@ -794,7 +793,7 @@ public class DirectoryFragment extends Fragment {
final ThumbnailAsyncTask oldTask = (ThumbnailAsyncTask) iconThumb.getTag();
if (oldTask != null) {
- oldTask.reallyCancel();
+ oldTask.preempt();
iconThumb.setTag(null);
}
@@ -818,7 +817,7 @@ public class DirectoryFragment extends Fragment {
final ThumbnailAsyncTask task = new ThumbnailAsyncTask(
uri, iconMime, iconThumb, mThumbSize);
iconThumb.setTag(task);
- task.executeOnExecutor(ProviderExecutor.forAuthority(docAuthority));
+ ProviderExecutor.forAuthority(docAuthority).execute(task);
}
}
@@ -988,7 +987,8 @@ public class DirectoryFragment extends Fragment {
}
}
- private static class ThumbnailAsyncTask extends AsyncTask<Uri, Void, Bitmap> {
+ private static class ThumbnailAsyncTask extends AsyncTask<Uri, Void, Bitmap>
+ implements Preemptable {
private final Uri mUri;
private final ImageView mIconMime;
private final ImageView mIconThumb;
@@ -1004,7 +1004,8 @@ public class DirectoryFragment extends Fragment {
mSignal = new CancellationSignal();
}
- public void reallyCancel() {
+ @Override
+ public void preempt() {
cancel(false);
mSignal.cancel();
}
@@ -1028,7 +1029,9 @@ public class DirectoryFragment extends Fragment {
thumbs.put(mUri, result);
}
} catch (Exception e) {
- Log.w(TAG, "Failed to load thumbnail for " + mUri + ": " + e);
+ if (!(e instanceof OperationCanceledException)) {
+ Log.w(TAG, "Failed to load thumbnail for " + mUri + ": " + e);
+ }
} finally {
ContentProviderClient.releaseQuietly(client);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index d675e8d..4212e96 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -255,7 +255,9 @@ public class DocumentsActivity extends Activity {
}
mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
- mState.showAdvanced = SettingsActivity.getDisplayAdvancedDevices(this);
+ mState.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
+ mState.showAdvanced = mState.forceAdvanced
+ | SettingsActivity.getDisplayAdvancedDevices(this);
}
private class RestoreRootTask extends AsyncTask<Void, Void, RootInfo> {
@@ -661,6 +663,13 @@ public class DocumentsActivity extends Activity {
DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
}
+ public void setPending(boolean pending) {
+ final SaveFragment save = SaveFragment.get(getFragmentManager());
+ if (save != null) {
+ save.setPending(pending);
+ }
+ }
+
@Override
public void onBackPressed() {
if (!mState.stackTouched) {
@@ -1051,6 +1060,11 @@ public class DocumentsActivity extends Activity {
}
@Override
+ protected void onPreExecute() {
+ setPending(true);
+ }
+
+ @Override
protected Uri doInBackground(Void... params) {
final ContentResolver resolver = getContentResolver();
final DocumentInfo cwd = getCurrentDirectory();
@@ -1083,6 +1097,8 @@ public class DocumentsActivity extends Activity {
Toast.makeText(DocumentsActivity.this, R.string.save_error, Toast.LENGTH_SHORT)
.show();
}
+
+ setPending(false);
}
}
@@ -1122,6 +1138,7 @@ public class DocumentsActivity extends Activity {
public boolean allowMultiple = false;
public boolean showSize = false;
public boolean localOnly = false;
+ public boolean forceAdvanced = false;
public boolean showAdvanced = false;
public boolean stackTouched = false;
public boolean restored = false;
@@ -1162,6 +1179,7 @@ public class DocumentsActivity extends Activity {
out.writeInt(allowMultiple ? 1 : 0);
out.writeInt(showSize ? 1 : 0);
out.writeInt(localOnly ? 1 : 0);
+ out.writeInt(forceAdvanced ? 1 : 0);
out.writeInt(showAdvanced ? 1 : 0);
out.writeInt(stackTouched ? 1 : 0);
out.writeInt(restored ? 1 : 0);
@@ -1181,6 +1199,7 @@ public class DocumentsActivity extends Activity {
state.allowMultiple = in.readInt() != 0;
state.showSize = in.readInt() != 0;
state.localOnly = in.readInt() != 0;
+ state.forceAdvanced = in.readInt() != 0;
state.showAdvanced = in.readInt() != 0;
state.stackTouched = in.readInt() != 0;
state.restored = in.readInt() != 0;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java
index 6b46e3a..547e343 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java
@@ -75,6 +75,7 @@ public class DocumentsApplication extends Application {
packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
packageFilter.addDataScheme("package");
registerReceiver(mCacheReceiver, packageFilter);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
index 52d816f..55d73f2 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
@@ -17,6 +17,8 @@
package com.android.documentsui;
import static com.android.documentsui.DocumentsActivity.TAG;
+import static com.android.documentsui.model.DocumentInfo.getCursorLong;
+import static com.android.documentsui.model.DocumentInfo.getCursorString;
import android.database.AbstractCursor;
import android.database.Cursor;
@@ -50,10 +52,8 @@ public class FilteringCursorWrapper extends AbstractCursor {
cursor.moveToPosition(-1);
while (cursor.moveToNext()) {
- final String mimeType = cursor.getString(
- cursor.getColumnIndex(Document.COLUMN_MIME_TYPE));
- final long lastModified = cursor.getLong(
- cursor.getColumnIndex(Document.COLUMN_LAST_MODIFIED));
+ final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ final long lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
if (rejectMimes != null && MimePredicate.mimeMatches(rejectMimes, mimeType)) {
continue;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java b/packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java
new file mode 100644
index 0000000..aef63af
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013 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.documentsui;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+
+/**
+ * Clean up {@link RecentsProvider} when packages are removed.
+ */
+public class PackageReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final ContentResolver resolver = context.getContentResolver();
+
+ final String action = intent.getAction();
+ if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
+ resolver.call(RecentsProvider.buildRecent(), RecentsProvider.METHOD_PURGE, null, null);
+
+ } else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) {
+ final Uri data = intent.getData();
+ if (data != null) {
+ final String packageName = data.getSchemeSpecificPart();
+ resolver.call(RecentsProvider.buildRecent(), RecentsProvider.METHOD_PURGE_PACKAGE,
+ packageName, null);
+ }
+ }
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java b/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java
index 2105cb4..f94aebd 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java
@@ -16,10 +16,15 @@
package com.android.documentsui;
+import android.os.AsyncTask;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
+import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
@@ -29,7 +34,7 @@ public class ProviderExecutor extends Thread implements Executor {
@GuardedBy("sExecutors")
private static HashMap<String, ProviderExecutor> sExecutors = Maps.newHashMap();
- public static Executor forAuthority(String authority) {
+ public static ProviderExecutor forAuthority(String authority) {
synchronized (sExecutors) {
ProviderExecutor executor = sExecutors.get(authority);
if (executor == null) {
@@ -42,10 +47,54 @@ public class ProviderExecutor extends Thread implements Executor {
}
}
+ public interface Preemptable {
+ void preempt();
+ }
+
private final LinkedBlockingQueue<Runnable> mQueue = new LinkedBlockingQueue<Runnable>();
+ private final ArrayList<WeakReference<Preemptable>> mPreemptable = Lists.newArrayList();
+
+ private void preempt() {
+ synchronized (mPreemptable) {
+ int count = 0;
+ for (WeakReference<Preemptable> ref : mPreemptable) {
+ final Preemptable p = ref.get();
+ if (p != null) {
+ count++;
+ p.preempt();
+ }
+ }
+ mPreemptable.clear();
+ }
+ }
+
+ /**
+ * Execute the given task. If given task is not {@link Preemptable}, it will
+ * preempt all outstanding preemptable tasks.
+ */
+ public <P> void execute(AsyncTask<P, ?, ?> task, P... params) {
+ if (task instanceof Preemptable) {
+ synchronized (mPreemptable) {
+ mPreemptable.add(new WeakReference<Preemptable>((Preemptable) task));
+ }
+ task.executeOnExecutor(mNonPreemptingExecutor, params);
+ } else {
+ task.executeOnExecutor(this, params);
+ }
+ }
+
+ private Executor mNonPreemptingExecutor = new Executor() {
+ @Override
+ public void execute(Runnable command) {
+ Preconditions.checkNotNull(command);
+ mQueue.add(command);
+ }
+ };
+
@Override
public void execute(Runnable command) {
+ preempt();
Preconditions.checkNotNull(command);
mQueue.add(command);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
index 4313fa7..f6e4349 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
@@ -16,24 +16,40 @@
package com.android.documentsui;
+import static com.android.documentsui.model.DocumentInfo.getCursorString;
+
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
+import android.content.Intent;
import android.content.UriMatcher;
+import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
+import android.os.Bundle;
+import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.text.format.DateUtils;
import android.util.Log;
+import com.android.documentsui.model.DocumentStack;
+import com.android.documentsui.model.DurableUtils;
+import com.android.internal.util.Predicate;
+import com.google.android.collect.Sets;
+
+import libcore.io.IoUtils;
+
+import java.io.IOException;
+import java.util.Set;
+
public class RecentsProvider extends ContentProvider {
private static final String TAG = "RecentsProvider";
- public static final long MAX_HISTORY_IN_MILLIS = 45 * DateUtils.DAY_IN_MILLIS;
+ private static final long MAX_HISTORY_IN_MILLIS = 45 * DateUtils.DAY_IN_MILLIS;
private static final String AUTHORITY = "com.android.documentsui.recents";
@@ -43,6 +59,9 @@ public class RecentsProvider extends ContentProvider {
private static final int URI_STATE = 2;
private static final int URI_RESUME = 3;
+ public static final String METHOD_PURGE = "purge";
+ public static final String METHOD_PURGE_PACKAGE = "purgePackage";
+
static {
sMatcher.addURI(AUTHORITY, "recent", URI_RECENT);
// state/authority/rootId/docId
@@ -231,4 +250,116 @@ public class RecentsProvider extends ContentProvider {
public int delete(Uri uri, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException("Unsupported Uri " + uri);
}
+
+ @Override
+ public Bundle call(String method, String arg, Bundle extras) {
+ if (METHOD_PURGE.equals(method)) {
+ // Purge references to unknown authorities
+ final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
+ final Set<String> knownAuth = Sets.newHashSet();
+ for (ResolveInfo info : getContext()
+ .getPackageManager().queryIntentContentProviders(intent, 0)) {
+ knownAuth.add(info.providerInfo.authority);
+ }
+
+ purgeByAuthority(new Predicate<String>() {
+ @Override
+ public boolean apply(String authority) {
+ // Purge unknown authorities
+ return !knownAuth.contains(authority);
+ }
+ });
+
+ return null;
+
+ } else if (METHOD_PURGE_PACKAGE.equals(method)) {
+ // Purge references to authorities in given package
+ final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
+ intent.setPackage(arg);
+ final Set<String> packageAuth = Sets.newHashSet();
+ for (ResolveInfo info : getContext()
+ .getPackageManager().queryIntentContentProviders(intent, 0)) {
+ packageAuth.add(info.providerInfo.authority);
+ }
+
+ if (!packageAuth.isEmpty()) {
+ purgeByAuthority(new Predicate<String>() {
+ @Override
+ public boolean apply(String authority) {
+ // Purge authority matches
+ return packageAuth.contains(authority);
+ }
+ });
+ }
+
+ return null;
+
+ } else {
+ return super.call(method, arg, extras);
+ }
+ }
+
+ /**
+ * Purge all internal data whose authority matches the given
+ * {@link Predicate}.
+ */
+ private void purgeByAuthority(Predicate<String> predicate) {
+ final SQLiteDatabase db = mHelper.getWritableDatabase();
+ final DocumentStack stack = new DocumentStack();
+
+ Cursor cursor = db.query(TABLE_RECENT, null, null, null, null, null, null);
+ try {
+ while (cursor.moveToNext()) {
+ try {
+ final byte[] rawStack = cursor.getBlob(
+ cursor.getColumnIndex(RecentColumns.STACK));
+ DurableUtils.readFromArray(rawStack, stack);
+
+ if (stack.root != null && predicate.apply(stack.root.authority)) {
+ final String key = getCursorString(cursor, RecentColumns.KEY);
+ db.delete(TABLE_RECENT, RecentColumns.KEY + "=?", new String[] { key });
+ }
+ } catch (IOException ignored) {
+ }
+ }
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ }
+
+ cursor = db.query(TABLE_STATE, new String[] {
+ StateColumns.AUTHORITY }, null, null, StateColumns.AUTHORITY, null, null);
+ try {
+ while (cursor.moveToNext()) {
+ final String authority = getCursorString(cursor, StateColumns.AUTHORITY);
+ if (predicate.apply(authority)) {
+ db.delete(TABLE_STATE, StateColumns.AUTHORITY + "=?", new String[] {
+ authority });
+ Log.d(TAG, "Purged state for " + authority);
+ }
+ }
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ }
+
+ cursor = db.query(TABLE_RESUME, null, null, null, null, null, null);
+ try {
+ while (cursor.moveToNext()) {
+ try {
+ final byte[] rawStack = cursor.getBlob(
+ cursor.getColumnIndex(ResumeColumns.STACK));
+ DurableUtils.readFromArray(rawStack, stack);
+
+ if (stack.root != null && predicate.apply(stack.root.authority)) {
+ final String packageName = getCursorString(
+ cursor, ResumeColumns.PACKAGE_NAME);
+ db.delete(TABLE_RESUME, ResumeColumns.PACKAGE_NAME + "=?",
+ new String[] { packageName });
+ }
+ } catch (IOException ignored) {
+ }
+ }
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ }
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index b98e1ee..f6b43c7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -60,8 +60,8 @@ import java.util.concurrent.TimeUnit;
public class RootsCache {
private static final boolean LOGD = true;
- // TODO: cache roots in local provider to avoid spinning up backends
- // TODO: root updates should trigger UI refresh
+ public static final Uri sNotificationUri = Uri.parse(
+ "content://com.android.documentsui.roots/");
private final Context mContext;
private final ContentObserver mObserver;
@@ -201,6 +201,7 @@ public class RootsCache {
mStoppedAuthorities = mTaskStoppedAuthorities;
}
mFirstLoad.countDown();
+ resolver.notifyChange(sNotificationUri, null, false);
return null;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index fdbc3ab..931dac9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -16,6 +16,8 @@
package com.android.documentsui;
+import static com.android.documentsui.DocumentsActivity.State.ACTION_GET_CONTENT;
+
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
@@ -25,7 +27,9 @@ import android.content.Intent;
import android.content.Loader;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.net.Uri;
import android.os.Bundle;
+import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.view.LayoutInflater;
@@ -33,6 +37,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
@@ -131,7 +136,15 @@ public class RootsFragment extends Fragment {
final Context context = getActivity();
final State state = ((DocumentsActivity) context).getDisplayState();
- state.showAdvanced = SettingsActivity.getDisplayAdvancedDevices(context);
+ state.showAdvanced = state.forceAdvanced
+ | SettingsActivity.getDisplayAdvancedDevices(context);
+
+ if (state.action == ACTION_GET_CONTENT) {
+ mList.setOnItemLongClickListener(mItemLongClickListener);
+ } else {
+ mList.setOnItemLongClickListener(null);
+ mList.setLongClickable(false);
+ }
getLoaderManager().restartLoader(2, null, mCallbacks);
}
@@ -152,6 +165,13 @@ public class RootsFragment extends Fragment {
}
}
+ private void showAppDetails(ResolveInfo ri) {
+ final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ intent.setData(Uri.fromParts("package", ri.activityInfo.packageName, null));
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ startActivity(intent);
+ }
+
private OnItemClickListener mItemListener = new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
@@ -167,6 +187,19 @@ public class RootsFragment extends Fragment {
}
};
+ private OnItemLongClickListener mItemLongClickListener = new OnItemLongClickListener() {
+ @Override
+ public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
+ final Item item = mAdapter.getItem(position);
+ if (item instanceof AppItem) {
+ showAppDetails(((AppItem) item).info);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ };
+
private static abstract class Item {
private final int mLayoutId;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java
index 7108971..8d37cdf 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java
@@ -25,6 +25,8 @@ import com.android.documentsui.model.RootInfo;
import java.util.Collection;
public class RootsLoader extends AsyncTaskLoader<Collection<RootInfo>> {
+ private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();
+
private final RootsCache mRoots;
private final State mState;
@@ -34,6 +36,9 @@ public class RootsLoader extends AsyncTaskLoader<Collection<RootInfo>> {
super(context);
mRoots = roots;
mState = state;
+
+ getContext().getContentResolver()
+ .registerContentObserver(RootsCache.sNotificationUri, false, mObserver);
}
@Override
@@ -77,5 +82,7 @@ public class RootsLoader extends AsyncTaskLoader<Collection<RootInfo>> {
onStopLoading();
mResult = null;
+
+ getContext().getContentResolver().unregisterContentObserver(mObserver);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
index 23e047c..9d70c51 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
@@ -30,6 +30,7 @@ import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
+import android.widget.ProgressBar;
import com.android.documentsui.model.DocumentInfo;
@@ -42,6 +43,7 @@ public class SaveFragment extends Fragment {
private DocumentInfo mReplaceTarget;
private EditText mDisplayName;
private Button mSave;
+ private ProgressBar mProgress;
private boolean mIgnoreNextEdit;
private static final String EXTRA_MIME_TYPE = "mime_type";
@@ -83,6 +85,8 @@ public class SaveFragment extends Fragment {
mSave.setOnClickListener(mSaveListener);
mSave.setEnabled(false);
+ mProgress = (ProgressBar) view.findViewById(android.R.id.progress);
+
return view;
}
@@ -92,7 +96,6 @@ public class SaveFragment extends Fragment {
if (mIgnoreNextEdit) {
mIgnoreNextEdit = false;
} else {
- Log.d(TAG, "onTextChanged!");
mReplaceTarget = null;
}
}
@@ -140,4 +143,9 @@ public class SaveFragment extends Fragment {
public void setSaveEnabled(boolean enabled) {
mSave.setEnabled(enabled);
}
+
+ public void setPending(boolean pending) {
+ mSave.setVisibility(pending ? View.INVISIBLE : View.VISIBLE);
+ mProgress.setVisibility(pending ? View.VISIBLE : View.GONE);
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
index 19ad2e2..a23dd15 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
@@ -19,6 +19,8 @@ package com.android.documentsui;
import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_DISPLAY_NAME;
import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_LAST_MODIFIED;
import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_SIZE;
+import static com.android.documentsui.model.DocumentInfo.getCursorLong;
+import static com.android.documentsui.model.DocumentInfo.getCursorString;
import android.database.AbstractCursor;
import android.database.Cursor;
@@ -62,10 +64,9 @@ public class SortingCursorWrapper extends AbstractCursor {
switch (sortOrder) {
case SORT_ORDER_DISPLAY_NAME:
- final String mimeType = cursor.getString(
- cursor.getColumnIndex(Document.COLUMN_MIME_TYPE));
- final String displayName = cursor.getString(
- cursor.getColumnIndex(Document.COLUMN_DISPLAY_NAME));
+ final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ final String displayName = getCursorString(
+ cursor, Document.COLUMN_DISPLAY_NAME);
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
mValueString[i] = '\001' + displayName;
} else {
@@ -73,11 +74,10 @@ public class SortingCursorWrapper extends AbstractCursor {
}
break;
case SORT_ORDER_LAST_MODIFIED:
- mValueLong[i] = cursor.getLong(
- cursor.getColumnIndex(Document.COLUMN_LAST_MODIFIED));
+ mValueLong[i] = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
break;
case SORT_ORDER_SIZE:
- mValueLong[i] = cursor.getLong(cursor.getColumnIndex(Document.COLUMN_SIZE));
+ mValueLong[i] = getCursorLong(cursor, Document.COLUMN_SIZE);
break;
}
}
diff --git a/packages/ExternalStorageProvider/AndroidManifest.xml b/packages/ExternalStorageProvider/AndroidManifest.xml
index 99a4260..edd6255 100644
--- a/packages/ExternalStorageProvider/AndroidManifest.xml
+++ b/packages/ExternalStorageProvider/AndroidManifest.xml
@@ -3,6 +3,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
<application android:label="@string/app_label">
<provider
@@ -16,6 +17,14 @@
</intent-filter>
</provider>
+ <receiver android:name=".MountReceiver">
+ <intent-filter>
+ <action android:name="android.intent.action.MEDIA_MOUNTED" />
+ <action android:name="android.intent.action.MEDIA_UNMOUNTED" />
+ <data android:scheme="file" />
+ </intent-filter>
+ </receiver>
+
<!-- TODO: find a better place for tests to live -->
<provider
android:name=".TestDocumentsProvider"
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 11ff2d8..559e052 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -16,21 +16,28 @@
package com.android.externalstorage;
+import android.content.ContentResolver;
+import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder;
import android.graphics.Point;
-import android.media.ExifInterface;
+import android.net.Uri;
import android.os.CancellationSignal;
import android.os.Environment;
+import android.os.FileObserver;
import android.os.ParcelFileDescriptor;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
+import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
-import android.provider.DocumentsContract;
import android.provider.DocumentsProvider;
+import android.util.Log;
import android.webkit.MimeTypeMap;
+import com.android.internal.annotations.GuardedBy;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
@@ -45,6 +52,10 @@ import java.util.Map;
public class ExternalStorageProvider extends DocumentsProvider {
private static final String TAG = "ExternalStorage";
+ private static final boolean LOG_INOTIFY = false;
+
+ public static final String AUTHORITY = "com.android.externalstorage.documents";
+
// docId format: root:path/to/file
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
@@ -64,42 +75,94 @@ public class ExternalStorageProvider extends DocumentsProvider {
public String docId;
}
+ private static final String ROOT_ID_PRIMARY_EMULATED = "primary";
+
+ private StorageManager mStorageManager;
+
+ private final Object mRootsLock = new Object();
+
+ @GuardedBy("mRootsLock")
private ArrayList<RootInfo> mRoots;
+ @GuardedBy("mRootsLock")
private HashMap<String, RootInfo> mIdToRoot;
+ @GuardedBy("mRootsLock")
private HashMap<String, File> mIdToPath;
+ @GuardedBy("mObservers")
+ private Map<File, DirectoryObserver> mObservers = Maps.newHashMap();
+
@Override
public boolean onCreate() {
+ mStorageManager = (StorageManager) getContext().getSystemService(Context.STORAGE_SERVICE);
+
mRoots = Lists.newArrayList();
mIdToRoot = Maps.newHashMap();
mIdToPath = Maps.newHashMap();
- // TODO: support multiple storage devices, requiring that volume serial
- // number be burned into rootId so we can identify files from different
- // volumes. currently we only use a static rootId for emulated storage,
- // since that storage never changes.
- if (!Environment.isExternalStorageEmulated()) return true;
-
- try {
- final String rootId = "primary";
- final File path = Environment.getExternalStorageDirectory();
- mIdToPath.put(rootId, path);
-
- final RootInfo root = new RootInfo();
- root.rootId = rootId;
- root.flags = Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED
- | Root.FLAG_SUPPORTS_SEARCH;
- root.title = getContext().getString(R.string.root_internal_storage);
- root.docId = getDocIdForFile(path);
- mRoots.add(root);
- mIdToRoot.put(rootId, root);
- } catch (FileNotFoundException e) {
- throw new IllegalStateException(e);
- }
+ updateVolumes();
return true;
}
+ public void updateVolumes() {
+ synchronized (mRootsLock) {
+ updateVolumesLocked();
+ }
+ }
+
+ private void updateVolumesLocked() {
+ mRoots.clear();
+ mIdToPath.clear();
+ mIdToRoot.clear();
+
+ final StorageVolume[] volumes = mStorageManager.getVolumeList();
+ for (StorageVolume volume : volumes) {
+ final boolean mounted = Environment.MEDIA_MOUNTED.equals(volume.getState())
+ || Environment.MEDIA_MOUNTED_READ_ONLY.equals(volume.getState());
+ if (!mounted) continue;
+
+ final String rootId;
+ if (volume.isPrimary() && volume.isEmulated()) {
+ rootId = ROOT_ID_PRIMARY_EMULATED;
+ } else if (volume.getUuid() != null) {
+ rootId = volume.getUuid();
+ } else {
+ Log.d(TAG, "Missing UUID for " + volume.getPath() + "; skipping");
+ continue;
+ }
+
+ if (mIdToPath.containsKey(rootId)) {
+ Log.w(TAG, "Duplicate UUID " + rootId + "; skipping");
+ continue;
+ }
+
+ try {
+ final File path = volume.getPathFile();
+ mIdToPath.put(rootId, path);
+
+ final RootInfo root = new RootInfo();
+ root.rootId = rootId;
+ root.flags = Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED
+ | Root.FLAG_SUPPORTS_SEARCH;
+ if (ROOT_ID_PRIMARY_EMULATED.equals(rootId)) {
+ root.title = getContext().getString(R.string.root_internal_storage);
+ } else {
+ root.title = volume.getUserLabel();
+ }
+ root.docId = getDocIdForFile(path);
+ mRoots.add(root);
+ mIdToRoot.put(rootId, root);
+ } catch (FileNotFoundException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ Log.d(TAG, "After updating volumes, found " + mRoots.size() + " active roots");
+
+ getContext().getContentResolver()
+ .notifyChange(DocumentsContract.buildRootsUri(AUTHORITY), null, false);
+ }
+
private static String[] resolveRootProjection(String[] projection) {
return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
}
@@ -113,11 +176,13 @@ public class ExternalStorageProvider extends DocumentsProvider {
// Find the most-specific root path
Map.Entry<String, File> mostSpecific = null;
- for (Map.Entry<String, File> root : mIdToPath.entrySet()) {
- final String rootPath = root.getValue().getPath();
- if (path.startsWith(rootPath) && (mostSpecific == null
- || rootPath.length() > mostSpecific.getValue().getPath().length())) {
- mostSpecific = root;
+ synchronized (mRootsLock) {
+ for (Map.Entry<String, File> root : mIdToPath.entrySet()) {
+ final String rootPath = root.getValue().getPath();
+ if (path.startsWith(rootPath) && (mostSpecific == null
+ || rootPath.length() > mostSpecific.getValue().getPath().length())) {
+ mostSpecific = root;
+ }
}
}
@@ -143,7 +208,10 @@ public class ExternalStorageProvider extends DocumentsProvider {
final String tag = docId.substring(0, splitIndex);
final String path = docId.substring(splitIndex + 1);
- File target = mIdToPath.get(tag);
+ File target;
+ synchronized (mRootsLock) {
+ target = mIdToPath.get(tag);
+ }
if (target == null) {
throw new FileNotFoundException("No root for " + tag);
}
@@ -199,16 +267,18 @@ public class ExternalStorageProvider extends DocumentsProvider {
@Override
public Cursor queryRoots(String[] projection) throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
- for (String rootId : mIdToPath.keySet()) {
- final RootInfo root = mIdToRoot.get(rootId);
- final File path = mIdToPath.get(rootId);
-
- final RowBuilder row = result.newRow();
- row.add(Root.COLUMN_ROOT_ID, root.rootId);
- row.add(Root.COLUMN_FLAGS, root.flags);
- row.add(Root.COLUMN_TITLE, root.title);
- row.add(Root.COLUMN_DOCUMENT_ID, root.docId);
- row.add(Root.COLUMN_AVAILABLE_BYTES, path.getFreeSpace());
+ synchronized (mRootsLock) {
+ for (String rootId : mIdToPath.keySet()) {
+ final RootInfo root = mIdToRoot.get(rootId);
+ final File path = mIdToPath.get(rootId);
+
+ final RowBuilder row = result.newRow();
+ row.add(Root.COLUMN_ROOT_ID, root.rootId);
+ row.add(Root.COLUMN_FLAGS, root.flags);
+ row.add(Root.COLUMN_TITLE, root.title);
+ row.add(Root.COLUMN_DOCUMENT_ID, root.docId);
+ row.add(Root.COLUMN_AVAILABLE_BYTES, path.getFreeSpace());
+ }
}
return result;
}
@@ -265,8 +335,9 @@ public class ExternalStorageProvider extends DocumentsProvider {
public Cursor queryChildDocuments(
String parentDocumentId, String[] projection, String sortOrder)
throws FileNotFoundException {
- final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
final File parent = getFileForDocId(parentDocumentId);
+ final MatrixCursor result = new DirectoryCursor(
+ resolveDocumentProjection(projection), parentDocumentId, parent);
for (File file : parent.listFiles()) {
includeFile(result, null, file);
}
@@ -277,7 +348,11 @@ public class ExternalStorageProvider extends DocumentsProvider {
public Cursor querySearchDocuments(String rootId, String query, String[] projection)
throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
- final File parent = mIdToPath.get(rootId);
+
+ final File parent;
+ synchronized (mRootsLock) {
+ parent = mIdToPath.get(rootId);
+ }
final LinkedList<File> pending = new LinkedList<File>();
pending.add(parent);
@@ -328,7 +403,7 @@ public class ExternalStorageProvider extends DocumentsProvider {
private static String getTypeForName(String name) {
final int lastDot = name.lastIndexOf('.');
if (lastDot >= 0) {
- final String extension = name.substring(lastDot + 1);
+ final String extension = name.substring(lastDot + 1).toLowerCase();
final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
if (mime != null) {
return mime;
@@ -345,7 +420,7 @@ public class ExternalStorageProvider extends DocumentsProvider {
private static String removeExtension(String mimeType, String name) {
final int lastDot = name.lastIndexOf('.');
if (lastDot >= 0) {
- final String extension = name.substring(lastDot + 1);
+ final String extension = name.substring(lastDot + 1).toLowerCase();
final String nameMime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
if (mimeType.equals(nameMime)) {
return name.substring(0, lastDot);
@@ -365,4 +440,86 @@ public class ExternalStorageProvider extends DocumentsProvider {
}
return name;
}
+
+ private void startObserving(File file, Uri notifyUri) {
+ synchronized (mObservers) {
+ DirectoryObserver observer = mObservers.get(file);
+ if (observer == null) {
+ observer = new DirectoryObserver(
+ file, getContext().getContentResolver(), notifyUri);
+ observer.startWatching();
+ mObservers.put(file, observer);
+ }
+ observer.mRefCount++;
+
+ if (LOG_INOTIFY) Log.d(TAG, "after start: " + observer);
+ }
+ }
+
+ private void stopObserving(File file) {
+ synchronized (mObservers) {
+ DirectoryObserver observer = mObservers.get(file);
+ if (observer == null) return;
+
+ observer.mRefCount--;
+ if (observer.mRefCount == 0) {
+ mObservers.remove(file);
+ observer.stopWatching();
+ }
+
+ if (LOG_INOTIFY) Log.d(TAG, "after stop: " + observer);
+ }
+ }
+
+ private static class DirectoryObserver extends FileObserver {
+ private static final int NOTIFY_EVENTS = ATTRIB | CLOSE_WRITE | MOVED_FROM | MOVED_TO
+ | CREATE | DELETE | DELETE_SELF | MOVE_SELF;
+
+ private final File mFile;
+ private final ContentResolver mResolver;
+ private final Uri mNotifyUri;
+
+ private int mRefCount = 0;
+
+ public DirectoryObserver(File file, ContentResolver resolver, Uri notifyUri) {
+ super(file.getAbsolutePath(), NOTIFY_EVENTS);
+ mFile = file;
+ mResolver = resolver;
+ mNotifyUri = notifyUri;
+ }
+
+ @Override
+ public void onEvent(int event, String path) {
+ if ((event & NOTIFY_EVENTS) != 0) {
+ if (LOG_INOTIFY) Log.d(TAG, "onEvent() " + event + " at " + path);
+ mResolver.notifyChange(mNotifyUri, null, false);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "DirectoryObserver{file=" + mFile.getAbsolutePath() + ", ref=" + mRefCount + "}";
+ }
+ }
+
+ private class DirectoryCursor extends MatrixCursor {
+ private final File mFile;
+
+ public DirectoryCursor(String[] columnNames, String docId, File file) {
+ super(columnNames);
+
+ final Uri notifyUri = DocumentsContract.buildChildDocumentsUri(
+ AUTHORITY, docId);
+ setNotificationUri(getContext().getContentResolver(), notifyUri);
+
+ mFile = file;
+ startObserving(mFile, notifyUri);
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ stopObserving(mFile);
+ }
+ }
}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/MountReceiver.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/MountReceiver.java
new file mode 100644
index 0000000..8a6c7d6
--- /dev/null
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/MountReceiver.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 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.externalstorage;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentProviderClient;
+import android.content.Context;
+import android.content.Intent;
+
+public class MountReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final ContentProviderClient client = context.getContentResolver()
+ .acquireContentProviderClient(ExternalStorageProvider.AUTHORITY);
+ try {
+ ((ExternalStorageProvider) client.getLocalContentProvider()).updateVolumes();
+ } finally {
+ ContentProviderClient.releaseQuietly(client);
+ }
+ }
+}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
index 0caddcc..8eb70e9 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
@@ -33,6 +33,7 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.CancellationSignal;
+import android.os.CancellationSignal.OnCancelListener;
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
import android.provider.DocumentsContract;
@@ -54,8 +55,12 @@ import java.lang.ref.WeakReference;
public class TestDocumentsProvider extends DocumentsProvider {
private static final String TAG = "TestDocuments";
+ private static final boolean LAG = false;
+
+ private static final boolean ROOT_LAME_PROJECTION = false;
+ private static final boolean DOCUMENT_LAME_PROJECTION = false;
+
private static final boolean ROOTS_WEDGE = false;
- private static final boolean ROOTS_LAG = false;
private static final boolean ROOTS_CRASH = false;
private static final boolean ROOTS_REFRESH = false;
@@ -86,10 +91,12 @@ public class TestDocumentsProvider extends DocumentsProvider {
};
private static String[] resolveRootProjection(String[] projection) {
+ if (ROOT_LAME_PROJECTION) return new String[0];
return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
}
private static String[] resolveDocumentProjection(String[] projection) {
+ if (DOCUMENT_LAME_PROJECTION) return new String[0];
return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION;
}
@@ -105,8 +112,8 @@ public class TestDocumentsProvider extends DocumentsProvider {
public Cursor queryRoots(String[] projection) throws FileNotFoundException {
Log.d(TAG, "Someone asked for our roots!");
- if (ROOTS_WEDGE) SystemClock.sleep(Integer.MAX_VALUE);
- if (ROOTS_LAG) SystemClock.sleep(3000);
+ if (LAG) lagUntilCanceled(null);
+ if (ROOTS_WEDGE) wedgeUntilCanceled(null);
if (ROOTS_CRASH) System.exit(12);
if (ROOTS_REFRESH) {
@@ -125,7 +132,7 @@ public class TestDocumentsProvider extends DocumentsProvider {
final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
final RowBuilder row = result.newRow();
row.add(Root.COLUMN_ROOT_ID, MY_ROOT_ID);
- row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_RECENTS);
+ row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_RECENTS | Root.FLAG_SUPPORTS_CREATE);
row.add(Root.COLUMN_TITLE, "_Test title which is really long");
row.add(Root.COLUMN_SUMMARY,
SystemClock.elapsedRealtime() + " summary which is also super long text");
@@ -137,6 +144,7 @@ public class TestDocumentsProvider extends DocumentsProvider {
@Override
public Cursor queryDocument(String documentId, String[] projection)
throws FileNotFoundException {
+ if (LAG) lagUntilCanceled(null);
if (DOCUMENT_CRASH) System.exit(12);
final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
@@ -144,6 +152,14 @@ public class TestDocumentsProvider extends DocumentsProvider {
return result;
}
+ @Override
+ public String createDocument(String parentDocumentId, String mimeType, String displayName)
+ throws FileNotFoundException {
+ if (LAG) lagUntilCanceled(null);
+
+ return super.createDocument(parentDocumentId, mimeType, displayName);
+ }
+
/**
* Holds any outstanding or finished "network" fetching.
*/
@@ -209,6 +225,7 @@ public class TestDocumentsProvider extends DocumentsProvider {
String parentDocumentId, String[] projection, String sortOrder)
throws FileNotFoundException {
+ if (LAG) lagUntilCanceled(null);
if (CHILD_WEDGE) SystemClock.sleep(Integer.MAX_VALUE);
if (CHILD_CRASH) System.exit(12);
@@ -228,7 +245,7 @@ public class TestDocumentsProvider extends DocumentsProvider {
if (THUMB_HUNDREDS) {
for (int i = 0; i < 256; i++) {
- includeFile(result, "i maded u an picshure", Document.FLAG_SUPPORTS_THUMBNAIL);
+ includeFile(result, "i maded u an picshure" + i, Document.FLAG_SUPPORTS_THUMBNAIL);
}
}
@@ -278,7 +295,8 @@ public class TestDocumentsProvider extends DocumentsProvider {
public Cursor queryRecentDocuments(String rootId, String[] projection)
throws FileNotFoundException {
- if (RECENT_WEDGE) SystemClock.sleep(Integer.MAX_VALUE);
+ if (LAG) lagUntilCanceled(null);
+ if (RECENT_WEDGE) wedgeUntilCanceled(null);
// Pretend to take a super long time to respond
SystemClock.sleep(3000);
@@ -292,6 +310,7 @@ public class TestDocumentsProvider extends DocumentsProvider {
@Override
public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal)
throws FileNotFoundException {
+ if (LAG) lagUntilCanceled(null);
throw new FileNotFoundException();
}
@@ -299,6 +318,7 @@ public class TestDocumentsProvider extends DocumentsProvider {
public AssetFileDescriptor openDocumentThumbnail(
String docId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException {
+ if (LAG) lagUntilCanceled(signal);
if (THUMB_WEDGE) wedgeUntilCanceled(signal);
if (THUMB_CRASH) System.exit(12);
@@ -339,15 +359,34 @@ public class TestDocumentsProvider extends DocumentsProvider {
return true;
}
+ private static void lagUntilCanceled(CancellationSignal signal) {
+ waitForCancelOrTimeout(signal, 1500);
+ }
+
private static void wedgeUntilCanceled(CancellationSignal signal) {
+ waitForCancelOrTimeout(signal, Integer.MAX_VALUE);
+ }
+
+ private static void waitForCancelOrTimeout(
+ final CancellationSignal signal, long timeoutMillis) {
if (signal != null) {
- while (true) {
- signal.throwIfCanceled();
- SystemClock.sleep(500);
- }
- } else {
- Log.w(TAG, "WEDGING WITHOUT A CANCELLATIONSIGNAL");
- SystemClock.sleep(Integer.MAX_VALUE);
+ final Thread blocked = Thread.currentThread();
+ signal.setOnCancelListener(new OnCancelListener() {
+ @Override
+ public void onCancel() {
+ blocked.interrupt();
+ }
+ });
+ signal.throwIfCanceled();
+ }
+
+ try {
+ Thread.sleep(timeoutMillis);
+ } catch (InterruptedException e) {
+ }
+
+ if (signal != null) {
+ signal.throwIfCanceled();
}
}
@@ -360,6 +399,7 @@ public class TestDocumentsProvider extends DocumentsProvider {
if (MY_DOC_ID.equals(docId)) {
row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+ row.add(Document.COLUMN_FLAGS, Document.FLAG_DIR_SUPPORTS_CREATE);
} else if (MY_DOC_NULL.equals(docId)) {
// No MIME type
} else {
diff --git a/packages/Keyguard/AndroidManifest.xml b/packages/Keyguard/AndroidManifest.xml
index 9e296e2..66d1e75 100644
--- a/packages/Keyguard/AndroidManifest.xml
+++ b/packages/Keyguard/AndroidManifest.xml
@@ -38,6 +38,7 @@
<uses-permission android:name="android.permission.BIND_DEVICE_ADMIN" />
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
+ <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
<application android:label="@string/app_name"
android:process="com.android.systemui"
diff --git a/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_forgotpassword_normal.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_forgotpassword_normal.png
deleted file mode 100644
index 6402d3d..0000000
--- a/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_forgotpassword_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_forgotpassword_pressed.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_forgotpassword_pressed.png
deleted file mode 100644
index 83be046..0000000
--- a/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_forgotpassword_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_forgotpassword_normal.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_forgotpassword_normal.png
deleted file mode 100644
index a7e063a..0000000
--- a/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_forgotpassword_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_forgotpassword_pressed.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_forgotpassword_pressed.png
deleted file mode 100644
index 53af5a5..0000000
--- a/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_forgotpassword_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_forgotpassword_normal.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_forgotpassword_normal.png
deleted file mode 100644
index e4172ce..0000000
--- a/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_forgotpassword_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_forgotpassword_pressed.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_forgotpassword_pressed.png
deleted file mode 100644
index e2c7621..0000000
--- a/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_forgotpassword_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/Keyguard/res/layout-land/keyguard_host_view.xml b/packages/Keyguard/res/layout-land/keyguard_host_view.xml
index eeb9ee7..9f1c1f0 100644
--- a/packages/Keyguard/res/layout-land/keyguard_host_view.xml
+++ b/packages/Keyguard/res/layout-land/keyguard_host_view.xml
@@ -60,6 +60,8 @@
android:id="@+id/keyguard_security_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
androidprv:layout_childType="challenge"
androidprv:layout_centerWithinArea="0.55">
<com.android.keyguard.KeyguardSecurityViewFlipper
diff --git a/packages/Keyguard/res/layout-port/keyguard_host_view.xml b/packages/Keyguard/res/layout-port/keyguard_host_view.xml
index 8498dcf..136b296 100644
--- a/packages/Keyguard/res/layout-port/keyguard_host_view.xml
+++ b/packages/Keyguard/res/layout-port/keyguard_host_view.xml
@@ -31,7 +31,8 @@
<com.android.keyguard.SlidingChallengeLayout
android:id="@+id/sliding_layout"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:clipChildren="false">
<FrameLayout
android:layout_width="match_parent"
@@ -64,6 +65,8 @@
android:id="@+id/keyguard_security_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
androidprv:layout_maxHeight="@dimen/keyguard_security_height"
androidprv:layout_childType="challenge"
android:padding="0dp"
diff --git a/packages/Keyguard/res/layout-sw600dp-port/keyguard_host_view.xml b/packages/Keyguard/res/layout-sw600dp-port/keyguard_host_view.xml
index 77bc9b5..85f6b6d 100644
--- a/packages/Keyguard/res/layout-sw600dp-port/keyguard_host_view.xml
+++ b/packages/Keyguard/res/layout-sw600dp-port/keyguard_host_view.xml
@@ -61,6 +61,8 @@
android:id="@+id/keyguard_security_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
androidprv:layout_centerWithinArea="0.5"
androidprv:layout_childType="challenge"
android:layout_gravity="center_horizontal|bottom">
diff --git a/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml b/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
index 313fe9f..b4847f0 100644
--- a/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
+++ b/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
@@ -32,17 +32,15 @@
android:id="@+id/carrier_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:singleLine="true"
android:ellipsize="marquee"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="@dimen/kg_status_line_font_size"
- android:textColor="?android:attr/textColorSecondary"
- android:textAllCaps="@bool/kg_use_all_caps" />
+ android:textColor="?android:attr/textColorSecondary" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="-10dip"
+ android:layout_marginTop="@dimen/eca_overlap"
style="?android:attr/buttonBarStyle"
android:orientation="horizontal"
android:gravity="center"
@@ -66,12 +64,10 @@
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:drawableLeft="@drawable/lockscreen_forgot_password_button"
style="?android:attr/buttonBarButtonStyle"
android:textSize="@dimen/kg_status_line_font_size"
android:textColor="?android:attr/textColorSecondary"
android:textAppearance="?android:attr/textAppearanceMedium"
- android:drawablePadding="8dip"
android:visibility="gone"
android:textAllCaps="@bool/kg_use_all_caps" />
</LinearLayout>
diff --git a/packages/Keyguard/res/layout/keyguard_presentation.xml b/packages/Keyguard/res/layout/keyguard_presentation.xml
new file mode 100644
index 0000000..7df0b70
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_presentation.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is a view that shows general status information in Keyguard. -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res/com.android.keyguard"
+ android:id="@+id/presentation"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.android.keyguard.KeyguardStatusView
+ android:id="@+id/clock"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/keyguard_accessibility_status">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|top"
+ android:orientation="vertical"
+ android:focusable="true">
+ <TextClock
+ android:id="@+id/clock_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|top"
+ android:textColor="@color/clock_white"
+ android:singleLine="true"
+ style="@style/widget_big_thin"
+ android:format12Hour="@string/keyguard_widget_12_hours_format"
+ android:format24Hour="@string/keyguard_widget_24_hours_format"
+ android:baselineAligned="true"
+ android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
+
+ <include layout="@layout/keyguard_status_area" />
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dip"
+ android:layout_gravity="center_horizontal"
+ android:src="@drawable/kg_security_lock_normal" />
+ </LinearLayout>
+ </com.android.keyguard.KeyguardStatusView>
+
+</FrameLayout>
diff --git a/packages/Keyguard/res/layout/keyguard_status_view.xml b/packages/Keyguard/res/layout/keyguard_status_view.xml
index 5857fc2..a4d298a 100644
--- a/packages/Keyguard/res/layout/keyguard_status_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_status_view.xml
@@ -26,7 +26,7 @@
android:layout_height="match_parent"
androidprv:layout_maxWidth="@dimen/keyguard_security_width"
androidprv:layout_maxHeight="@dimen/keyguard_security_height"
- android:gravity="center_horizontal">
+ android:gravity="center">
<com.android.keyguard.KeyguardStatusView
android:id="@+id/keyguard_status_view_face_palm"
diff --git a/packages/Keyguard/res/values-sw540dp-port/dimens.xml b/packages/Keyguard/res/values-sw540dp-port/dimens.xml
new file mode 100644
index 0000000..de3106f
--- /dev/null
+++ b/packages/Keyguard/res/values-sw540dp-port/dimens.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/dimens.xml
+**
+** Copyright 2013, 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>
+ <!-- Height of the sliding KeyguardSecurityContainer
+ (includes 2x keyguard_security_view_margin) -->
+ <dimen name="keyguard_security_height">500dp</dimen>
+</resources>
diff --git a/packages/Keyguard/res/values-sw600dp/dimens.xml b/packages/Keyguard/res/values-sw600dp/dimens.xml
index f8a1362..ea5ef27 100644
--- a/packages/Keyguard/res/values-sw600dp/dimens.xml
+++ b/packages/Keyguard/res/values-sw600dp/dimens.xml
@@ -66,4 +66,7 @@
<dimen name="widget_label_font_size">16dp</dimen>
<dimen name="widget_big_font_size">141dp</dimen>
+ <!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
+ Should be 0 on devices with plenty of room (e.g. tablets) -->
+ <dimen name="eca_overlap">0dip</dimen>
</resources>
diff --git a/packages/Keyguard/res/values/dimens.xml b/packages/Keyguard/res/values/dimens.xml
index a1ad120..71e9924 100644
--- a/packages/Keyguard/res/values/dimens.xml
+++ b/packages/Keyguard/res/values/dimens.xml
@@ -150,6 +150,10 @@
security mode. -->
<dimen name="kg_small_widget_height">160dp</dimen>
+ <!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
+ Should be 0 on devices with plenty of room (e.g. tablets) -->
+ <dimen name="eca_overlap">-10dip</dimen>
+
<!-- Default clock parameters -->
<dimen name="bottom_text_spacing_digital">-8dp</dimen>
<dimen name="label_font_size">14dp</dimen>
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index abc4483..4738049 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -24,12 +24,12 @@
<!-- Instructions telling the user to enter their SIM PUK to unlock the keyguard.
Displayed in one line in a large font. -->
- <string name="keyguard_password_enter_puk_code">Type PUK and new PIN code</string>
+ <string name="keyguard_password_enter_puk_code">Type SIM PUK and new PIN code</string>
<!-- Prompt to enter SIM PUK in Edit Text Box in unlock screen -->
- <string name="keyguard_password_enter_puk_prompt">PUK code</string>
+ <string name="keyguard_password_enter_puk_prompt">SIM PUK code</string>
<!-- Prompt to enter New SIM PIN in Edit Text Box in unlock screen -->
- <string name="keyguard_password_enter_pin_prompt">New PIN code</string>
+ <string name="keyguard_password_enter_pin_prompt">New SIM PIN code</string>
<!-- Displayed as hint in passwordEntry EditText on PasswordUnlockScreen [CHAR LIMIT=30]-->
<string name="keyguard_password_entry_touch_hint"><font size="17">Touch to type password</font></string>
@@ -249,8 +249,6 @@
<string name="kg_enter_confirm_pin_hint">Confirm desired PIN code</string>
<!-- Message shown in dialog while the device is unlocking the SIM card -->
<string name="kg_sim_unlock_progress_dialog_message">Unlocking SIM card\u2026</string>
- <!-- Message shown when the user enters the wrong PIN code -->
- <string name="kg_password_wrong_pin_code">Incorrect PIN code.</string>
<!-- Message shown when the user enters an invalid SIM pin password in PUK screen -->
<string name="kg_invalid_sim_pin_hint">Type a PIN that is 4 to 8 numbers.</string>
<!-- Message shown when the user enters an invalid PUK code in the PUK screen -->
@@ -333,6 +331,34 @@
<!-- The delete-widget drop target button text -->
<string name="kg_reordering_delete_drop_target_text">Remove</string>
+ <!-- Instructions telling the user that they entered the wrong SIM PIN for the last time.
+ Displayed in a dialog box. -->
+ <string name="kg_password_wrong_pin_code_pukked">Incorrect SIM PIN code you must now contact your carrier to unlock your device.</string>
+ <!-- Instructions telling the user that they entered the wrong SIM PIN while trying
+ to unlock the keyguard. Displayed in a dialog box. -->
+ <plurals name="kg_password_wrong_pin_code">
+ <item quantity="one">Incorrect SIM PIN code, you have <xliff:g id="number">%d</xliff:g> remaining attempt before you must contact your carrier to unlock your device.</item>
+ <item quantity="other">Incorrect SIM PIN code, you have <xliff:g id="number">%d</xliff:g> remaining attempts.</item>
+ </plurals>
+
+ <!-- Instructions telling the user that they have exhausted SIM PUK retries and the SIM is now unusable.
+ Displayed in a dialog box. -->
+ <string name="kg_password_wrong_puk_code_dead">SIM is unusable. Contact your carrier.</string>
+ <!-- Instructions telling the user that they entered the wrong puk while trying
+ to unlock the keyguard. Displayed in a dialog box. -->
+ <plurals name="kg_password_wrong_puk_code">
+ <item quantity="one">Incorrect SIM PUK code, you have <xliff:g id="number">%d</xliff:g> remaining attempt before SIM becomes permanently unusable.</item>
+ <item quantity="other">Incorrect SIM PUK code, you have <xliff:g id="number">%d</xliff:g> remaining attempts before SIM becomes permanently unusable.</item>
+ </plurals>
+ <!-- Instructions telling the user that the operation to unlock the keyguard
+ with SIM PIN failed. Displayed in one line in a large font. -->
+ <string name="kg_password_pin_failed">SIM PIN operation failed!</string>
+ <!-- Instructions telling the user that the operation to unlock the keyguard
+ with PUK failed. Displayed in one line in a large font. -->
+ <string name="kg_password_puk_failed">SIM PUK operation failed!</string>
+ <!-- Notification telling the user that the PIN1 they entered is valid -->
+ <string name="kg_pin_accepted">Code Accepted!</string>
+
<!-- Transport control strings -->
<!-- Shown on transport control of lockscreen. Pressing button goes to previous track. -->
<string name="keyguard_transport_prev_description">Previous track button</string>
diff --git a/packages/Keyguard/src/com/android/keyguard/CameraWidgetFrame.java b/packages/Keyguard/src/com/android/keyguard/CameraWidgetFrame.java
index 7d1f24f..74e6f33 100644
--- a/packages/Keyguard/src/com/android/keyguard/CameraWidgetFrame.java
+++ b/packages/Keyguard/src/com/android/keyguard/CameraWidgetFrame.java
@@ -19,7 +19,6 @@ package com.android.keyguard;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Color;
-import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
@@ -41,7 +40,7 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli
private static final String TAG = CameraWidgetFrame.class.getSimpleName();
private static final boolean DEBUG = KeyguardHostView.DEBUG;
private static final int WIDGET_ANIMATION_DURATION = 250; // ms
- private static final int WIDGET_WAIT_DURATION = 650; // ms
+ private static final int WIDGET_WAIT_DURATION = 400; // ms
private static final int RECOVERY_DELAY = 1000; // ms
interface Callbacks {
@@ -68,6 +67,7 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli
private FixedSizeFrameLayout mPreview;
private View mFullscreenPreview;
private View mFakeNavBar;
+ private boolean mUseFastTransition;
private final Runnable mTransitionToCameraRunnable = new Runnable() {
@Override
@@ -243,11 +243,12 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli
final float pvTransX = pvWidth < thisWidth ? (thisWidth - pvWidth) / 2 : 0;
final float pvTransY = pvHeight < thisHeight ? (thisHeight - pvHeight) / 2 : 0;
- mPreview.setPivotX(0);
+ final boolean isRtl = mPreview.getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+ mPreview.setPivotX(isRtl ? mPreview.width : 0);
mPreview.setPivotY(0);
mPreview.setScaleX(pvScale);
mPreview.setScaleY(pvScale);
- mPreview.setTranslationX(pvTransX);
+ mPreview.setTranslationX((isRtl ? -1 : 1) * pvTransX);
mPreview.setTranslationY(pvTransY);
mRenderedSize.set(width, height);
@@ -417,7 +418,8 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli
private void rescheduleTransitionToCamera() {
if (DEBUG) Log.d(TAG, "rescheduleTransitionToCamera at " + SystemClock.uptimeMillis());
mHandler.removeCallbacks(mTransitionToCameraRunnable);
- mHandler.postDelayed(mTransitionToCameraRunnable, WIDGET_WAIT_DURATION);
+ final long duration = mUseFastTransition ? 0 : WIDGET_WAIT_DURATION;
+ mHandler.postDelayed(mTransitionToCameraRunnable, duration);
}
private void cancelTransitionToCamera() {
@@ -512,4 +514,8 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli
if (DEBUG) Log.d(TAG, "setInsets: " + insets);
mInsets.set(insets);
}
+
+ public void setUseFastTransition(boolean useFastTransition) {
+ mUseFastTransition = useFastTransition;
+ }
}
diff --git a/packages/Keyguard/src/com/android/keyguard/CarrierText.java b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
index c33f174..88558cd 100644
--- a/packages/Keyguard/src/com/android/keyguard/CarrierText.java
+++ b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
@@ -17,14 +17,18 @@
package com.android.keyguard;
import android.content.Context;
+import android.text.method.SingleLineTransformationMethod;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.view.View;
import android.widget.TextView;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.IccCardConstants.State;
import com.android.internal.widget.LockPatternUtils;
+import java.util.Locale;
+
public class CarrierText extends TextView {
private static CharSequence mSeparator;
@@ -77,6 +81,8 @@ public class CarrierText extends TextView {
public CarrierText(Context context, AttributeSet attrs) {
super(context, attrs);
mLockPatternUtils = new LockPatternUtils(mContext);
+ boolean useAllCaps = mContext.getResources().getBoolean(R.bool.kg_use_all_caps);
+ setTransformationMethod(new CarrierTextTransformationMethod(mContext, useAllCaps));
}
protected void updateCarrierText(State simState, CharSequence plmn, CharSequence spn) {
@@ -258,4 +264,25 @@ public class CarrierText extends TextView {
return mContext.getText(carrierHelpTextId);
}
+
+ private class CarrierTextTransformationMethod extends SingleLineTransformationMethod {
+ private final Locale mLocale;
+ private final boolean mAllCaps;
+
+ public CarrierTextTransformationMethod(Context context, boolean allCaps) {
+ mLocale = context.getResources().getConfiguration().locale;
+ mAllCaps = allCaps;
+ }
+
+ @Override
+ public CharSequence getTransformation(CharSequence source, View view) {
+ source = super.getTransformation(source, view);
+
+ if (mAllCaps && source != null) {
+ source = source.toString().toUpperCase(mLocale);
+ }
+
+ return source;
+ }
+ }
}
diff --git a/packages/Keyguard/src/com/android/keyguard/ChallengeLayout.java b/packages/Keyguard/src/com/android/keyguard/ChallengeLayout.java
index 677f1f1..2ee21ac 100644
--- a/packages/Keyguard/src/com/android/keyguard/ChallengeLayout.java
+++ b/packages/Keyguard/src/com/android/keyguard/ChallengeLayout.java
@@ -39,7 +39,7 @@ public interface ChallengeLayout {
*
* @param b true to show, false to hide
*/
- void showChallenge(boolean b);
+ void showChallenge(boolean show);
/**
* Show the bouncer challenge. This may block access to other child views.
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardActivityLauncher.java b/packages/Keyguard/src/com/android/keyguard/KeyguardActivityLauncher.java
index 9a1aa5b..0a915ea 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardActivityLauncher.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardActivityLauncher.java
@@ -99,6 +99,11 @@ public abstract class KeyguardActivityLauncher {
public void launchCamera(Handler worker, Runnable onSecureCameraStarted) {
LockPatternUtils lockPatternUtils = getLockPatternUtils();
+
+ // Workaround to avoid camera release/acquisition race when resuming face unlock
+ // after showing lockscreen camera (bug 11063890).
+ KeyguardUpdateMonitor.getInstance(getContext()).setAlternateUnlockEnabled(false);
+
if (lockPatternUtils.isSecure()) {
// Launch the secure version of the camera
if (wouldLaunchResolverActivity(SECURE_CAMERA_INTENT)) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardDisplayManager.java
new file mode 100644
index 0000000..6bcbd6c
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2013 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.keyguard;
+
+import android.app.Presentation;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnDismissListener;
+import android.graphics.Point;
+import android.media.MediaRouter;
+import android.media.MediaRouter.RouteInfo;
+import android.os.Bundle;
+import android.util.Slog;
+import android.view.Display;
+import android.view.View;
+import android.view.WindowManager;
+
+public class KeyguardDisplayManager {
+ protected static final String TAG = "KeyguardDisplayManager";
+ private static boolean DEBUG = KeyguardViewMediator.DEBUG;
+ Presentation mPresentation;
+ private MediaRouter mMediaRouter;
+ private Context mContext;
+ private boolean mShowing;
+
+ KeyguardDisplayManager(Context context) {
+ mContext = context;
+ mMediaRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE);
+ }
+
+ void show() {
+ if (!mShowing) {
+ if (DEBUG) Slog.v(TAG, "show");
+ mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
+ mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
+ updateDisplays(true);
+ }
+ mShowing = true;
+ }
+
+ void hide() {
+ if (mShowing) {
+ if (DEBUG) Slog.v(TAG, "hide");
+ mMediaRouter.removeCallback(mMediaRouterCallback);
+ updateDisplays(false);
+ }
+ mShowing = false;
+ }
+
+ private final MediaRouter.SimpleCallback mMediaRouterCallback =
+ new MediaRouter.SimpleCallback() {
+ @Override
+ public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
+ if (DEBUG) Slog.d(TAG, "onRouteSelected: type=" + type + ", info=" + info);
+ updateDisplays(mShowing);
+ }
+
+ @Override
+ public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
+ if (DEBUG) Slog.d(TAG, "onRouteUnselected: type=" + type + ", info=" + info);
+ updateDisplays(mShowing);
+ }
+
+ @Override
+ public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
+ if (DEBUG) Slog.d(TAG, "onRoutePresentationDisplayChanged: info=" + info);
+ updateDisplays(mShowing);
+ }
+ };
+
+ private OnDismissListener mOnDismissListener = new OnDismissListener() {
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ mPresentation = null;
+ }
+ };
+
+ protected void updateDisplays(boolean showing) {
+ if (showing) {
+ MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(
+ MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
+ boolean useDisplay = route != null
+ && route.getPlaybackType() == MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE;
+ Display presentationDisplay = useDisplay ? route.getPresentationDisplay() : null;
+
+ if (mPresentation != null && mPresentation.getDisplay() != presentationDisplay) {
+ if (DEBUG) Slog.v(TAG, "Display gone: " + mPresentation.getDisplay());
+ mPresentation.dismiss();
+ mPresentation = null;
+ }
+
+ if (mPresentation == null && presentationDisplay != null) {
+ if (DEBUG) Slog.i(TAG, "Keyguard enabled on display: " + presentationDisplay);
+ mPresentation = new KeyguardPresentation(mContext, presentationDisplay);
+ mPresentation.setOnDismissListener(mOnDismissListener);
+ try {
+ mPresentation.show();
+ } catch (WindowManager.InvalidDisplayException ex) {
+ Slog.w(TAG, "Invalid display:", ex);
+ mPresentation = null;
+ }
+ }
+ } else {
+ if (mPresentation != null) {
+ mPresentation.dismiss();
+ mPresentation = null;
+ }
+ }
+ }
+
+ private final static class KeyguardPresentation extends Presentation {
+ private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
+ private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
+ private View mClock;
+ private int mUsableWidth;
+ private int mUsableHeight;
+ private int mMarginTop;
+ private int mMarginLeft;
+ Runnable mMoveTextRunnable = new Runnable() {
+ @Override
+ public void run() {
+ int x = mMarginLeft + (int) (Math.random() * (mUsableWidth - mClock.getWidth()));
+ int y = mMarginTop + (int) (Math.random() * (mUsableHeight - mClock.getHeight()));
+ mClock.setTranslationX(x);
+ mClock.setTranslationY(y);
+ mClock.postDelayed(mMoveTextRunnable, MOVE_CLOCK_TIMEOUT);
+ }
+ };
+
+ public KeyguardPresentation(Context context, Display display) {
+ super(context, display);
+ getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ }
+
+ public void onDetachedFromWindow() {
+ mClock.removeCallbacks(mMoveTextRunnable);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Point p = new Point();
+ getDisplay().getSize(p);
+ mUsableWidth = VIDEO_SAFE_REGION * p.x/100;
+ mUsableHeight = VIDEO_SAFE_REGION * p.y/100;
+ mMarginLeft = (100 - VIDEO_SAFE_REGION) * p.x / 200;
+ mMarginTop = (100 - VIDEO_SAFE_REGION) * p.y / 200;
+
+ setContentView(R.layout.keyguard_presentation);
+ mClock = findViewById(R.id.clock);
+
+ // Avoid screen burn in
+ mClock.post(mMoveTextRunnable);
+ }
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index e94cf18..1bae9b8 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -16,6 +16,10 @@
package com.android.keyguard;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.keyguard.KeyguardUpdateMonitor.DisplayClientState;
+
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -51,9 +55,6 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.RemoteViews.OnClickHandler;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.keyguard.KeyguardUpdateMonitor.DisplayClientState;
import java.io.File;
import java.lang.ref.WeakReference;
@@ -217,7 +218,7 @@ public class KeyguardHostView extends KeyguardViewBase {
mTransportState = (dcs.clearing ? TRANSPORT_GONE :
(isMusicPlaying(dcs.playbackState) ? TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE));
- if (DEBUG) Log.v(TAG, "Initial transport state: "
+ if (DEBUGXPORT) Log.v(TAG, "Initial transport state: "
+ mTransportState + ", pbstate=" + dcs.playbackState);
}
@@ -279,7 +280,7 @@ public class KeyguardHostView extends KeyguardViewBase {
if (newState != mTransportState) {
mTransportState = newState;
if (DEBUGXPORT) Log.v(TAG, "update widget: transport state changed");
- KeyguardHostView.this.postShowAppropriateWidgetPage();
+ KeyguardHostView.this.post(mSwitchPageRunnable);
}
}
@Override
@@ -291,7 +292,7 @@ public class KeyguardHostView extends KeyguardViewBase {
if (newState != mTransportState) {
mTransportState = newState;
if (DEBUGXPORT) Log.v(TAG, "update widget: play state changed");
- KeyguardHostView.this.postShowAppropriateWidgetPage();
+ KeyguardHostView.this.post(mSwitchPageRunnable);
}
}
}
@@ -495,7 +496,6 @@ public class KeyguardHostView extends KeyguardViewBase {
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- removeCallbacks(mSwitchPageRunnable);
mAppWidgetHost.stopListening();
KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallbacks);
}
@@ -1369,7 +1369,7 @@ public class KeyguardHostView extends KeyguardViewBase {
}
}
- Runnable mSwitchPageRunnable = new Runnable() {
+ private final Runnable mSwitchPageRunnable = new Runnable() {
@Override
public void run() {
showAppropriateWidgetPage();
@@ -1438,7 +1438,7 @@ public class KeyguardHostView extends KeyguardViewBase {
mAppWidgetToShow = ss.appWidgetToShow;
setInsets(ss.insets);
if (DEBUG) Log.d(TAG, "onRestoreInstanceState, transport=" + mTransportState);
- postShowAppropriateWidgetPage();
+ mSwitchPageRunnable.run();
}
@Override
@@ -1471,20 +1471,22 @@ public class KeyguardHostView extends KeyguardViewBase {
}
}
- void showAppropriateWidgetPage() {
- int state = mTransportState;
- ensureTransportPresentOrRemoved(state);
- if (mAppWidgetContainer.isLayoutRequested()) {
- postShowAppropriateWidgetPage();
- return;
+ private void showAppropriateWidgetPage() {
+ final int state = mTransportState;
+ final boolean transportAdded = ensureTransportPresentOrRemoved(state);
+ final int pageToShow = getAppropriateWidgetPage(state);
+ if (!transportAdded) {
+ mAppWidgetContainer.setCurrentPage(pageToShow);
+ } else if (state == TRANSPORT_VISIBLE) {
+ // If the transport was just added, we need to wait for layout to happen before
+ // we can set the current page.
+ post(new Runnable() {
+ @Override
+ public void run() {
+ mAppWidgetContainer.setCurrentPage(pageToShow);
+ }
+ });
}
- int pageToShow = getAppropriateWidgetPage(state);
- mAppWidgetContainer.setCurrentPage(pageToShow);
- }
-
- void postShowAppropriateWidgetPage() {
- removeCallbacks(mSwitchPageRunnable);
- post(mSwitchPageRunnable);
}
/**
@@ -1508,12 +1510,11 @@ public class KeyguardHostView extends KeyguardViewBase {
*
* @param state
*/
- private void ensureTransportPresentOrRemoved(int state) {
+ private boolean ensureTransportPresentOrRemoved(int state) {
final boolean showing = getWidgetPosition(R.id.keyguard_transport_control) != -1;
final boolean visible = state == TRANSPORT_VISIBLE;
final boolean shouldBeVisible = state == TRANSPORT_INVISIBLE && isMusicPlaying(state);
if (!showing && (visible || shouldBeVisible)) {
- if (DEBUGXPORT) Log.v(TAG, "add transport");
// insert to left of camera if it exists, otherwise after right-most widget
int lastWidget = mAppWidgetContainer.getChildCount() - 1;
int position = 0; // handle no widget case
@@ -1521,13 +1522,16 @@ public class KeyguardHostView extends KeyguardViewBase {
position = mAppWidgetContainer.isCameraPage(lastWidget) ?
lastWidget : lastWidget + 1;
}
+ if (DEBUGXPORT) Log.v(TAG, "add transport at " + position);
mAppWidgetContainer.addWidget(getOrCreateTransportControl(), position);
+ return true;
} else if (showing && state == TRANSPORT_GONE) {
if (DEBUGXPORT) Log.v(TAG, "remove transport");
mAppWidgetContainer.removeWidget(getOrCreateTransportControl());
mTransportControl = null;
KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground(null);
}
+ return false;
}
private CameraWidgetFrame findCameraPage() {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
index 69075ec..751572c 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
@@ -177,6 +177,7 @@ class KeyguardMessageArea extends TextView {
public KeyguardMessageArea(Context context, AttributeSet attrs) {
super(context, attrs);
+ setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
mLockPatternUtils = new LockPatternUtils(context);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardService.java b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
index d7c5fe2..36b2446 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
@@ -141,6 +141,10 @@ public class KeyguardService extends Service {
checkPermission();
mKeyguardViewMediator.launchCamera();
}
+ public void onBootCompleted() {
+ checkPermission();
+ mKeyguardViewMediator.onBootCompleted();
+ }
};
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
index 5059407..9accbb4 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
@@ -17,11 +17,16 @@
package com.android.keyguard;
import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.PhoneConstants;
import android.content.Context;
+import android.content.DialogInterface;
import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.app.ProgressDialog;
+import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.Editable;
@@ -30,6 +35,7 @@ import android.text.TextWatcher;
import android.text.method.DigitsKeyListener;
import android.util.AttributeSet;
import android.view.View;
+import android.util.Log;
import android.view.WindowManager;
import android.widget.TextView.OnEditorActionListener;
@@ -38,9 +44,14 @@ import android.widget.TextView.OnEditorActionListener;
*/
public class KeyguardSimPinView extends KeyguardAbsKeyInputView
implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
+ private static final String LOG_TAG = "KeyguardSimPinView";
+ private static final boolean DEBUG = KeyguardViewMediator.DEBUG;
+ public static final String TAG = "KeyguardSimPinView";
private ProgressDialog mSimUnlockProgressDialog = null;
- private volatile boolean mSimCheckInProgress;
+ private CheckSimPin mCheckSimPinThread;
+
+ private AlertDialog mRemainingAttemptsDialog;
public KeyguardSimPinView(Context context) {
this(context, null);
@@ -55,6 +66,23 @@ public class KeyguardSimPinView extends KeyguardAbsKeyInputView
mPasswordEntry.setEnabled(true);
}
+ private String getPinPasswordErrorMessage(int attemptsRemaining) {
+ String displayMessage;
+
+ if (attemptsRemaining == 0) {
+ displayMessage = getContext().getString(R.string.kg_password_wrong_pin_code_pukked);
+ } else if (attemptsRemaining > 0) {
+ displayMessage = getContext().getResources()
+ .getQuantityString(R.plurals.kg_password_wrong_pin_code, attemptsRemaining,
+ attemptsRemaining);
+ } else {
+ displayMessage = getContext().getString(R.string.kg_password_pin_failed);
+ }
+ if (DEBUG) Log.d(LOG_TAG, "getPinPasswordErrorMessage:"
+ + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
+ return displayMessage;
+ }
+
@Override
protected boolean shouldLockout(long deadline) {
// SIM PIN doesn't have a timed lockout
@@ -109,6 +137,8 @@ public class KeyguardSimPinView extends KeyguardAbsKeyInputView
| InputType.TYPE_NUMBER_VARIATION_PASSWORD);
mPasswordEntry.requestFocus();
+
+ mSecurityMessageDisplay.setTimeout(0); // don't show ownerinfo/charging status by default
}
@Override
@@ -135,22 +165,25 @@ public class KeyguardSimPinView extends KeyguardAbsKeyInputView
mPin = pin;
}
- abstract void onSimCheckResponse(boolean success);
+ abstract void onSimCheckResponse(final int result, final int attemptsRemaining);
@Override
public void run() {
try {
- final boolean result = ITelephony.Stub.asInterface(ServiceManager
- .checkService("phone")).supplyPin(mPin);
+ Log.v(TAG, "call supplyPinReportResult()");
+ final int[] result = ITelephony.Stub.asInterface(ServiceManager
+ .checkService("phone")).supplyPinReportResult(mPin);
+ Log.v(TAG, "supplyPinReportResult returned: " + result[0] + " " + result[1]);
post(new Runnable() {
public void run() {
- onSimCheckResponse(result);
+ onSimCheckResponse(result[0], result[1]);
}
});
} catch (RemoteException e) {
+ Log.e(TAG, "RemoteException for supplyPinReportResult:", e);
post(new Runnable() {
public void run() {
- onSimCheckResponse(false);
+ onSimCheckResponse(PhoneConstants.PIN_GENERAL_FAILURE, -1);
}
});
}
@@ -164,14 +197,28 @@ public class KeyguardSimPinView extends KeyguardAbsKeyInputView
mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
mSimUnlockProgressDialog.setIndeterminate(true);
mSimUnlockProgressDialog.setCancelable(false);
- if (!(mContext instanceof Activity)) {
- mSimUnlockProgressDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- }
+ mSimUnlockProgressDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
}
return mSimUnlockProgressDialog;
}
+ private Dialog getSimRemainingAttemptsDialog(int remaining) {
+ String msg = getPinPasswordErrorMessage(remaining);
+ if (mRemainingAttemptsDialog == null) {
+ Builder builder = new AlertDialog.Builder(mContext);
+ builder.setMessage(msg);
+ builder.setCancelable(false);
+ builder.setNeutralButton(R.string.ok, null);
+ mRemainingAttemptsDialog = builder.create();
+ mRemainingAttemptsDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ } else {
+ mRemainingAttemptsDialog.setMessage(msg);
+ }
+ return mRemainingAttemptsDialog;
+ }
+
@Override
protected void verifyPasswordAndUnlock() {
String entry = mPasswordEntry.getText().toString();
@@ -186,31 +233,45 @@ public class KeyguardSimPinView extends KeyguardAbsKeyInputView
getSimUnlockProgressDialog().show();
- if (!mSimCheckInProgress) {
- mSimCheckInProgress = true; // there should be only one
- new CheckSimPin(mPasswordEntry.getText().toString()) {
- void onSimCheckResponse(final boolean success) {
+ if (mCheckSimPinThread == null) {
+ mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText().toString()) {
+ void onSimCheckResponse(final int result, final int attemptsRemaining) {
post(new Runnable() {
public void run() {
if (mSimUnlockProgressDialog != null) {
mSimUnlockProgressDialog.hide();
}
- if (success) {
- // before closing the keyguard, report back that the sim is unlocked
- // so it knows right away.
+ if (result == PhoneConstants.PIN_RESULT_SUCCESS) {
KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked();
mCallback.dismiss(true);
} else {
- mSecurityMessageDisplay.setMessage
- (R.string.kg_password_wrong_pin_code, true);
+ if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) {
+ if (attemptsRemaining <= 2) {
+ // this is getting critical - show dialog
+ getSimRemainingAttemptsDialog(attemptsRemaining).show();
+ } else {
+ // show message
+ mSecurityMessageDisplay.setMessage(
+ getPinPasswordErrorMessage(attemptsRemaining), true);
+ }
+ } else {
+ // "PIN operation failed!" - no idea what this was and no way to
+ // find out. :/
+ mSecurityMessageDisplay.setMessage(getContext().getString(
+ R.string.kg_password_pin_failed), true);
+ }
+ if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
+ + " CheckSimPin.onSimCheckResponse: " + result
+ + " attemptsRemaining=" + attemptsRemaining);
mPasswordEntry.setText("");
}
mCallback.userActivity(0);
- mSimCheckInProgress = false;
+ mCheckSimPinThread = null;
}
});
}
- }.start();
+ };
+ mCheckSimPinThread.start();
}
}
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
index 2ae4cc7..6e9e83e 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
@@ -16,10 +16,10 @@
package com.android.keyguard;
-import com.android.internal.telephony.ITelephony;
-
import android.content.Context;
+import android.animation.AnimatorSet.Builder;
import android.app.Activity;
+import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.os.RemoteException;
@@ -29,21 +29,30 @@ import android.text.InputType;
import android.text.TextWatcher;
import android.text.method.DigitsKeyListener;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView.OnEditorActionListener;
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.PhoneConstants;
+
+
/**
* Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier.
*/
public class KeyguardSimPukView extends KeyguardAbsKeyInputView
implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
+ private static final String LOG_TAG = "KeyguardSimPukView";
+ private static final boolean DEBUG = KeyguardViewMediator.DEBUG;
+ public static final String TAG = "KeyguardSimPukView";
private ProgressDialog mSimUnlockProgressDialog = null;
- private volatile boolean mCheckInProgress;
+ private CheckSimPuk mCheckSimPukThread;
private String mPukText;
private String mPinText;
private StateMachine mStateMachine = new StateMachine();
+ private AlertDialog mRemainingAttemptsDialog;
private class StateMachine {
final int ENTER_PUK = 0;
@@ -93,6 +102,23 @@ public class KeyguardSimPukView extends KeyguardAbsKeyInputView
}
}
+ private String getPukPasswordErrorMessage(int attemptsRemaining) {
+ String displayMessage;
+
+ if (attemptsRemaining == 0) {
+ displayMessage = getContext().getString(R.string.kg_password_wrong_puk_code_dead);
+ } else if (attemptsRemaining > 0) {
+ displayMessage = getContext().getResources()
+ .getQuantityString(R.plurals.kg_password_wrong_puk_code, attemptsRemaining,
+ attemptsRemaining);
+ } else {
+ displayMessage = getContext().getString(R.string.kg_password_puk_failed);
+ }
+ if (DEBUG) Log.d(LOG_TAG, "getPukPasswordErrorMessage:"
+ + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
+ return displayMessage;
+ }
+
public KeyguardSimPukView(Context context) {
this(context, null);
}
@@ -190,23 +216,25 @@ public class KeyguardSimPukView extends KeyguardAbsKeyInputView
mPin = pin;
}
- abstract void onSimLockChangedResponse(boolean success);
+ abstract void onSimLockChangedResponse(final int result, final int attemptsRemaining);
@Override
public void run() {
try {
- final boolean result = ITelephony.Stub.asInterface(ServiceManager
- .checkService("phone")).supplyPuk(mPuk, mPin);
-
+ Log.v(TAG, "call supplyPukReportResult()");
+ final int[] result = ITelephony.Stub.asInterface(ServiceManager
+ .checkService("phone")).supplyPukReportResult(mPuk, mPin);
+ Log.v(TAG, "supplyPukReportResult returned: " + result[0] + " " + result[1]);
post(new Runnable() {
public void run() {
- onSimLockChangedResponse(result);
+ onSimLockChangedResponse(result[0], result[1]);
}
});
} catch (RemoteException e) {
+ Log.e(TAG, "RemoteException for supplyPukReportResult:", e);
post(new Runnable() {
public void run() {
- onSimLockChangedResponse(false);
+ onSimLockChangedResponse(PhoneConstants.PIN_GENERAL_FAILURE, -1);
}
});
}
@@ -228,6 +256,22 @@ public class KeyguardSimPukView extends KeyguardAbsKeyInputView
return mSimUnlockProgressDialog;
}
+ private Dialog getPukRemainingAttemptsDialog(int remaining) {
+ String msg = getPukPasswordErrorMessage(remaining);
+ if (mRemainingAttemptsDialog == null) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+ builder.setMessage(msg);
+ builder.setCancelable(false);
+ builder.setNeutralButton(R.string.ok, null);
+ mRemainingAttemptsDialog = builder.create();
+ mRemainingAttemptsDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ } else {
+ mRemainingAttemptsDialog.setMessage(msg);
+ }
+ return mRemainingAttemptsDialog;
+ }
+
private boolean checkPuk() {
// make sure the puk is at least 8 digits long.
if (mPasswordEntry.getText().length() >= 8) {
@@ -254,26 +298,42 @@ public class KeyguardSimPukView extends KeyguardAbsKeyInputView
private void updateSim() {
getSimUnlockProgressDialog().show();
- if (!mCheckInProgress) {
- mCheckInProgress = true;
- new CheckSimPuk(mPukText, mPinText) {
- void onSimLockChangedResponse(final boolean success) {
+ if (mCheckSimPukThread == null) {
+ mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText) {
+ void onSimLockChangedResponse(final int result, final int attemptsRemaining) {
post(new Runnable() {
public void run() {
if (mSimUnlockProgressDialog != null) {
mSimUnlockProgressDialog.hide();
}
- if (success) {
+ if (result == PhoneConstants.PIN_RESULT_SUCCESS) {
+ KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked();
mCallback.dismiss(true);
} else {
+ if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) {
+ if (attemptsRemaining <= 2) {
+ // this is getting critical - show dialog
+ getPukRemainingAttemptsDialog(attemptsRemaining).show();
+ } else {
+ // show message
+ mSecurityMessageDisplay.setMessage(
+ getPukPasswordErrorMessage(attemptsRemaining), true);
+ }
+ } else {
+ mSecurityMessageDisplay.setMessage(getContext().getString(
+ R.string.kg_password_puk_failed), true);
+ }
+ if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
+ + " UpdateSim.onSimCheckResponse: "
+ + " attemptsRemaining=" + attemptsRemaining);
mStateMachine.reset();
- mSecurityMessageDisplay.setMessage(R.string.kg_invalid_puk, true);
}
- mCheckInProgress = false;
+ mCheckSimPukThread = null;
}
});
}
- }.start();
+ };
+ mCheckSimPukThread.start();
}
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index d933275..0bfee38 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -98,26 +98,13 @@ public class KeyguardStatusView extends GridLayout {
}
protected void refresh() {
- Resources res = mContext.getResources();
- Locale locale = Locale.getDefault();
- final String dateFormat = DateFormat.getBestDateTimePattern(locale,
- res.getString(R.string.abbrev_wday_month_day_no_year));
-
- mDateView.setFormat24Hour(dateFormat);
- mDateView.setFormat12Hour(dateFormat);
-
- // 12-hour clock.
- // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
- // format. The following code removes the AM/PM indicator if we didn't want it.
- final String clock12skel = res.getString(R.string.clock_12hr_format);
- String clock12hr = DateFormat.getBestDateTimePattern(locale, clock12skel);
- clock12hr = clock12skel.contains("a") ? clock12hr : clock12hr.replaceAll("a", "").trim();
- mClockView.setFormat12Hour(clock12hr);
-
- // 24-hour clock
- final String clock24skel = res.getString(R.string.clock_24hr_format);
- final String clock24hr = DateFormat.getBestDateTimePattern(locale, clock24skel);
- mClockView.setFormat24Hour(clock24hr);
+ Patterns.update(mContext);
+
+ mDateView.setFormat24Hour(Patterns.dateView);
+ mDateView.setFormat12Hour(Patterns.dateView);
+
+ mClockView.setFormat12Hour(Patterns.clockView12);
+ mClockView.setFormat24Hour(Patterns.clockView24);
refreshAlarmStatus();
}
@@ -149,4 +136,35 @@ public class KeyguardStatusView extends GridLayout {
return LockPatternUtils.ID_DEFAULT_STATUS_WIDGET;
}
+ // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
+ // This is an optimization to ensure we only recompute the patterns when the inputs change.
+ private static final class Patterns {
+ static String dateView;
+ static String clockView12;
+ static String clockView24;
+ static String cacheKey;
+
+ static void update(Context context) {
+ final Locale locale = Locale.getDefault();
+ final Resources res = context.getResources();
+ final String dateViewSkel = res.getString(R.string.abbrev_wday_month_day_no_year);
+ final String clockView12Skel = res.getString(R.string.clock_12hr_format);
+ final String clockView24Skel = res.getString(R.string.clock_24hr_format);
+ final String key = locale.toString() + dateViewSkel + clockView12Skel + clockView24Skel;
+ if (key.equals(cacheKey)) return;
+
+ dateView = DateFormat.getBestDateTimePattern(locale, dateViewSkel);
+
+ clockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel);
+ // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
+ // format. The following code removes the AM/PM indicator if we didn't want it.
+ if (!clockView12Skel.contains("a")) {
+ clockView12 = clockView12.replaceAll("a", "").trim();
+ }
+
+ clockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel);
+
+ cacheKey = key;
+ }
+ }
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java
index a48f23e..349078f 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java
@@ -60,12 +60,12 @@ import java.util.TimeZone;
*/
public class KeyguardTransportControlView extends FrameLayout {
- private static final int DISPLAY_TIMEOUT_MS = 5000; // 5s
private static final int RESET_TO_METADATA_DELAY = 5000;
protected static final boolean DEBUG = false;
protected static final String TAG = "TransportControlView";
private static final boolean ANIMATE_TRANSITIONS = true;
+ protected static final long QUIESCENT_PLAYBACK_FACTOR = 1000;
private ViewGroup mMetadataContainer;
private ViewGroup mInfoContainer;
@@ -75,7 +75,7 @@ public class KeyguardTransportControlView extends FrameLayout {
private View mTransientSeek;
private SeekBar mTransientSeekBar;
private TextView mTransientSeekTimeElapsed;
- private TextView mTransientSeekTimeRemaining;
+ private TextView mTransientSeekTimeTotal;
private ImageView mBtnPrev;
private ImageView mBtnPlay;
@@ -89,9 +89,10 @@ public class KeyguardTransportControlView extends FrameLayout {
private ImageView mBadge;
private boolean mSeekEnabled;
- private boolean mUserSeeking;
private java.text.DateFormat mFormat;
+ private Date mTempDate = new Date();
+
/**
* The metadata which should be populated into the view once we've been attached
*/
@@ -108,18 +109,25 @@ public class KeyguardTransportControlView extends FrameLayout {
@Override
public void onClientPlaybackStateUpdate(int state) {
- setSeekBarsEnabled(false);
updatePlayPauseState(state);
}
@Override
public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs,
long currentPosMs, float speed) {
- setSeekBarsEnabled(mMetadata != null && mMetadata.duration > 0);
updatePlayPauseState(state);
if (DEBUG) Log.d(TAG, "onClientPlaybackStateUpdate(state=" + state +
", stateChangeTimeMs=" + stateChangeTimeMs + ", currentPosMs=" + currentPosMs +
", speed=" + speed + ")");
+
+ removeCallbacks(mUpdateSeekBars);
+ // Since the music client may be responding to historical events that cause the
+ // playback state to change dramatically, wait until things become quiescent before
+ // resuming automatic scrub position update.
+ if (mTransientSeek.getVisibility() == View.VISIBLE
+ && playbackPositionShouldMove(mCurrentPlayState)) {
+ postDelayed(mUpdateSeekBars, QUIESCENT_PLAYBACK_FACTOR);
+ }
}
@Override
@@ -133,14 +141,21 @@ public class KeyguardTransportControlView extends FrameLayout {
}
};
- private final Runnable mUpdateSeekBars = new Runnable() {
+ private class UpdateSeekBarRunnable implements Runnable {
public void run() {
- if (updateSeekBars()) {
+ boolean seekAble = updateOnce();
+ if (seekAble) {
+ removeCallbacks(this);
postDelayed(this, 1000);
}
}
+ public boolean updateOnce() {
+ return updateSeekBars();
+ }
};
+ private final UpdateSeekBarRunnable mUpdateSeekBars = new UpdateSeekBarRunnable();
+
private final Runnable mResetToMetadata = new Runnable() {
public void run() {
resetToMetadata();
@@ -159,6 +174,7 @@ public class KeyguardTransportControlView extends FrameLayout {
}
if (keyCode != -1) {
sendMediaButtonClick(keyCode);
+ delayResetToMetadata(); // if the scrub bar is showing, keep showing it.
}
}
};
@@ -173,25 +189,67 @@ public class KeyguardTransportControlView extends FrameLayout {
}
};
+ // This class is here to throttle scrub position updates to the music client
+ class FutureSeekRunnable implements Runnable {
+ private int mProgress;
+ private boolean mPending;
+
+ public void run() {
+ scrubTo(mProgress);
+ mPending = false;
+ }
+
+ void setProgress(int progress) {
+ mProgress = progress;
+ if (!mPending) {
+ mPending = true;
+ postDelayed(this, 30);
+ }
+ }
+ };
+
+ // This is here because RemoteControlClient's method isn't visible :/
+ private final static boolean playbackPositionShouldMove(int playstate) {
+ switch(playstate) {
+ case RemoteControlClient.PLAYSTATE_STOPPED:
+ case RemoteControlClient.PLAYSTATE_PAUSED:
+ case RemoteControlClient.PLAYSTATE_BUFFERING:
+ case RemoteControlClient.PLAYSTATE_ERROR:
+ case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
+ case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
+ return false;
+ case RemoteControlClient.PLAYSTATE_PLAYING:
+ case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
+ case RemoteControlClient.PLAYSTATE_REWINDING:
+ default:
+ return true;
+ }
+ }
+
+ private final FutureSeekRunnable mFutureSeekRunnable = new FutureSeekRunnable();
+
private final SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener =
new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
- scrubTo(progress);
+ mFutureSeekRunnable.setProgress(progress);
delayResetToMetadata();
+ mTempDate.setTime(progress);
+ mTransientSeekTimeElapsed.setText(mFormat.format(mTempDate));
+ } else {
+ updateSeekDisplay();
}
- updateSeekDisplay();
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
- mUserSeeking = true;
+ delayResetToMetadata();
+ removeCallbacks(mUpdateSeekBars); // don't update during user interaction
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
- mUserSeeking = false;
}
};
@@ -204,10 +262,10 @@ public class KeyguardTransportControlView extends FrameLayout {
= new KeyguardUpdateMonitorCallback() {
public void onScreenTurnedOff(int why) {
setEnableMarquee(false);
- };
+ }
public void onScreenTurnedOn() {
setEnableMarquee(true);
- };
+ }
};
public KeyguardTransportControlView(Context context, AttributeSet attrs) {
@@ -243,18 +301,11 @@ public class KeyguardTransportControlView extends FrameLayout {
if (enabled == mSeekEnabled) return;
mSeekEnabled = enabled;
- if (mTransientSeek.getVisibility() == VISIBLE) {
+ if (mTransientSeek.getVisibility() == VISIBLE && !enabled) {
mTransientSeek.setVisibility(INVISIBLE);
mMetadataContainer.setVisibility(VISIBLE);
- mUserSeeking = false;
cancelResetToMetadata();
}
- if (enabled) {
- mUpdateSeekBars.run();
- postDelayed(mUpdateSeekBars, 1000);
- } else {
- removeCallbacks(mUpdateSeekBars);
- }
}
public void setTransportControlCallback(KeyguardHostView.TransportControlCallback
@@ -280,7 +331,7 @@ public class KeyguardTransportControlView extends FrameLayout {
mTransientSeekBar = (SeekBar) findViewById(R.id.transient_seek_bar);
mTransientSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangeListener);
mTransientSeekTimeElapsed = (TextView) findViewById(R.id.transient_seek_time_elapsed);
- mTransientSeekTimeRemaining = (TextView) findViewById(R.id.transient_seek_time_remaining);
+ mTransientSeekTimeTotal = (TextView) findViewById(R.id.transient_seek_time_remaining);
mBtnPrev = (ImageView) findViewById(R.id.btn_prev);
mBtnPlay = (ImageView) findViewById(R.id.btn_play);
mBtnNext = (ImageView) findViewById(R.id.btn_next);
@@ -291,6 +342,8 @@ public class KeyguardTransportControlView extends FrameLayout {
}
final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
setEnableMarquee(screenOn);
+ // Allow long-press anywhere else in this view to show the seek bar
+ setOnLongClickListener(mTransportShowSeekBarListener);
}
@Override
@@ -302,6 +355,7 @@ public class KeyguardTransportControlView extends FrameLayout {
mPopulateMetadataWhenAttached = null;
}
if (DEBUG) Log.v(TAG, "Registering TCV " + this);
+ mMetadata.clear();
mAudioManager.registerRemoteController(mRemoteController);
KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitor);
}
@@ -321,10 +375,37 @@ public class KeyguardTransportControlView extends FrameLayout {
if (DEBUG) Log.v(TAG, "Unregistering TCV " + this);
mAudioManager.unregisterRemoteController(mRemoteController);
KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitor);
- mUserSeeking = false;
+ mMetadata.clear();
removeCallbacks(mUpdateSeekBars);
}
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ SavedState ss = new SavedState(super.onSaveInstanceState());
+ ss.artist = mMetadata.artist;
+ ss.trackTitle = mMetadata.trackTitle;
+ ss.albumTitle = mMetadata.albumTitle;
+ ss.duration = mMetadata.duration;
+ ss.bitmap = mMetadata.bitmap;
+ return ss;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ if (!(state instanceof SavedState)) {
+ super.onRestoreInstanceState(state);
+ return;
+ }
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+ mMetadata.artist = ss.artist;
+ mMetadata.trackTitle = ss.trackTitle;
+ mMetadata.albumTitle = ss.albumTitle;
+ mMetadata.duration = ss.duration;
+ mMetadata.bitmap = ss.bitmap;
+ populateMetadata();
+ }
+
void setBadgeIcon(Drawable bmp) {
mBadge.setImageDrawable(bmp);
@@ -395,10 +476,10 @@ public class KeyguardTransportControlView extends FrameLayout {
Log.e(TAG, "Couldn't get remote control client package icon", e);
}
setBadgeIcon(badgeIcon);
- if (!TextUtils.isEmpty(mMetadata.trackTitle)) {
- mTrackTitle.setText(mMetadata.trackTitle);
- }
- StringBuilder sb = new StringBuilder();
+ mTrackTitle.setText(!TextUtils.isEmpty(mMetadata.trackTitle)
+ ? mMetadata.trackTitle : null);
+
+ final StringBuilder sb = new StringBuilder();
if (!TextUtils.isEmpty(mMetadata.artist)) {
if (sb.length() != 0) {
sb.append(" - ");
@@ -411,7 +492,10 @@ public class KeyguardTransportControlView extends FrameLayout {
}
sb.append(mMetadata.albumTitle);
}
- mTrackArtistAlbum.setText(sb.toString());
+
+ final String trackArtistAlbum = sb.toString();
+ mTrackArtistAlbum.setText(!TextUtils.isEmpty(trackArtistAlbum) ?
+ trackArtistAlbum : null);
if (mMetadata.duration >= 0) {
setSeekBarsEnabled(true);
@@ -434,8 +518,7 @@ public class KeyguardTransportControlView extends FrameLayout {
setSeekBarsEnabled(false);
}
- KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground(
- mMetadata.bitmap);
+ KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground(mMetadata.bitmap);
final int flags = mTransportControlFlags;
setVisibilityBasedOnFlag(mBtnPrev, flags, RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS);
setVisibilityBasedOnFlag(mBtnNext, flags, RemoteControlClient.FLAG_KEY_MEDIA_NEXT);
@@ -450,15 +533,13 @@ public class KeyguardTransportControlView extends FrameLayout {
void updateSeekDisplay() {
if (mMetadata != null && mRemoteController != null && mFormat != null) {
- final long timeElapsed = mRemoteController.getEstimatedMediaPosition();
- final long duration = mMetadata.duration;
- final long remaining = duration - timeElapsed;
+ mTempDate.setTime(mRemoteController.getEstimatedMediaPosition());
+ mTransientSeekTimeElapsed.setText(mFormat.format(mTempDate));
+ mTempDate.setTime(mMetadata.duration);
+ mTransientSeekTimeTotal.setText(mFormat.format(mTempDate));
- mTransientSeekTimeElapsed.setText(mFormat.format(new Date(timeElapsed)));
- mTransientSeekTimeRemaining.setText(mFormat.format(new Date(remaining)));
-
- if (DEBUG) Log.d(TAG, "updateSeekDisplay timeElapsed=" + timeElapsed +
- " duration=" + duration + " remaining=" + remaining);
+ if (DEBUG) Log.d(TAG, "updateSeekDisplay timeElapsed=" + mTempDate +
+ " duration=" + mMetadata.duration);
}
}
@@ -470,10 +551,16 @@ public class KeyguardTransportControlView extends FrameLayout {
mTransientSeek.setVisibility(INVISIBLE);
mMetadataContainer.setVisibility(VISIBLE);
cancelResetToMetadata();
+ removeCallbacks(mUpdateSeekBars); // don't update if scrubber isn't visible
} else {
mTransientSeek.setVisibility(VISIBLE);
mMetadataContainer.setVisibility(INVISIBLE);
delayResetToMetadata();
+ if (playbackPositionShouldMove(mCurrentPlayState)) {
+ mUpdateSeekBars.run();
+ } else {
+ mUpdateSeekBars.updateOnce();
+ }
}
mTransportControlCallback.userActivity();
return true;
@@ -535,9 +622,6 @@ public class KeyguardTransportControlView extends FrameLayout {
case RemoteControlClient.PLAYSTATE_PLAYING:
imageResId = R.drawable.ic_media_pause;
imageDescId = R.string.keyguard_transport_pause_description;
- if (mSeekEnabled) {
- postDelayed(mUpdateSeekBars, 1000);
- }
break;
case RemoteControlClient.PLAYSTATE_BUFFERING:
@@ -552,10 +636,9 @@ public class KeyguardTransportControlView extends FrameLayout {
break;
}
- if (state != RemoteControlClient.PLAYSTATE_PLAYING) {
- removeCallbacks(mUpdateSeekBars);
- updateSeekBars();
- }
+ boolean clientSupportsSeek = mMetadata != null && mMetadata.duration > 0;
+ setSeekBarsEnabled(clientSupportsSeek);
+
mBtnPlay.setImageResource(imageResId);
mBtnPlay.setContentDescription(getResources().getString(imageDescId));
mCurrentPlayState = state;
@@ -563,10 +646,9 @@ public class KeyguardTransportControlView extends FrameLayout {
boolean updateSeekBars() {
final int position = (int) mRemoteController.getEstimatedMediaPosition();
+ if (DEBUG) Log.v(TAG, "Estimated time:" + position);
if (position >= 0) {
- if (!mUserSeeking) {
- mTransientSeekBar.setProgress(position);
- }
+ mTransientSeekBar.setProgress(position);
return true;
}
Log.w(TAG, "Updating seek bars; received invalid estimated media position (" +
@@ -577,6 +659,11 @@ public class KeyguardTransportControlView extends FrameLayout {
static class SavedState extends BaseSavedState {
boolean clientPresent;
+ String artist;
+ String trackTitle;
+ String albumTitle;
+ long duration;
+ Bitmap bitmap;
SavedState(Parcelable superState) {
super(superState);
@@ -584,13 +671,23 @@ public class KeyguardTransportControlView extends FrameLayout {
private SavedState(Parcel in) {
super(in);
- this.clientPresent = in.readInt() != 0;
+ clientPresent = in.readInt() != 0;
+ artist = in.readString();
+ trackTitle = in.readString();
+ albumTitle = in.readString();
+ duration = in.readLong();
+ bitmap = Bitmap.CREATOR.createFromParcel(in);
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
- out.writeInt(this.clientPresent ? 1 : 0);
+ out.writeInt(clientPresent ? 1 : 0);
+ out.writeString(artist);
+ out.writeString(trackTitle);
+ out.writeString(albumTitle);
+ out.writeLong(duration);
+ bitmap.writeToParcel(out, flags);
}
public static final Parcelable.Creator<SavedState> CREATOR
@@ -617,34 +714,4 @@ public class KeyguardTransportControlView extends FrameLayout {
public boolean providesClock() {
return false;
}
-
- private boolean wasPlayingRecently(int state, long stateChangeTimeMs) {
- switch (state) {
- case RemoteControlClient.PLAYSTATE_PLAYING:
- case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
- case RemoteControlClient.PLAYSTATE_REWINDING:
- case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
- case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
- case RemoteControlClient.PLAYSTATE_BUFFERING:
- // actively playing or about to play
- return true;
- case RemoteControlClient.PLAYSTATE_NONE:
- return false;
- case RemoteControlClient.PLAYSTATE_STOPPED:
- case RemoteControlClient.PLAYSTATE_PAUSED:
- case RemoteControlClient.PLAYSTATE_ERROR:
- // we have stopped playing, check how long ago
- if (DEBUG) {
- if ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS) {
- Log.v(TAG, "wasPlayingRecently: time < TIMEOUT was playing recently");
- } else {
- Log.v(TAG, "wasPlayingRecently: time > TIMEOUT");
- }
- }
- return ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS);
- default:
- Log.e(TAG, "Unknown playback state " + state + " in wasPlayingRecently()");
- return false;
- }
- }
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 45cd3d4..a849316 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -635,15 +635,14 @@ public class KeyguardUpdateMonitor {
* PhoneWindowManager in this case.
*/
protected void dispatchBootCompleted() {
- if (!mBootCompleted) {
- mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED);
- }
+ mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED);
}
/**
* Handle {@link #MSG_BOOT_COMPLETED}
*/
protected void handleBootCompleted() {
+ if (mBootCompleted) return;
mBootCompleted = true;
mAudioManager = new AudioManager(mContext);
mAudioManager.registerRemoteControlDisplay(mRemoteControlDisplay);
@@ -816,7 +815,7 @@ public class KeyguardUpdateMonitor {
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onKeyguardVisibilityChanged(isShowing);
+ cb.onKeyguardVisibilityChangedRaw(isShowing);
}
}
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 76f9637..c08880d 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -19,6 +19,7 @@ import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
import android.graphics.Bitmap;
import android.media.AudioManager;
+import android.os.SystemClock;
import android.view.WindowManagerPolicy;
import com.android.internal.telephony.IccCardConstants;
@@ -27,6 +28,11 @@ import com.android.internal.telephony.IccCardConstants;
* Callback for general information relevant to lock screen.
*/
class KeyguardUpdateMonitorCallback {
+
+ private static final long VISIBILITY_CHANGED_COLLAPSE_MS = 1000;
+ private long mVisibilityChangedCalled;
+ private boolean mShowing;
+
/**
* Called when the battery status changes, e.g. when plugged in or unplugged, charge
* level, etc. changes.
@@ -70,6 +76,15 @@ class KeyguardUpdateMonitorCallback {
*/
void onKeyguardVisibilityChanged(boolean showing) { }
+ void onKeyguardVisibilityChangedRaw(boolean showing) {
+ final long now = SystemClock.elapsedRealtime();
+ if (showing == mShowing
+ && (now - mVisibilityChangedCalled) < VISIBILITY_CHANGED_COLLAPSE_MS) return;
+ onKeyguardVisibilityChanged(showing);
+ mVisibilityChangedCalled = now;
+ mShowing = showing;
+ }
+
/**
* Called when visibility of lockscreen clock changes, such as when
* obscured by a widget.
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
index fd7cae6..6aa0a4b 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
@@ -61,9 +61,11 @@ import android.widget.FrameLayout;
public class KeyguardViewManager {
private final static boolean DEBUG = KeyguardViewMediator.DEBUG;
private static String TAG = "KeyguardViewManager";
- public static boolean USE_UPPER_CASE = true;
public final static String IS_SWITCHING_USER = "is_switching_user";
+ // Delay dismissing keyguard to allow animations to complete.
+ private static final int HIDE_KEYGUARD_DELAY = 500;
+
// Timeout used for keypresses
static final int DIGIT_PRESS_WAKE_MILLIS = 5000;
@@ -509,9 +511,10 @@ public class KeyguardViewManager {
mKeyguardHost.setCustomBackground(null);
updateShowWallpaper(true);
mKeyguardHost.removeView(lastView);
+ mViewMediatorCallback.keyguardGone();
}
}
- }, 500);
+ }, HIDE_KEYGUARD_DELAY);
}
}
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
index a37a3a4..4086f84 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
@@ -200,7 +200,7 @@ public class KeyguardViewMediator {
// cached value of whether we are showing (need to know this to quickly
// answer whether the input should be restricted)
- private boolean mShowing = false;
+ private boolean mShowing;
// true if the keyguard is hidden by another window
private boolean mHidden = false;
@@ -253,6 +253,11 @@ public class KeyguardViewMediator {
private final float mLockSoundVolume;
/**
+ * For managing external displays
+ */
+ private KeyguardDisplayManager mKeyguardDisplayManager;
+
+ /**
* Cache of avatar drawables, for use by KeyguardMultiUserAvatar.
*/
private static MultiUserAvatarCache sMultiUserAvatarCache = new MultiUserAvatarCache();
@@ -304,6 +309,11 @@ public class KeyguardViewMediator {
* Report that the keyguard is dismissable, pending the next keyguardDone call.
*/
void keyguardDonePending();
+
+ /**
+ * Report when keyguard is actually gone
+ */
+ void keyguardGone();
}
KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
@@ -457,6 +467,11 @@ public class KeyguardViewMediator {
public void keyguardDonePending() {
mKeyguardDonePending = true;
}
+
+ @Override
+ public void keyguardGone() {
+ mKeyguardDisplayManager.hide();
+ }
};
private void userActivity() {
@@ -483,6 +498,8 @@ public class KeyguardViewMediator {
mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(DELAYED_KEYGUARD_ACTION));
+ mKeyguardDisplayManager = new KeyguardDisplayManager(context);
+
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
@@ -491,6 +508,10 @@ public class KeyguardViewMediator {
? lockPatternUtils : new LockPatternUtils(mContext);
mLockPatternUtils.setCurrentUser(UserHandle.USER_OWNER);
+ // Assume keyguard is showing (unless it's disabled) until we know for sure...
+ mShowing = (mUpdateMonitor.isDeviceProvisioned() || mLockPatternUtils.isSecure())
+ && !mLockPatternUtils.isLockScreenDisabled();
+
WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
mKeyguardViewManager = new KeyguardViewManager(context, wm, mViewMediatorCallback,
@@ -530,9 +551,6 @@ public class KeyguardViewMediator {
mSystemReady = true;
mUpdateMonitor.registerCallback(mUpdateCallback);
- // Send boot completed message if it hasn't already been sent.
- mUpdateMonitor.dispatchBootCompleted();
-
// Suppress biometric unlock right after boot until things have settled if it is the
// selected security method, otherwise unsuppress it. It must be unsuppressed if it is
// not the selected security method for the following reason: if the user starts
@@ -1221,6 +1239,7 @@ public class KeyguardViewMediator {
mShowKeyguardWakeLock.release();
}
+ mKeyguardDisplayManager.show();
}
/**
@@ -1366,4 +1385,8 @@ public class KeyguardViewMediator {
Message msg = mHandler.obtainMessage(LAUNCH_CAMERA);
mHandler.sendMessage(msg);
}
+
+ public void onBootCompleted() {
+ mUpdateMonitor.dispatchBootCompleted();
+ }
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java
index 8e39628..169899f 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java
@@ -15,6 +15,9 @@
*/
package com.android.keyguard;
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
@@ -46,6 +49,20 @@ public class KeyguardViewStateManager implements
int mChallengeTop = 0;
+ private final AnimatorListener mPauseListener = new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ mKeyguardSecurityContainer.onPause();
+ }
+ };
+
+ private final AnimatorListener mResumeListener = new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ if (((View)mKeyguardSecurityContainer).isShown()) {
+ mKeyguardSecurityContainer.onResume(0);
+ }
+ }
+ };
+
public KeyguardViewStateManager(KeyguardHostView hostView) {
mKeyguardHostView = hostView;
}
@@ -102,20 +119,20 @@ public class KeyguardViewStateManager implements
}
public void fadeOutSecurity(int duration) {
- ((View) mKeyguardSecurityContainer).animate().alpha(0f).setDuration(duration).start();
+ ((View) mKeyguardSecurityContainer).animate().alpha(0f).setDuration(duration)
+ .setListener(mPauseListener);
}
public void fadeInSecurity(int duration) {
- ((View) mKeyguardSecurityContainer).animate().alpha(1f).setDuration(duration).start();
+ ((View) mKeyguardSecurityContainer).animate().alpha(1f).setDuration(duration)
+ .setListener(mResumeListener);
}
public void onPageBeginMoving() {
if (mChallengeLayout.isChallengeOverlapping() &&
mChallengeLayout instanceof SlidingChallengeLayout) {
SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout;
- if (!mKeyguardWidgetPager.isWarping()) {
- scl.fadeOutChallenge();
- }
+ scl.fadeOutChallenge();
mPageIndexOnPageBeginMoving = mKeyguardWidgetPager.getCurrentPage();
}
// We use mAppWidgetToShow to show a particular widget after you add it--
@@ -137,11 +154,12 @@ public class KeyguardViewStateManager implements
public void onPageSwitching(View newPage, int newPageIndex) {
if (mKeyguardWidgetPager != null && mChallengeLayout instanceof SlidingChallengeLayout) {
boolean isCameraPage = newPage instanceof CameraWidgetFrame;
- SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout;
- scl.setChallengeInteractive(!isCameraPage);
if (isCameraPage) {
- scl.fadeOutChallenge();
+ CameraWidgetFrame camera = (CameraWidgetFrame) newPage;
+ camera.setUseFastTransition(mKeyguardWidgetPager.isWarping());
}
+ SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout;
+ scl.setChallengeInteractive(!isCameraPage);
final int currentFlags = mKeyguardWidgetPager.getSystemUiVisibility();
final int newFlags = isCameraPage ? (currentFlags | View.STATUS_BAR_DISABLE_SEARCH)
: (currentFlags & ~View.STATUS_BAR_DISABLE_SEARCH);
@@ -178,7 +196,7 @@ public class KeyguardViewStateManager implements
boolean challengeOverlapping = mChallengeLayout.isChallengeOverlapping();
if (challengeOverlapping && !newCurPage.isSmall()
&& mPageListeningToSlider != newPageIndex) {
- newCurPage.shrinkWidget();
+ newCurPage.shrinkWidget(true);
}
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java
index ab8a759..8ee9b61 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java
@@ -375,10 +375,6 @@ public class KeyguardWidgetFrame extends FrameLayout {
return mSmallFrameHeight;
}
- public void shrinkWidget() {
- shrinkWidget(true);
- }
-
public void setWidgetLockedSmall(boolean locked) {
if (locked) {
setWidgetHeight(mSmallWidgetHeight);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
index e07e0d0..99f7757 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
@@ -40,6 +40,7 @@ import android.view.accessibility.AccessibilityManager;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.TextClock;
+
import com.android.internal.widget.LockPatternUtils;
import java.util.ArrayList;
@@ -193,7 +194,9 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit
@Override
public void onPageEndWarp() {
- hideOutlinesAndSidePages();
+ // if we're moving to the warp page, then immediately hide the other widgets.
+ int duration = getPageWarpIndex() == getNextPage() ? 0 : -1;
+ animateOutlinesAndSidePages(false, duration);
mViewStateManager.onPageEndWarp();
}
@@ -668,7 +671,7 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit
// On the very first measure pass, if the challenge is showing, we need to make sure
// that the widget on the current page is small.
if (challengeShowing && i == mCurrentPage && !mHasMeasure) {
- frame.shrinkWidget();
+ frame.shrinkWidget(true);
}
}
}
diff --git a/packages/Keyguard/src/com/android/keyguard/PagedView.java b/packages/Keyguard/src/com/android/keyguard/PagedView.java
index 9d237dc..53c17a5 100644
--- a/packages/Keyguard/src/com/android/keyguard/PagedView.java
+++ b/packages/Keyguard/src/com/android/keyguard/PagedView.java
@@ -82,13 +82,13 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f;
// The page is moved more than halfway, automatically move to the next page on touch up.
- private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.4f;
+ private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.5f;
// The following constants need to be scaled based on density. The scaled versions will be
// assigned to the corresponding member variables below.
- private static final int FLING_THRESHOLD_VELOCITY = 500;
+ private static final int FLING_THRESHOLD_VELOCITY = 1500;
private static final int MIN_SNAP_VELOCITY = 1500;
- private static final int MIN_FLING_VELOCITY = 250;
+ private static final int MIN_FLING_VELOCITY = 500;
// We are disabling touch interaction of the widget region for factory ROM.
private static final boolean DISABLE_TOUCH_INTERACTION = false;
@@ -267,6 +267,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
private boolean mIsCameraEvent;
private float mWarpPeekAmount;
+ private boolean mOnPageEndWarpCalled;
+ private boolean mOnPageBeginWarpCalled;
public interface PageSwitchListener {
void onPageSwitching(View newPage, int newPageIndex);
@@ -491,7 +493,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
if (!mIsPageMoving) {
mIsPageMoving = true;
if (isWarping()) {
- onPageBeginWarp();
+ dispatchOnPageBeginWarp();
if (mPageSwapIndex != -1) {
swapPages(mPageSwapIndex, mPageWarpIndex);
}
@@ -500,6 +502,22 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
}
+ private void dispatchOnPageBeginWarp() {
+ if (!mOnPageBeginWarpCalled) {
+ onPageBeginWarp();
+ mOnPageBeginWarpCalled = true;
+ }
+ mOnPageEndWarpCalled = false;
+ }
+
+ private void dispatchOnPageEndWarp() {
+ if (!mOnPageEndWarpCalled) {
+ onPageEndWarp();
+ mOnPageEndWarpCalled = true;
+ }
+ mOnPageBeginWarpCalled = false;
+ }
+
protected void pageEndMoving() {
if (DEBUG_WARP) Log.v(TAG, "pageEndMoving(" + mIsPageMoving + ")");
if (mIsPageMoving) {
@@ -508,7 +526,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
if (mPageSwapIndex != -1) {
swapPages(mPageSwapIndex, mPageWarpIndex);
}
- onPageEndWarp();
+ dispatchOnPageEndWarp();
resetPageWarp();
}
onPageEndMoving();
@@ -1919,11 +1937,13 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
if (isWarping()) {
- onPageEndWarp();
+ dispatchOnPageEndWarp();
+ notifyPageSwitching(whichPage);
resetPageWarp();
+ } else {
+ notifyPageSwitching(whichPage);
}
- notifyPageSwitching(whichPage);
View focusedChild = getFocusedChild();
if (focusedChild != null && whichPage != mCurrentPage &&
focusedChild == getPageAt(mCurrentPage)) {
@@ -2260,11 +2280,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
mTempVisiblePagesRange[0] = 0;
mTempVisiblePagesRange[1] = getPageCount() - 1;
boundByReorderablePages(true, mTempVisiblePagesRange);
- mReorderingStarted = true;
// Check if we are within the reordering range
if (mTempVisiblePagesRange[0] <= dragViewIndex &&
dragViewIndex <= mTempVisiblePagesRange[1]) {
+ mReorderingStarted = true;
if (zoomOut()) {
// Find the drag view under the pointer
mDragView = getChildAt(dragViewIndex);
@@ -2702,12 +2722,12 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
@Override
public void onAnimationEnd(Animator animation) {
mWarpAnimation = null;
- mWarpPageExposed = true;
+ mWarpPageExposed = false;
}
};
private void cancelWarpAnimation(String msg, boolean abortAnimation) {
- if (DEBUG_WARP) Log.v(TAG, "cancelWarpAnimation(" + msg + ")");
+ if (DEBUG_WARP) Log.v(TAG, "cancelWarpAnimation(" + msg + ",abort=" + abortAnimation + ")");
if (abortAnimation) {
// We're done with the animation and moving to a new page. Let the scroller
// take over the animation.
@@ -2727,9 +2747,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
private void animateWarpPageOnScreen(String reason) {
if (DEBUG_WARP) Log.v(TAG, "animateWarpPageOnScreen(" + reason + ")");
- if (isWarping()) {
+ if (isWarping() && !mWarpPageExposed) {
mWarpPageExposed = true;
- onPageBeginWarp();
+ dispatchOnPageBeginWarp();
KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(mPageWarpIndex);
if (DEBUG_WARP) Log.v(TAG, "moving page on screen: Tx=" + v.getTranslationX());
DecelerateInterpolator interp = new DecelerateInterpolator(1.5f);
@@ -2744,7 +2764,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
private void animateWarpPageOffScreen(String reason, boolean animate) {
if (DEBUG_WARP) Log.v(TAG, "animateWarpPageOffScreen(" + reason + " anim:" + animate + ")");
if (isWarping()) {
- onPageEndWarp();
+ dispatchOnPageEndWarp();
KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(mPageWarpIndex);
if (DEBUG_WARP) Log.v(TAG, "moving page off screen: Tx=" + v.getTranslationX());
AccelerateInterpolator interp = new AccelerateInterpolator(1.5f);
diff --git a/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java b/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java
index 7a9a1c8..3d515ce 100644
--- a/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java
+++ b/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java
@@ -1003,6 +1003,16 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout
}
}
+ @Override
+ protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+ // Focus security fileds before widgets.
+ if (mChallengeView != null &&
+ mChallengeView.requestFocus(direction, previouslyFocusedRect)) {
+ return true;
+ }
+ return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
+ }
+
public void computeScroll() {
super.computeScroll();
diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml b/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml
index d503216..3303ef1 100644
--- a/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml
+++ b/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml
@@ -16,7 +16,7 @@
<com.android.printspooler.PrintDialogFrame xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
- android:layout_height="fill_parent">
+ android:layout_height="wrap_content">
<FrameLayout
android:id="@+id/content_container"
android:layout_width="fill_parent"
diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity_content_editing.xml b/packages/PrintSpooler/res/layout/print_job_config_activity_content_editing.xml
index 02740a3..e50a7af 100644
--- a/packages/PrintSpooler/res/layout/print_job_config_activity_content_editing.xml
+++ b/packages/PrintSpooler/res/layout/print_job_config_activity_content_editing.xml
@@ -18,8 +18,7 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:scrollbars="vertical"
- android:background="@color/editable_background">
+ android:scrollbars="vertical">
<LinearLayout
android:layout_width="fill_parent"
@@ -42,6 +41,7 @@
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
+ android:layout_marginBottom="24dip"
android:orientation="horizontal"
android:baselineAligned="false">
@@ -203,27 +203,79 @@
</LinearLayout>
+ <!-- Advanced settings button -->
+
+ <LinearLayout
+ android:id="@+id/advanced_settings_container"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone">
+
+ <ImageView
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:layout_marginStart="24dip"
+ android:layout_marginEnd="24dip"
+ android:layout_gravity="fill_horizontal"
+ android:background="@color/separator"
+ android:contentDescription="@null">
+ </ImageView>
+
+ <Button
+ android:id="@+id/advanced_settings_button"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="24dip"
+ android:layout_marginEnd="24dip"
+ android:layout_gravity="fill_horizontal"
+ android:text="@string/advanced_settings_button"
+ android:gravity="start|center_vertical"
+ android:textSize="16sp"
+ android:textColor="@color/item_text_color">
+ </Button>
+
+ <ImageView
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:layout_gravity="fill_horizontal"
+ android:layout_marginStart="24dip"
+ android:layout_marginEnd="24dip"
+ android:background="@color/separator"
+ android:contentDescription="@null">
+ </ImageView>
+
+ </LinearLayout>
+
<!-- Print button -->
- <ImageView
+ <FrameLayout
android:layout_width="fill_parent"
- android:layout_height="1dip"
+ android:layout_height="wrap_content"
android:layout_marginTop="24dip"
- android:layout_gravity="fill_horizontal"
- android:background="@color/separator"
- android:contentDescription="@null">
- </ImageView>
+ android:background="@color/action_button_background">
+
+ <ImageView
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:layout_gravity="fill_horizontal"
+ android:background="@color/separator"
+ android:contentDescription="@null">
+ </ImageView>
+
+ <Button
+ android:id="@+id/print_button"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="fill_horizontal"
+ android:text="@string/print_button"
+ android:textSize="16sp"
+ android:textColor="@color/item_text_color">
+ </Button>
- <Button
- android:id="@+id/print_button"
- style="?android:attr/buttonBarButtonStyle"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="fill_horizontal"
- android:text="@string/print_button"
- android:textSize="16sp"
- android:textColor="@color/item_text_color">
- </Button>
+ </FrameLayout>
</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity_content_error.xml b/packages/PrintSpooler/res/layout/print_job_config_activity_content_error.xml
index 222b5b6..d9f0a9a 100644
--- a/packages/PrintSpooler/res/layout/print_job_config_activity_content_error.xml
+++ b/packages/PrintSpooler/res/layout/print_job_config_activity_content_error.xml
@@ -23,7 +23,6 @@
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:background="@color/editable_background"
android:orientation="vertical">
<TextView
@@ -36,30 +35,36 @@
android:layout_marginBottom="32dip"
android:layout_gravity="center"
style="?android:attr/buttonBarButtonStyle"
- android:singleLine="true"
android:ellipsize="end"
android:text="@string/print_error_default_message"
android:textColor="@color/important_text"
android:textSize="16sp">
</TextView>
+ </LinearLayout>
+
+ <FrameLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/action_button_background">
+
<View
android:layout_width="fill_parent"
android:layout_height="1dip"
android:background="@color/separator">
</View>
- </LinearLayout>
+ <Button
+ android:id="@+id/ok_button"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="fill_horizontal"
+ style="?android:attr/buttonBarButtonStyle"
+ android:text="@android:string/ok"
+ android:textSize="16sp"
+ android:textColor="@color/important_text">
+ </Button>
- <Button
- android:id="@+id/ok_button"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="fill_horizontal"
- style="?android:attr/buttonBarButtonStyle"
- android:text="@android:string/ok"
- android:textSize="16sp"
- android:textColor="@color/important_text">
- </Button>
+ </FrameLayout>
</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity_content_generating.xml b/packages/PrintSpooler/res/layout/print_job_config_activity_content_generating.xml
index 8bdb6c9..10602ee 100644
--- a/packages/PrintSpooler/res/layout/print_job_config_activity_content_generating.xml
+++ b/packages/PrintSpooler/res/layout/print_job_config_activity_content_generating.xml
@@ -23,7 +23,6 @@
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:background="@color/editable_background"
android:orientation="vertical">
<TextView
@@ -51,23 +50,30 @@
style="?android:attr/progressBarStyleLarge">
</ProgressBar>
+ </LinearLayout>
+
+ <FrameLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/action_button_background">
+
<View
android:layout_width="fill_parent"
android:layout_height="1dip"
android:background="@color/separator">
</View>
- </LinearLayout>
+ <Button
+ android:id="@+id/cancel_button"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="fill_horizontal"
+ style="?android:attr/buttonBarButtonStyle"
+ android:text="@string/cancel"
+ android:textSize="16sp"
+ android:textColor="@color/important_text">
+ </Button>
- <Button
- android:id="@+id/cancel_button"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="fill_horizontal"
- style="?android:attr/buttonBarButtonStyle"
- android:text="@string/cancel"
- android:textSize="16sp"
- android:textColor="@color/important_text">
- </Button>
+ </FrameLayout>
</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
index 2749aa6..1a61b99 100644
--- a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
+++ b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
@@ -15,13 +15,13 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:paddingStart="16dip"
- android:paddingEnd="16dip"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:orientation="horizontal"
- android:gravity="start|center_vertical">
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="16dip"
+ android:paddingEnd="16dip"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:orientation="horizontal"
+ android:gravity="start|center_vertical">
<ImageView
android:id="@+id/icon"
@@ -31,7 +31,7 @@
android:layout_marginEnd="8dip"
android:duplicateParentState="true"
android:contentDescription="@null"
- android:visibility="gone">
+ android:visibility="invisible">
</ImageView>
<LinearLayout
diff --git a/packages/PrintSpooler/res/layout/printer_list_item.xml b/packages/PrintSpooler/res/layout/printer_list_item.xml
new file mode 100644
index 0000000..47eb0b5
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/printer_list_item.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="horizontal"
+ android:gravity="start|center_vertical">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="32dip"
+ android:layout_height="32dip"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="8dip"
+ android:duplicateParentState="true"
+ android:contentDescription="@null"
+ android:visibility="gone">
+ </ImageView>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:duplicateParentState="true">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textIsSelectable="false"
+ android:gravity="top|start"
+ android:textColor="@color/item_text_color"
+ android:duplicateParentState="true">
+ </TextView>
+
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textIsSelectable="false"
+ android:visibility="gone"
+ android:textColor="@color/print_option_title"
+ android:duplicateParentState="true">
+ </TextView>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/select_printer_fragment.xml b/packages/PrintSpooler/res/layout/select_printer_fragment.xml
new file mode 100644
index 0000000..bbd012e
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/select_printer_fragment.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/list"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:paddingStart="@dimen/printer_list_view_padding_start"
+ android:paddingEnd="@dimen/printer_list_view_padding_end"
+ android:scrollbarStyle="outsideOverlay"
+ android:cacheColorHint="@android:color/transparent"
+ android:scrollbarAlwaysDrawVerticalTrack="true" >
+</ListView>
diff --git a/packages/PrintSpooler/res/values-ja/arrays.xml b/packages/PrintSpooler/res/values-ja/arrays.xml
index 3364979..3187cbe 100644
--- a/packages/PrintSpooler/res/values-ja/arrays.xml
+++ b/packages/PrintSpooler/res/values-ja/arrays.xml
@@ -20,13 +20,13 @@
<item>JIS_B9</item>
<item>JIS_B8</item>
<item>JIS_B7</item>
- <item>JIS_b6</item>
- <item>JIS_b5</item>
- <item>JIS_b4</item>
- <item>JIS_b3</item>
- <item>JIS_b2</item>
- <item>JIS_b1</item>
- <item>JIS_b0</item>
+ <item>JIS_B6</item>
+ <item>JIS_B5</item>
+ <item>JIS_B4</item>
+ <item>JIS_B3</item>
+ <item>JIS_B2</item>
+ <item>JIS_B1</item>
+ <item>JIS_B0</item>
<item>JIS_EXEC</item>
<item>JPN_CHOU4</item>
<item>JPN_CHOU3</item>
diff --git a/packages/Keyguard/res/drawable/lockscreen_forgot_password_button.xml b/packages/PrintSpooler/res/values-land/constants.xml
index 6c081bf..d68b77e 100644
--- a/packages/Keyguard/res/drawable/lockscreen_forgot_password_button.xml
+++ b/packages/PrintSpooler/res/values-land/constants.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
+<!-- Copyright (C) 2013 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,8 +14,9 @@
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="true" android:drawable="@drawable/ic_lockscreen_forgotpassword_normal" />
- <item android:state_pressed="true" android:drawable="@drawable/ic_lockscreen_forgotpassword_pressed" />
- <item android:drawable="@drawable/ic_lockscreen_forgotpassword_normal" />
-</selector>
+<resources>
+
+ <dimen name="printer_list_view_padding_start">48dip</dimen>
+ <dimen name="printer_list_view_padding_end">48dip</dimen>
+
+</resources>
diff --git a/packages/PrintSpooler/res/values/colors.xml b/packages/PrintSpooler/res/values/colors.xml
index 9972c96..4fc25b3 100644
--- a/packages/PrintSpooler/res/values/colors.xml
+++ b/packages/PrintSpooler/res/values/colors.xml
@@ -16,10 +16,10 @@
<resources>
- <color name="container_background">#FFFFFF</color>
+ <color name="container_background">#F2F2F2</color>
<color name="important_text">#333333</color>
<color name="print_option_title">#888888</color>
<color name="separator">#CCCCCC</color>
- <color name="editable_background">#F2F2F2</color>
+ <color name="action_button_background">#FFFFFF</color>
-</resources> \ No newline at end of file
+</resources>
diff --git a/packages/PrintSpooler/res/values/constants.xml b/packages/PrintSpooler/res/values/constants.xml
index e5a9d5d..e9c925c 100644
--- a/packages/PrintSpooler/res/values/constants.xml
+++ b/packages/PrintSpooler/res/values/constants.xml
@@ -26,4 +26,7 @@
<dimen name="print_dialog_frame_max_width_dip">400dip</dimen>
-</resources> \ No newline at end of file
+ <dimen name="printer_list_view_padding_start">16dip</dimen>
+ <dimen name="printer_list_view_padding_end">16dip</dimen>
+
+</resources>
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index d74b414..d2613d0 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -19,6 +19,9 @@
<!-- Title of the PrintSpooler application. [CHAR LIMIT=50] -->
<string name="app_label">Print Spooler</string>
+ <!-- Label of the print dialog's button for advanced printer settings. [CHAR LIMIT=25] -->
+ <string name="advanced_settings_button">Printer settings</string>
+
<!-- Label of the print dialog's print button. [CHAR LIMIT=16] -->
<string name="print_button">Print</string>
@@ -90,6 +93,12 @@
<!-- Title of the action bar button to got to add a printer. [CHAR LIMIT=25] -->
<string name="print_add_printer">Add printer</string>
+ <!-- Title of the menu item to select a printer. [CHAR LIMIT=25] -->
+ <string name="print_select_printer">Select printer</string>
+
+ <!-- Title of the menu item to forget a printer. [CHAR LIMIT=25] -->
+ <string name="print_forget_printer">Forget printer</string>
+
<!-- Utterance to announce a change in the number of matches during a search. This is spoken to a blind user. [CHAR LIMIT=none] -->
<plurals name="print_search_result_count_utterance">
<item quantity="one"><xliff:g id="count" example="1">%1$s</xliff:g> printer found</item>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
index 0601467..9831839 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
@@ -79,6 +79,8 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> {
private PrinterId mTrackedPrinter;
+ private boolean mPrintersUpdatedBefore;
+
public FusedPrintersProvider(Context context) {
super(context);
mPersistenceManager = new PersistenceManager(context);
@@ -88,13 +90,14 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> {
mPersistenceManager.addPrinterAndWritePrinterHistory(printer);
}
- private void computeAndDeliverResult(Map<PrinterId, PrinterInfo> discoveredPrinters) {
+ private void computeAndDeliverResult(ArrayMap<PrinterId, PrinterInfo> discoveredPrinters,
+ ArrayMap<PrinterId, PrinterInfo> favoritePrinters) {
List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
// Add the updated favorite printers.
- final int favoritePrinterCount = mFavoritePrinters.size();
+ final int favoritePrinterCount = favoritePrinters.size();
for (int i = 0; i < favoritePrinterCount; i++) {
- PrinterInfo favoritePrinter = mFavoritePrinters.get(i);
+ PrinterInfo favoritePrinter = favoritePrinters.valueAt(i);
PrinterInfo updatedPrinter = discoveredPrinters.remove(
favoritePrinter.getId());
if (updatedPrinter != null) {
@@ -123,8 +126,11 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> {
mPrinters.addAll(printers);
if (isStarted()) {
- // Deliver the printers.
+ // If stated deliver the new printers.
deliverResult(printers);
+ } else {
+ // Otherwise, take a note for the change.
+ onContentChanged();
}
}
@@ -165,6 +171,8 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> {
.getSystemService(Context.PRINT_SERVICE);
mDiscoverySession = printManager.createPrinterDiscoverySession();
mPersistenceManager.readPrinterHistory();
+ } else if (mPersistenceManager.isHistoryChanged()) {
+ mPersistenceManager.readPrinterHistory();
}
if (mPersistenceManager.isReadHistoryCompleted()
&& !mDiscoverySession.isPrinterDiscoveryStarted()) {
@@ -176,7 +184,7 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> {
+ mDiscoverySession.getPrinters().size()
+ " " + FusedPrintersProvider.this.hashCode());
}
- updatePrinters(mDiscoverySession.getPrinters());
+ updatePrinters(mDiscoverySession.getPrinters(), mFavoritePrinters);
}
});
final int favoriteCount = mFavoritePrinters.size();
@@ -187,15 +195,19 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> {
mDiscoverySession.startPrinterDisovery(printerIds);
List<PrinterInfo> printers = mDiscoverySession.getPrinters();
if (!printers.isEmpty()) {
- updatePrinters(printers);
+ updatePrinters(printers, mFavoritePrinters);
}
}
}
- private void updatePrinters(List<PrinterInfo> printers) {
- if (mPrinters.equals(printers)) {
+ private void updatePrinters(List<PrinterInfo> printers, List<PrinterInfo> favoritePrinters) {
+ if (mPrintersUpdatedBefore && mPrinters.equals(printers)
+ && mFavoritePrinters.equals(favoritePrinters)) {
return;
}
+
+ mPrintersUpdatedBefore = true;
+
ArrayMap<PrinterId, PrinterInfo> printersMap =
new ArrayMap<PrinterId, PrinterInfo>();
final int printerCount = printers.size();
@@ -203,7 +215,16 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> {
PrinterInfo printer = printers.get(i);
printersMap.put(printer.getId(), printer);
}
- computeAndDeliverResult(printersMap);
+
+ ArrayMap<PrinterId, PrinterInfo> favoritePrintersMap =
+ new ArrayMap<PrinterId, PrinterInfo>();
+ final int favoritePrinterCount = favoritePrinters.size();
+ for (int i = 0; i < favoritePrinterCount; i++) {
+ PrinterInfo favoritePrinter = favoritePrinters.get(i);
+ favoritePrintersMap.put(favoritePrinter.getId(), favoritePrinter);
+ }
+
+ computeAndDeliverResult(printersMap, favoritePrintersMap);
}
@Override
@@ -264,6 +285,42 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> {
}
}
+ public boolean isFavoritePrinter(PrinterId printerId) {
+ final int printerCount = mFavoritePrinters.size();
+ for (int i = 0; i < printerCount; i++) {
+ PrinterInfo favoritePritner = mFavoritePrinters.get(i);
+ if (favoritePritner.getId().equals(printerId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void forgetFavoritePrinter(PrinterId printerId) {
+ List<PrinterInfo> newFavoritePrinters = null;
+
+ // Remove the printer from the favorites.
+ final int favoritePrinterCount = mFavoritePrinters.size();
+ for (int i = 0; i < favoritePrinterCount; i++) {
+ PrinterInfo favoritePrinter = mFavoritePrinters.get(i);
+ if (favoritePrinter.getId().equals(printerId)) {
+ newFavoritePrinters = new ArrayList<PrinterInfo>();
+ newFavoritePrinters.addAll(mPrinters);
+ newFavoritePrinters.remove(i);
+ break;
+ }
+ }
+
+ // If we removed a favorite printer, we have work to do.
+ if (newFavoritePrinters != null) {
+ // Remove the printer from history and persist the latter.
+ mPersistenceManager.removeHistoricalPrinterAndWritePrinterHistory(printerId);
+
+ // Recompute and deliver the printers.
+ updatePrinters(mDiscoverySession.getPrinters(), newFavoritePrinters);
+ }
+ }
+
private final class PersistenceManager {
private static final String PERSIST_FILE_NAME = "printer_history.xml";
@@ -281,13 +338,15 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> {
private final AtomicFile mStatePersistFile;
- private List<PrinterInfo> mHistoricalPrinters;
+ private List<PrinterInfo> mHistoricalPrinters = new ArrayList<PrinterInfo>();
private boolean mReadHistoryCompleted;
private boolean mReadHistoryInProgress;
private ReadTask mReadTask;
+ private volatile long mLastReadHistoryTimestamp;
+
private PersistenceManager(Context context) {
mStatePersistFile = new AtomicFile(new File(context.getFilesDir(),
PERSIST_FILE_NAME));
@@ -327,6 +386,27 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> {
new ArrayList<PrinterInfo>(mHistoricalPrinters));
}
+ @SuppressWarnings("unchecked")
+ public void removeHistoricalPrinterAndWritePrinterHistory(PrinterId printerId) {
+ boolean writeHistory = false;
+ final int printerCount = mHistoricalPrinters.size();
+ for (int i = printerCount - 1; i >= 0; i--) {
+ PrinterInfo historicalPrinter = mHistoricalPrinters.get(i);
+ if (historicalPrinter.getId().equals(printerId)) {
+ mHistoricalPrinters.remove(i);
+ writeHistory = true;
+ }
+ }
+ if (writeHistory) {
+ new WriteTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
+ new ArrayList<PrinterInfo>(mHistoricalPrinters));
+ }
+ }
+
+ public boolean isHistoryChanged() {
+ return mLastReadHistoryTimestamp != mStatePersistFile.getBaseFile().lastModified();
+ }
+
private List<PrinterInfo> computeFavoritePrinters(List<PrinterInfo> printers) {
Map<PrinterId, PrinterRecord> recordMap =
new ArrayMap<PrinterId, PrinterRecord>();
@@ -423,11 +503,10 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> {
mReadHistoryInProgress = false;
mReadHistoryCompleted = true;
- // Deliver the favorites.
- Map<PrinterId, PrinterInfo> discoveredPrinters = Collections.emptyMap();
- computeAndDeliverResult(discoveredPrinters);
+ // Deliver the printers.
+ updatePrinters(mDiscoverySession.getPrinters(), mHistoricalPrinters);
- // Start loading the available printers.
+ // Loading the available printers if needed.
loadInternal();
// We are done.
@@ -450,6 +529,8 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, null);
parseState(parser, printers);
+ // Take a note which version of the history was read.
+ mLastReadHistoryTimestamp = mStatePersistFile.getBaseFile().lastModified();
return printers;
} catch (IllegalStateException ise) {
Slog.w(LOG_TAG, "Failed parsing ", ise);
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index 2997707..88403a3 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -19,6 +19,7 @@ package com.android.printspooler;
import android.app.Activity;
import android.app.Dialog;
import android.app.LoaderManager;
+import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -26,6 +27,8 @@ import android.content.Loader;
import android.content.ServiceConnection;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -40,6 +43,7 @@ import android.os.Message;
import android.os.RemoteException;
import android.print.ILayoutResultCallback;
import android.print.IPrintDocumentAdapter;
+import android.print.IPrintDocumentAdapterObserver;
import android.print.IWriteResultCallback;
import android.print.PageRange;
import android.print.PrintAttributes;
@@ -54,6 +58,8 @@ import android.print.PrintManager;
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
+import android.printservice.PrintService;
+import android.printservice.PrintServiceInfo;
import android.provider.DocumentsContract;
import android.text.Editable;
import android.text.TextUtils;
@@ -129,6 +135,7 @@ public class PrintJobConfigActivity extends Activity {
private static final int ACTIVITY_REQUEST_CREATE_FILE = 1;
private static final int ACTIVITY_REQUEST_SELECT_PRINTER = 2;
+ private static final int ACTIVITY_POPULATE_ADVANCED_PRINT_OPTIONS = 3;
private static final int CONTROLLER_STATE_FINISHED = 1;
private static final int CONTROLLER_STATE_FAILED = 2;
@@ -201,6 +208,14 @@ public class PrintJobConfigActivity extends Activity {
throw new IllegalArgumentException("PrintDocumentAdapter cannot be null");
}
+ try {
+ IPrintDocumentAdapter.Stub.asInterface(mIPrintDocumentAdapter)
+ .setObserver(new PrintDocumentAdapterObserver(this));
+ } catch (RemoteException re) {
+ finish();
+ return;
+ }
+
PrintAttributes attributes = printJob.getAttributes();
if (attributes != null) {
mCurrPrintAttributes.copyFrom(attributes);
@@ -245,31 +260,32 @@ public class PrintJobConfigActivity extends Activity {
}
@Override
- protected void onDestroy() {
- // We can safely do the work in here since at this point
- // the system is bound to our (spooler) process which
- // guarantees that this process will not be killed.
- if (mController.hasStarted()) {
- mController.finish();
- }
- if (mEditor.isPrintConfirmed() && mController.isFinished()) {
- mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
- PrintJobInfo.STATE_QUEUED, null);
- } else {
- mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
- PrintJobInfo.STATE_CANCELED, null);
- }
- mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
- if (mGeneratingPrintJobDialog != null) {
- mGeneratingPrintJobDialog.dismiss();
- mGeneratingPrintJobDialog = null;
- }
- mSpoolerProvider.destroy();
- super.onDestroy();
+ public void onPause() {
+ if (isFinishing()) {
+ if (mController != null && mController.hasStarted()) {
+ mController.finish();
+ }
+ if (mEditor != null && mEditor.isPrintConfirmed()
+ && mController != null && mController.isFinished()) {
+ mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
+ PrintJobInfo.STATE_QUEUED, null);
+ } else {
+ mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
+ PrintJobInfo.STATE_CANCELED, null);
+ }
+ if (mGeneratingPrintJobDialog != null) {
+ mGeneratingPrintJobDialog.dismiss();
+ mGeneratingPrintJobDialog = null;
+ }
+ mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
+ mSpoolerProvider.destroy();
+ }
+ super.onPause();
}
public boolean onTouchEvent(MotionEvent event) {
- if (!mEditor.isPrintConfirmed() && mEditor.shouldCloseOnTouch(event)) {
+ if (mController != null && mEditor != null &&
+ !mEditor.isPrintConfirmed() && mEditor.shouldCloseOnTouch(event)) {
if (!mController.isWorking()) {
PrintJobConfigActivity.this.finish();
}
@@ -287,17 +303,19 @@ public class PrintJobConfigActivity extends Activity {
}
public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- if (mEditor.isShwoingGeneratingPrintJobUi()) {
- return true;
- }
- if (event.isTracking() && !event.isCanceled()) {
- if (!mController.isWorking()) {
- PrintJobConfigActivity.this.finish();
+ if (mController != null && mEditor != null) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ if (mEditor.isShwoingGeneratingPrintJobUi()) {
+ return true;
+ }
+ if (event.isTracking() && !event.isCanceled()) {
+ if (!mController.isWorking()) {
+ PrintJobConfigActivity.this.finish();
+ }
}
+ mEditor.cancel();
+ return true;
}
- mEditor.cancel();
- return true;
}
return super.onKeyUp(keyCode, event);
}
@@ -339,6 +357,9 @@ public class PrintJobConfigActivity extends Activity {
}
public void cancel() {
+ if (isWorking()) {
+ mRemotePrintAdapter.cancel();
+ }
mControllerState = CONTROLLER_STATE_CANCELLED;
}
@@ -594,21 +615,17 @@ public class PrintJobConfigActivity extends Activity {
} else {
// We did not get the pages we requested, then the application
// misbehaves, so we fail quickly.
- // TODO: We need some UI for announcing an error.
mControllerState = CONTROLLER_STATE_FAILED;
Log.e(LOG_TAG, "Received invalid pages from the app");
- mEditor.cancel();
- PrintJobConfigActivity.this.finish();
+ mEditor.showUi(Editor.UI_ERROR, null);
}
}
private void requestCreatePdfFileOrFinish() {
if (mEditor.isPrintingToPdf()) {
- PrintJobInfo printJob = mSpoolerProvider.getSpooler()
- .getPrintJobInfo(mPrintJobId, PrintManager.APP_ID_ANY);
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.setType("application/pdf");
- intent.putExtra(Intent.EXTRA_TITLE, printJob.getLabel());
+ intent.putExtra(Intent.EXTRA_TITLE, mDocument.info.getName());
intent.putExtra(DocumentsContract.EXTRA_PACKAGE_NAME, mCallingPackageName);
startActivityForResult(intent, ACTIVITY_REQUEST_CREATE_FILE);
} else {
@@ -778,6 +795,19 @@ public class PrintJobConfigActivity extends Activity {
}
mEditor.ensureCurrentPrinterSelected();
} break;
+
+ case ACTIVITY_POPULATE_ADVANCED_PRINT_OPTIONS: {
+ if (resultCode == RESULT_OK) {
+ PrintJobInfo printJobInfo = (PrintJobInfo) data.getParcelableExtra(
+ PrintService.EXTRA_PRINT_JOB_INFO);
+ if (printJobInfo != null) {
+ mEditor.updateFromAdvancedOptions(printJobInfo);
+ break;
+ }
+ }
+ mEditor.cancel();
+ PrintJobConfigActivity.this.finish();
+ } break;
}
}
@@ -856,6 +886,10 @@ public class PrintJobConfigActivity extends Activity {
private View mContentContainer;
+ private View mAdvancedPrintOptionsContainer;
+
+ private Button mAdvancedOptionsButton;
+
private Button mPrintButton;
private PrinterId mNextPrinterId;
@@ -903,6 +937,7 @@ public class PrintJobConfigActivity extends Activity {
mPrintJobId, mCurrentPrinter);
if (mCurrentPrinter.getStatus() == PrinterInfo.STATUS_UNAVAILABLE) {
+ mCapabilitiesTimeout.post();
updateUi();
return;
}
@@ -919,6 +954,10 @@ public class PrintJobConfigActivity extends Activity {
refreshCurrentPrinter();
}
} else if (spinner == mMediaSizeSpinner) {
+ if (mIgnoreNextMediaSizeChange) {
+ mIgnoreNextMediaSizeChange = false;
+ return;
+ }
if (mOldMediaSizeSelectionIndex
== mMediaSizeSpinner.getSelectedItemPosition()) {
mOldMediaSizeSelectionIndex = AdapterView.INVALID_POSITION;
@@ -934,6 +973,10 @@ public class PrintJobConfigActivity extends Activity {
mController.update();
}
} else if (spinner == mColorModeSpinner) {
+ if (mIgnoreNextColorChange) {
+ mIgnoreNextColorChange = false;
+ return;
+ }
if (mOldColorModeSelectionIndex
== mColorModeSpinner.getSelectedItemPosition()) {
mOldColorModeSelectionIndex = AdapterView.INVALID_POSITION;
@@ -1180,6 +1223,16 @@ public class PrintJobConfigActivity extends Activity {
// greater than the to page. When computing the requested pages
// we just swap them if necessary.
+ // Keep the print job up to date with the selected pages if we
+ // know how many pages are there in the document.
+ PageRange[] requestedPages = getRequestedPages();
+ if (requestedPages != null && requestedPages.length > 0
+ && requestedPages[requestedPages.length - 1].getEnd()
+ < mDocument.info.getPageCount()) {
+ mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(
+ mPrintJobId, requestedPages);
+ }
+
mPageRangeEditText.setError(null);
mPrintButton.setEnabled(true);
updateUi();
@@ -1202,6 +1255,8 @@ public class PrintJobConfigActivity extends Activity {
private boolean mIgnoreNextRangeOptionChange;
private boolean mIgnoreNextCopiesChange;
private boolean mIgnoreNextRangeChange;
+ private boolean mIgnoreNextMediaSizeChange;
+ private boolean mIgnoreNextColorChange;
private int mCurrentUi = UI_NONE;
@@ -1411,6 +1466,88 @@ public class PrintJobConfigActivity extends Activity {
}
}
+ public void updateFromAdvancedOptions(PrintJobInfo printJobInfo) {
+ boolean updateContent = false;
+
+ // Copies.
+ mCopiesEditText.setText(String.valueOf(printJobInfo.getCopies()));
+
+ // Media size and orientation
+ PrintAttributes attributes = printJobInfo.getAttributes();
+ if (!mCurrPrintAttributes.getMediaSize().equals(attributes.getMediaSize())) {
+ final int mediaSizeCount = mMediaSizeSpinnerAdapter.getCount();
+ for (int i = 0; i < mediaSizeCount; i++) {
+ MediaSize mediaSize = mMediaSizeSpinnerAdapter.getItem(i).value;
+ if (mediaSize.asPortrait().equals(attributes.getMediaSize().asPortrait())) {
+ updateContent = true;
+ mCurrPrintAttributes.setMediaSize(attributes.getMediaSize());
+ mMediaSizeSpinner.setSelection(i);
+ mIgnoreNextMediaSizeChange = true;
+ if (attributes.getMediaSize().isPortrait()) {
+ mOrientationSpinner.setSelection(0);
+ mIgnoreNextOrientationChange = true;
+ } else {
+ mOrientationSpinner.setSelection(1);
+ mIgnoreNextOrientationChange = true;
+ }
+ break;
+ }
+ }
+ }
+
+ // Color mode.
+ final int colorMode = attributes.getColorMode();
+ if (mCurrPrintAttributes.getColorMode() != colorMode) {
+ if (colorMode == PrintAttributes.COLOR_MODE_MONOCHROME) {
+ updateContent = true;
+ mColorModeSpinner.setSelection(0);
+ mIgnoreNextColorChange = true;
+ mCurrPrintAttributes.setColorMode(attributes.getColorMode());
+ } else if (colorMode == PrintAttributes.COLOR_MODE_COLOR) {
+ updateContent = true;
+ mColorModeSpinner.setSelection(1);
+ mIgnoreNextColorChange = true;
+ mCurrPrintAttributes.setColorMode(attributes.getColorMode());
+ }
+ }
+
+ // Range.
+ PageRange[] pageRanges = printJobInfo.getPages();
+ if (pageRanges != null && pageRanges.length > 0) {
+ pageRanges = PageRangeUtils.normalize(pageRanges);
+ final int pageRangeCount = pageRanges.length;
+ if (pageRangeCount == 1 && pageRanges[0] == PageRange.ALL_PAGES) {
+ mRangeOptionsSpinner.setSelection(0);
+ } else {
+ final int pageCount = mDocument.info.getPageCount();
+ if (pageRanges[0].getStart() >= 0
+ && pageRanges[pageRanges.length - 1].getEnd() < pageCount) {
+ mRangeOptionsSpinner.setSelection(1);
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < pageRangeCount; i++) {
+ if (builder.length() > 0) {
+ builder.append(',');
+ }
+ PageRange pageRange = pageRanges[i];
+ builder.append(pageRange.getStart());
+ builder.append('-');
+ builder.append(pageRange.getEnd());
+ }
+ mPageRangeEditText.setText(builder.toString());
+ }
+ }
+ }
+
+ // Update the advanced options.
+ mSpoolerProvider.getSpooler().setPrintJobAdvancedOptionsNoPersistence(
+ mPrintJobId, printJobInfo.getAdvancedOptions());
+
+ // Update the content if needed.
+ if (updateContent) {
+ mController.update();
+ }
+ }
+
public void ensurePrinterSelected(PrinterId printerId) {
// If the printer is not present maybe the loader is not
// updated yet. In this case make a note and as soon as
@@ -1596,6 +1733,44 @@ public class PrintJobConfigActivity extends Activity {
}
}
+ private void registerAdvancedPrintOptionsButtonClickListener() {
+ Button advancedOptionsButton = (Button) findViewById(R.id.advanced_settings_button);
+ advancedOptionsButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ ComponentName serviceName = mCurrentPrinter.getId().getServiceName();
+ String activityName = getAdvancedOptionsActivityName(serviceName);
+ if (TextUtils.isEmpty(activityName)) {
+ return;
+ }
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setComponent(new ComponentName(serviceName.getPackageName(),
+ activityName));
+
+ List<ResolveInfo> resolvedActivities = getPackageManager()
+ .queryIntentActivities(intent, 0);
+ if (resolvedActivities.isEmpty()) {
+ return;
+ }
+ // The activity is a component name, therefore it is one or none.
+ if (resolvedActivities.get(0).activityInfo.exported) {
+ PrintJobInfo printJobInfo = mSpoolerProvider.getSpooler().getPrintJobInfo(
+ mPrintJobId, PrintManager.APP_ID_ANY);
+ intent.putExtra(PrintService.EXTRA_PRINT_JOB_INFO, printJobInfo);
+ // TODO: Make this an API for the next release.
+ intent.putExtra("android.intent.extra.print.EXTRA_PRINTER_INFO",
+ mCurrentPrinter);
+ try {
+ startActivityForResult(intent,
+ ACTIVITY_POPULATE_ADVANCED_PRINT_OPTIONS);
+ } catch (ActivityNotFoundException anfe) {
+ Log.e(LOG_TAG, "Error starting activity for intent: " + intent, anfe);
+ }
+ }
+ }
+ });
+ }
+
private void registerPrintButtonClickListener() {
Button printButton = (Button) findViewById(R.id.print_button);
printButton.setOnClickListener(new OnClickListener() {
@@ -1643,6 +1818,9 @@ public class PrintJobConfigActivity extends Activity {
mEditor.initialize();
mEditor.bindUi();
mEditor.reselectCurrentPrinter();
+ if (!mController.hasPerformedLayout()) {
+ mController.update();
+ }
}
});
}
@@ -1844,6 +2022,11 @@ public class PrintJobConfigActivity extends Activity {
mPageRangeEditText.setOnFocusChangeListener(mFocusListener);
mPageRangeEditText.addTextChangedListener(mRangeTextWatcher);
+ // Advanced options button.
+ mAdvancedPrintOptionsContainer = findViewById(R.id.advanced_settings_container);
+ mAdvancedOptionsButton = (Button) findViewById(R.id.advanced_settings_button);
+ registerAdvancedPrintOptionsButtonClickListener();
+
// Print button
mPrintButton = (Button) findViewById(R.id.print_button);
registerPrintButtonClickListener();
@@ -1862,6 +2045,7 @@ public class PrintJobConfigActivity extends Activity {
mRangeOptionsSpinner.setEnabled(false);
mPageRangeEditText.setEnabled(false);
mPrintButton.setEnabled(false);
+ mAdvancedOptionsButton.setEnabled(false);
return false;
}
@@ -1887,6 +2071,7 @@ public class PrintJobConfigActivity extends Activity {
mRangeOptionsSpinner.setEnabled(false);
mPageRangeEditText.setEnabled(false);
mPrintButton.setEnabled(false);
+ mAdvancedOptionsButton.setEnabled(false);
return false;
} else {
boolean someAttributeSelectionChanged = false;
@@ -2064,7 +2249,17 @@ public class PrintJobConfigActivity extends Activity {
mPageRangeTitle.setVisibility(View.INVISIBLE);
}
- // Print/Print preview
+ // Advanced print options
+ ComponentName serviceName = mCurrentPrinter.getId().getServiceName();
+ if (!TextUtils.isEmpty(getAdvancedOptionsActivityName(serviceName))) {
+ mAdvancedPrintOptionsContainer.setVisibility(View.VISIBLE);
+ mAdvancedOptionsButton.setEnabled(true);
+ } else {
+ mAdvancedPrintOptionsContainer.setVisibility(View.GONE);
+ mAdvancedOptionsButton.setEnabled(false);
+ }
+
+ // Print
if (mDestinationSpinner.getSelectedItemId()
!= DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF) {
String newText = getString(R.string.print_button);
@@ -2104,6 +2299,21 @@ public class PrintJobConfigActivity extends Activity {
}
}
+ private String getAdvancedOptionsActivityName(ComponentName serviceName) {
+ PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
+ List<PrintServiceInfo> printServices = printManager.getEnabledPrintServices();
+ final int printServiceCount = printServices.size();
+ for (int i = 0; i < printServiceCount; i ++) {
+ PrintServiceInfo printServiceInfo = printServices.get(i);
+ ServiceInfo serviceInfo = printServiceInfo.getResolveInfo().serviceInfo;
+ if (serviceInfo.name.equals(serviceName.getClassName())
+ && serviceInfo.packageName.equals(serviceName.getPackageName())) {
+ return printServiceInfo.getAdvancedOptionsActivityName();
+ }
+ }
+ return null;
+ }
+
private void setMediaSizeSpinnerSelectionNoCallback(int position) {
if (mMediaSizeSpinner.getSelectedItemPosition() != position) {
mOldMediaSizeSelectionIndex = position;
@@ -2303,8 +2513,6 @@ public class PrintJobConfigActivity extends Activity {
R.layout.printer_dropdown_item, parent, false);
}
- convertView.getLayoutParams().width = mDestinationSpinner.getWidth();
-
CharSequence title = null;
CharSequence subtitle = null;
Drawable icon = null;
@@ -2353,7 +2561,7 @@ public class PrintJobConfigActivity extends Activity {
iconView.setImageDrawable(icon);
iconView.setVisibility(View.VISIBLE);
} else {
- iconView.setVisibility(View.GONE);
+ iconView.setVisibility(View.INVISIBLE);
}
return convertView;
@@ -2703,4 +2911,32 @@ public class PrintJobConfigActivity extends Activity {
/* do noting - we are in the same process */
}
}
+
+ private static final class PrintDocumentAdapterObserver
+ extends IPrintDocumentAdapterObserver.Stub {
+ private final WeakReference<PrintJobConfigActivity> mWeakActvity;
+
+ public PrintDocumentAdapterObserver(PrintJobConfigActivity activity) {
+ mWeakActvity = new WeakReference<PrintJobConfigActivity>(activity);
+ }
+
+ @Override
+ public void onDestroy() {
+ final PrintJobConfigActivity activity = mWeakActvity.get();
+ if (activity != null) {
+ activity.mController.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (activity.mController != null) {
+ activity.mController.cancel();
+ }
+ if (activity.mEditor != null) {
+ activity.mEditor.cancel();
+ }
+ activity.finish();
+ }
+ });
+ }
+ }
+ }
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
index 636e245..615d667 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
@@ -20,6 +20,7 @@ import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.os.AsyncTask;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.ParcelFileDescriptor;
@@ -440,6 +441,7 @@ public final class PrintSpoolerService extends Service {
private void removeObsoletePrintJobs() {
synchronized (mLock) {
+ boolean persistState = false;
final int printJobCount = mPrintJobs.size();
for (int i = printJobCount - 1; i >= 0; i--) {
PrintJobInfo printJob = mPrintJobs.get(i);
@@ -449,9 +451,12 @@ public final class PrintSpoolerService extends Service {
Slog.i(LOG_TAG, "[REMOVE] " + printJob.getId().flattenToString());
}
removePrintJobFileLocked(printJob.getId());
+ persistState = true;
}
}
- mPersistanceManager.writeStateLocked();
+ if (persistState) {
+ mPersistanceManager.writeStateLocked();
+ }
}
}
@@ -543,7 +548,7 @@ public final class PrintSpoolerService extends Service {
final int printJobCount = mPrintJobs.size();
for (int i = 0; i < printJobCount; i++) {
PrintJobInfo printJob = mPrintJobs.get(i);
- if (isActiveState(printJob.getState())
+ if (isActiveState(printJob.getState()) && printJob.getPrinterId() != null
&& printJob.getPrinterId().getServiceName().equals(service)) {
return true;
}
@@ -623,6 +628,16 @@ public final class PrintSpoolerService extends Service {
}
}
+ public void setPrintJobAdvancedOptionsNoPersistence(PrintJobId printJobId,
+ Bundle advancedOptions) {
+ synchronized (mLock) {
+ PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
+ if (printJob != null) {
+ printJob.setAdvancedOptions(advancedOptions);
+ }
+ }
+ }
+
public void setPrintJobPrintDocumentInfoNoPersistence(PrintJobId printJobId,
PrintDocumentInfo info) {
synchronized (mLock) {
@@ -704,6 +719,14 @@ public final class PrintSpoolerService extends Service {
private static final String ATTR_STATE_REASON = "stateReason";
private static final String ATTR_CANCELLING = "cancelling";
+ private static final String TAG_ADVANCED_OPTIONS = "advancedOptions";
+ private static final String TAG_ADVANCED_OPTION = "advancedOption";
+ private static final String ATTR_KEY = "key";
+ private static final String ATTR_TYPE = "type";
+ private static final String ATTR_VALUE = "value";
+ private static final String TYPE_STRING = "string";
+ private static final String TYPE_INT = "int";
+
private static final String TAG_MEDIA_SIZE = "mediaSize";
private static final String TAG_RESOLUTION = "resolution";
private static final String TAG_MARGINS = "margins";
@@ -780,6 +803,10 @@ public final class PrintSpoolerService extends Service {
for (int j = 0; j < printJobCount; j++) {
PrintJobInfo printJob = printJobs.get(j);
+ if (!shouldPersistPrintJob(printJob)) {
+ continue;
+ }
+
serializer.startTag(null, TAG_JOB);
serializer.attribute(null, ATTR_ID, printJob.getId().flattenToString());
@@ -899,6 +926,30 @@ public final class PrintSpoolerService extends Service {
serializer.endTag(null, TAG_DOCUMENT_INFO);
}
+ Bundle advancedOptions = printJob.getAdvancedOptions();
+ if (advancedOptions != null) {
+ serializer.startTag(null, TAG_ADVANCED_OPTIONS);
+ for (String key : advancedOptions.keySet()) {
+ Object value = advancedOptions.get(key);
+ if (value instanceof String) {
+ String stringValue = (String) value;
+ serializer.startTag(null, TAG_ADVANCED_OPTION);
+ serializer.attribute(null, ATTR_KEY, key);
+ serializer.attribute(null, ATTR_TYPE, TYPE_STRING);
+ serializer.attribute(null, ATTR_VALUE, stringValue);
+ serializer.endTag(null, TAG_ADVANCED_OPTION);
+ } else if (value instanceof Integer) {
+ String intValue = Integer.toString((Integer) value);
+ serializer.startTag(null, TAG_ADVANCED_OPTION);
+ serializer.attribute(null, ATTR_KEY, key);
+ serializer.attribute(null, ATTR_TYPE, TYPE_INT);
+ serializer.attribute(null, ATTR_VALUE, intValue);
+ serializer.endTag(null, TAG_ADVANCED_OPTION);
+ }
+ }
+ serializer.endTag(null, TAG_ADVANCED_OPTIONS);
+ }
+
serializer.endTag(null, TAG_JOB);
if (DEBUG_PERSISTENCE) {
@@ -1027,6 +1078,7 @@ public final class PrintSpoolerService extends Service {
skipEmptyTextTags(parser);
expect(parser, XmlPullParser.END_TAG, TAG_PAGE_RANGE);
parser.next();
+ skipEmptyTextTags(parser);
}
if (pageRanges != null) {
PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
@@ -1057,8 +1109,8 @@ public final class PrintSpoolerService extends Service {
final int labelResId = (labelResIdString != null)
? Integer.parseInt(labelResIdString) : 0;
label = parser.getAttributeValue(null, ATTR_LABEL);
- MediaSize mediaSize = new MediaSize(id, label, packageName, labelResId,
- widthMils, heightMils);
+ MediaSize mediaSize = new MediaSize(id, label, packageName,
+ widthMils, heightMils, labelResId);
builder.setMediaSize(mediaSize);
parser.next();
skipEmptyTextTags(parser);
@@ -1127,6 +1179,32 @@ public final class PrintSpoolerService extends Service {
parser.next();
}
+ skipEmptyTextTags(parser);
+ if (accept(parser, XmlPullParser.START_TAG, TAG_ADVANCED_OPTIONS)) {
+ parser.next();
+ skipEmptyTextTags(parser);
+ Bundle advancedOptions = new Bundle();
+ while (accept(parser, XmlPullParser.START_TAG, TAG_ADVANCED_OPTION)) {
+ String key = parser.getAttributeValue(null, ATTR_KEY);
+ String value = parser.getAttributeValue(null, ATTR_VALUE);
+ String type = parser.getAttributeValue(null, ATTR_TYPE);
+ if (TYPE_STRING.equals(type)) {
+ advancedOptions.putString(key, value);
+ } else if (TYPE_INT.equals(type)) {
+ advancedOptions.putInt(key, Integer.valueOf(value));
+ }
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_ADVANCED_OPTION);
+ parser.next();
+ skipEmptyTextTags(parser);
+ }
+ printJob.setAdvancedOptions(advancedOptions);
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_ADVANCED_OPTIONS);
+ parser.next();
+ }
+
mPrintJobs.add(printJob);
if (DEBUG_PERSISTENCE) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java
index fd14af9..d9ccb5d 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java
@@ -137,4 +137,15 @@ final class RemotePrintDocumentAdapter {
Log.e(LOG_TAG, "Error calling finish()", re);
}
}
+
+ public void cancel() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "cancel()");
+ }
+ try {
+ mRemoteInterface.cancel();
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error calling cancel()", re);
+ }
+ }
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java b/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
index be94ba4..fe5920c 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
@@ -22,7 +22,6 @@ import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentTransaction;
-import android.app.ListFragment;
import android.app.LoaderManager;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -47,12 +46,17 @@ import android.printservice.PrintServiceInfo;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
+import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.Filter;
@@ -68,7 +72,7 @@ import java.util.List;
/**
* This is a fragment for selecting a printer.
*/
-public final class SelectPrinterFragment extends ListFragment {
+public final class SelectPrinterFragment extends Fragment {
private static final String LOG_TAG = "SelectPrinterFragment";
@@ -80,9 +84,13 @@ public final class SelectPrinterFragment extends ListFragment {
private static final String FRAGMRNT_ARGUMENT_PRINT_SERVICE_INFOS =
"FRAGMRNT_ARGUMENT_PRINT_SERVICE_INFOS";
+ private static final String EXTRA_PRINTER_ID = "EXTRA_PRINTER_ID";
+
private final ArrayList<PrintServiceInfo> mAddPrinterServices =
new ArrayList<PrintServiceInfo>();
+ private ListView mListView;
+
private AnnounceFilterResult mAnnounceFilterResult;
public static interface OnPrinterSelectedListener {
@@ -97,8 +105,12 @@ public final class SelectPrinterFragment extends ListFragment {
}
@Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View content = inflater.inflate(R.layout.select_printer_fragment, container, false);
+
+ // Hook up the list view.
+ mListView = (ListView) content.findViewById(android.R.id.list);
final DestinationAdapter adapter = new DestinationAdapter();
adapter.registerDataSetObserver(new DataSetObserver() {
@Override
@@ -115,7 +127,28 @@ public final class SelectPrinterFragment extends ListFragment {
}
}
});
- setListAdapter(adapter);
+ mListView.setAdapter(adapter);
+
+ mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ if (!((DestinationAdapter) mListView.getAdapter()).isActionable(position)) {
+ return;
+ }
+ PrinterInfo printer = (PrinterInfo) mListView.getAdapter().getItem(position);
+ Activity activity = getActivity();
+ if (activity instanceof OnPrinterSelectedListener) {
+ ((OnPrinterSelectedListener) activity).onPrinterSelected(printer.getId());
+ } else {
+ throw new IllegalStateException("the host activity must implement"
+ + " OnPrinterSelectedListener");
+ }
+ }
+ });
+
+ registerForContextMenu(mListView);
+
+ return content;
}
@Override
@@ -133,7 +166,7 @@ public final class SelectPrinterFragment extends ListFragment {
@Override
public boolean onQueryTextChange(String searchString) {
- ((DestinationAdapter) getListAdapter()).getFilter().filter(searchString);
+ ((DestinationAdapter) mListView.getAdapter()).getFilter().filter(searchString);
return true;
}
});
@@ -162,6 +195,62 @@ public final class SelectPrinterFragment extends ListFragment {
}
@Override
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
+ if (view == mListView) {
+ final int position = ((AdapterContextMenuInfo) menuInfo).position;
+ PrinterInfo printer = (PrinterInfo) mListView.getAdapter().getItem(position);
+
+ menu.setHeaderTitle(printer.getName());
+
+ // Add the select menu item if applicable.
+ if (printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE) {
+ MenuItem selectItem = menu.add(Menu.NONE, R.string.print_select_printer,
+ Menu.NONE, R.string.print_select_printer);
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_PRINTER_ID, printer.getId());
+ selectItem.setIntent(intent);
+ }
+
+ // Add the forget menu item if applicable.
+ FusedPrintersProvider provider = (FusedPrintersProvider) (Loader<?>)
+ getLoaderManager().getLoader(LOADER_ID_PRINTERS_LOADER);
+ if (provider.isFavoritePrinter(printer.getId())) {
+ MenuItem forgetItem = menu.add(Menu.NONE, R.string.print_forget_printer,
+ Menu.NONE, R.string.print_forget_printer);
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_PRINTER_ID, printer.getId());
+ forgetItem.setIntent(intent);
+ }
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.string.print_select_printer: {
+ PrinterId printerId = (PrinterId) item.getIntent().getParcelableExtra(
+ EXTRA_PRINTER_ID);
+ Activity activity = getActivity();
+ if (activity instanceof OnPrinterSelectedListener) {
+ ((OnPrinterSelectedListener) activity).onPrinterSelected(printerId);
+ } else {
+ throw new IllegalStateException("the host activity must implement"
+ + " OnPrinterSelectedListener");
+ }
+ } return true;
+
+ case R.string.print_forget_printer: {
+ PrinterId printerId = (PrinterId) item.getIntent().getParcelableExtra(
+ EXTRA_PRINTER_ID);
+ FusedPrintersProvider provider = (FusedPrintersProvider) (Loader<?>)
+ getLoaderManager().getLoader(LOADER_ID_PRINTERS_LOADER);
+ provider.forgetFavoritePrinter(printerId);
+ } return true;
+ }
+ return false;
+ }
+
+ @Override
public void onResume() {
updateAddPrintersAdapter();
getActivity().invalidateOptionsMenu();
@@ -177,18 +266,6 @@ public final class SelectPrinterFragment extends ListFragment {
}
@Override
- public void onListItemClick(ListView list, View view, int position, long id) {
- PrinterInfo printer = (PrinterInfo) list.getAdapter().getItem(position);
- Activity activity = getActivity();
- if (activity instanceof OnPrinterSelectedListener) {
- ((OnPrinterSelectedListener) activity).onPrinterSelected(printer.getId());
- } else {
- throw new IllegalStateException("the host activity must implement"
- + " OnPrinterSelectedListener");
- }
- }
-
- @Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_add_printer) {
showAddPrinterSelectionDialog();
@@ -260,9 +337,9 @@ public final class SelectPrinterFragment extends ListFragment {
}
public void updateEmptyView(DestinationAdapter adapter) {
- if (getListView().getEmptyView() == null) {
+ if (mListView.getEmptyView() == null) {
View emptyView = getActivity().findViewById(R.id.empty_print_state);
- getListView().setEmptyView(emptyView);
+ mListView.setEmptyView(emptyView);
}
TextView titleView = (TextView) getActivity().findViewById(R.id.title);
View progressBar = getActivity().findViewById(R.id.progress_bar);
@@ -450,10 +527,10 @@ public final class SelectPrinterFragment extends ListFragment {
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getActivity().getLayoutInflater().inflate(
- R.layout.printer_dropdown_item, parent, false);
+ R.layout.printer_list_item, parent, false);
}
- convertView.setEnabled(isEnabled(position));
+ convertView.setEnabled(isActionable(position));
CharSequence title = null;
CharSequence subtitle = null;
@@ -495,8 +572,7 @@ public final class SelectPrinterFragment extends ListFragment {
return convertView;
}
- @Override
- public boolean isEnabled(int position) {
+ public boolean isActionable(int position) {
PrinterInfo printer = (PrinterInfo) getItem(position);
return printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
}
@@ -539,16 +615,16 @@ public final class SelectPrinterFragment extends ListFragment {
public void post() {
remove();
- getListView().postDelayed(this, SEARCH_RESULT_ANNOUNCEMENT_DELAY);
+ mListView.postDelayed(this, SEARCH_RESULT_ANNOUNCEMENT_DELAY);
}
public void remove() {
- getListView().removeCallbacks(this);
+ mListView.removeCallbacks(this);
}
@Override
public void run() {
- final int count = getListView().getAdapter().getCount();
+ final int count = mListView.getAdapter().getCount();
final String text;
if (count <= 0) {
text = getString(R.string.print_no_printers);
@@ -556,7 +632,7 @@ public final class SelectPrinterFragment extends ListFragment {
text = getActivity().getResources().getQuantityString(
R.plurals.print_search_result_count_utterance, count, count);
}
- getListView().announceForAccessibility(text);
+ mListView.announceForAccessibility(text);
}
}
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index ffb4c20..29e8d1d 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -5,7 +5,6 @@
>
<!-- Standard permissions granted to the shell. -->
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
@@ -64,6 +63,7 @@
<uses-permission android:name="android.permission.SET_SCREEN_COMPATIBILITY" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.MANAGE_USERS" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 09ac2da..8d6fe41 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -71,6 +71,9 @@
<!-- Keyguard -->
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
+ <!-- Wifi Display -->
+ <uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
+
<application
android:persistent="true"
android:allowClearUserData="false"
diff --git a/packages/SystemUI/res/drawable-hdpi/bg_protect.9.png b/packages/SystemUI/res/drawable-hdpi/bg_protect.9.png
new file mode 100644
index 0000000..5bbfa4f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/bg_protect.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png
index 2d8d074..693abf5 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_available.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_available.png
new file mode 100644
index 0000000..1c3518a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_available.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connected.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connected.png
new file mode 100644
index 0000000..9dbc65e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connected.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_0.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_0.png
new file mode 100644
index 0000000..ddb002d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_1.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_1.png
new file mode 100644
index 0000000..43b7ef2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_2.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_2.png
new file mode 100644
index 0000000..1d8b7ee
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_ime.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_ime.png
index 7220968..e3b3eeb 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_ime.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_location.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_location.png
deleted file mode 100644
index c561446..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_location.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_remote_display.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_remote_display.png
deleted file mode 100644
index 02d7fda..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_remote_display.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_remote_display_connected.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_remote_display_connected.png
deleted file mode 100644
index 263f07c..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_remote_display_connected.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_settings.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_settings.png
index a53108d..cfa539f 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_settings.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_settings.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_camera.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_camera.png
index 8f4cb64..c6f03c4 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_camera.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_camera.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/search_light.png b/packages/SystemUI/res/drawable-hdpi/search_light.png
index 116b1f0..3c0dc4e 100644
--- a/packages/SystemUI/res/drawable-hdpi/search_light.png
+++ b/packages/SystemUI/res/drawable-hdpi/search_light.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/search_light_land.png b/packages/SystemUI/res/drawable-hdpi/search_light_land.png
new file mode 100644
index 0000000..731f19b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/search_light_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-hdpi/bg_protect.9.png b/packages/SystemUI/res/drawable-land-hdpi/bg_protect.9.png
new file mode 100644
index 0000000..1a58144
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-hdpi/bg_protect.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-mdpi/bg_protect.9.png b/packages/SystemUI/res/drawable-land-mdpi/bg_protect.9.png
new file mode 100644
index 0000000..a12519e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-mdpi/bg_protect.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-xhdpi/bg_protect.9.png b/packages/SystemUI/res/drawable-land-xhdpi/bg_protect.9.png
new file mode 100644
index 0000000..ce41454
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-xhdpi/bg_protect.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-xxhdpi/bg_protect.9.png b/packages/SystemUI/res/drawable-land-xxhdpi/bg_protect.9.png
new file mode 100644
index 0000000..b0b4561
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-xxhdpi/bg_protect.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/bg_protect.9.png b/packages/SystemUI/res/drawable-mdpi/bg_protect.9.png
new file mode 100644
index 0000000..2856e09
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/bg_protect.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png
index 399db00..15340d3 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_available.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_available.png
new file mode 100644
index 0000000..11b2134
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_available.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connected.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connected.png
new file mode 100644
index 0000000..a858573
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connected.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_0.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_0.png
new file mode 100644
index 0000000..04de5d7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_1.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_1.png
new file mode 100644
index 0000000..caea37e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_2.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_2.png
new file mode 100644
index 0000000..b66aa46
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_ime.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_ime.png
index 8c2dc68..cc81794 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_ime.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_location.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_location.png
deleted file mode 100644
index e285bba..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_location.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_remote_display.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_remote_display.png
deleted file mode 100644
index 09ae409..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_remote_display.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_remote_display_connected.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_remote_display_connected.png
deleted file mode 100644
index 780cfc8..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_remote_display_connected.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_settings.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_settings.png
index 0f7607b..e6237eb 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_settings.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_settings.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_camera.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_camera.png
index 2142147..1c2d7aa 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_camera.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_camera.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/search_light.png b/packages/SystemUI/res/drawable-mdpi/search_light.png
index 7a70984..8010ce7 100644
--- a/packages/SystemUI/res/drawable-mdpi/search_light.png
+++ b/packages/SystemUI/res/drawable-mdpi/search_light.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/search_light_land.png b/packages/SystemUI/res/drawable-mdpi/search_light_land.png
new file mode 100644
index 0000000..a4d82f0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/search_light_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/bg_protect.9.png b/packages/SystemUI/res/drawable-xhdpi/bg_protect.9.png
new file mode 100644
index 0000000..72269f2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/bg_protect.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png
index c0032e2..e3cc9b0 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_available.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_available.png
new file mode 100644
index 0000000..10ebcd5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_available.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connected.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connected.png
new file mode 100644
index 0000000..fef43b8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connected.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_0.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_0.png
new file mode 100644
index 0000000..05e3267
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_1.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_1.png
new file mode 100644
index 0000000..ef42b27
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_2.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_2.png
new file mode 100644
index 0000000..fc1c95e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_ime.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_ime.png
index bffbf55..65d15b5 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_ime.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_location.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_location.png
deleted file mode 100644
index a52dc8d..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_location.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_remote_display.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_remote_display.png
deleted file mode 100644
index 48f90ac..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_remote_display.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_remote_display_connected.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_remote_display_connected.png
deleted file mode 100644
index 621c045..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_remote_display_connected.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_settings.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_settings.png
index 4ce9460..208089d 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_settings.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_settings.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_camera.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_camera.png
index b0ea8e0..fbd4d6b 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_camera.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_camera.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/search_light.png b/packages/SystemUI/res/drawable-xhdpi/search_light.png
index e2aed09..6d46fdd 100644
--- a/packages/SystemUI/res/drawable-xhdpi/search_light.png
+++ b/packages/SystemUI/res/drawable-xhdpi/search_light.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/search_light_land.png b/packages/SystemUI/res/drawable-xhdpi/search_light_land.png
new file mode 100644
index 0000000..b62c74e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/search_light_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/bg_protect.9.png b/packages/SystemUI/res/drawable-xxhdpi/bg_protect.9.png
new file mode 100644
index 0000000..efc9b04
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/bg_protect.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png
index a3cc08d..e15981a 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_available.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_available.png
new file mode 100644
index 0000000..68b1b7c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_available.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connected.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connected.png
new file mode 100644
index 0000000..8a8f890
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connected.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_0.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_0.png
new file mode 100644
index 0000000..12d4a01
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_1.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_1.png
new file mode 100644
index 0000000..3cb4421
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_2.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_2.png
new file mode 100644
index 0000000..4620b3a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_ime.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_ime.png
index ab841d2..1a5d26a 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_ime.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_location.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_location.png
deleted file mode 100644
index 3175636..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_location.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_remote_display.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_remote_display.png
deleted file mode 100644
index b07be828..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_remote_display.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_remote_display_connected.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_remote_display_connected.png
deleted file mode 100644
index f02d0ab..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_remote_display_connected.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_settings.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_settings.png
index 74a78dc..452942e 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_settings.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_settings.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_camera.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_camera.png
index aac3428..86df881 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_camera.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_camera.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/search_light.png b/packages/SystemUI/res/drawable-xxhdpi/search_light.png
index e5ef85d..7742207 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/search_light.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/search_light.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/search_light_land.png b/packages/SystemUI/res/drawable-xxhdpi/search_light_land.png
new file mode 100644
index 0000000..f364577
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/search_light_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_qs_cast_connecting.xml b/packages/SystemUI/res/drawable/ic_qs_cast_connecting.xml
new file mode 100644
index 0000000..70db2a9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_cast_connecting.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2013, 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.
+ */
+-->
+<animation-list
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:oneshot="false">
+ <item android:drawable="@drawable/ic_qs_cast_connecting_0" android:duration="500" />
+ <item android:drawable="@drawable/ic_qs_cast_connecting_1" android:duration="500" />
+ <item android:drawable="@drawable/ic_qs_cast_connecting_2" android:duration="500" />
+ <item android:drawable="@drawable/ic_qs_cast_connecting_1" android:duration="500" />
+</animation-list>
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
index b2ba25a..0c0be29 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
@@ -24,6 +24,7 @@
android:id="@+id/recents_root"
android:layout_height="match_parent"
android:layout_width="match_parent"
+ android:foreground="@drawable/bg_protect"
systemui:recentItemLayout="@layout/status_bar_recent_item"
>
<FrameLayout
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
index aa365ae..5488a87 100644
--- a/packages/SystemUI/res/layout/navigation_bar.xml
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -311,7 +311,7 @@
android:layout_height="80dp"
android:layout_width="match_parent"
android:layout_gravity="center_vertical"
- android:src="@drawable/search_light"
+ android:src="@drawable/search_light_land"
android:scaleType="center"
android:visibility="gone"
android:contentDescription="@string/accessibility_search_light"
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index d7312df..eb66908 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -95,12 +95,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
- <!-- battery must be padded below by 2px to match assets -->
+ <!-- battery must be padded below to match assets -->
<com.android.systemui.BatteryMeterView
android:id="@+id/battery"
android:layout_height="16dp"
android:layout_width="10.5dp"
- android:layout_marginBottom="2px"
+ android:layout_marginBottom="0.33dp"
android:layout_marginStart="4dip"
/>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_recent_panel.xml b/packages/SystemUI/res/layout/status_bar_recent_panel.xml
index e41475b..2f3968d 100644
--- a/packages/SystemUI/res/layout/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout/status_bar_recent_panel.xml
@@ -24,6 +24,7 @@
android:id="@+id/recents_root"
android:layout_height="match_parent"
android:layout_width="match_parent"
+ android:foreground="@drawable/bg_protect"
systemui:recentItemLayout="@layout/status_bar_recent_item"
>
<FrameLayout
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 58865ab..e36ca8e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -382,6 +382,8 @@
<string name="accessibility_quick_settings_airplane">Airplane Mode <xliff:g id="state" example="Off">%s</xliff:g>.</string>
<!-- Content description of the bluetooth tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_bluetooth">Bluetooth <xliff:g id="state" example="Off">%s</xliff:g>.</string>
+ <!-- Content description of the location tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_location">Location <xliff:g id="state" example="Off">%s</xliff:g>.</string>
<!-- Content description of the alarm tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_alarm">Alarm set for <xliff:g id="time" example="Wed 3:30 PM">%s</xliff:g>.</string>
@@ -488,10 +490,8 @@
<string name="quick_settings_wifi_no_network">No Network</string>
<!-- QuickSettings: Wifi (Off) [CHAR LIMIT=NONE] -->
<string name="quick_settings_wifi_off_label">Wi-Fi Off</string>
- <!-- QuickSettings: Wifi display [CHAR LIMIT=NONE] -->
- <string name="quick_settings_wifi_display_label">Wi-Fi Display</string>
- <!-- QuickSettings: Wifi display [CHAR LIMIT=NONE] -->
- <string name="quick_settings_wifi_display_no_connection_label">Wireless Display</string>
+ <!-- QuickSettings: Remote display [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_remote_display_no_connection_label">Cast Screen</string>
<!-- QuickSettings: Brightness dialog title [CHAR LIMIT=NONE] -->
<string name="quick_settings_brightness_dialog_title">Brightness</string>
<!-- QuickSettings: Brightness dialog auto brightness button [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index b6e03e1..13aafb2 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -66,7 +66,7 @@ public class BatteryMeterView extends View implements DemoMode {
private final RectF mFrame = new RectF();
private final RectF mButtonFrame = new RectF();
private final RectF mClipFrame = new RectF();
- private final Rect mBoltFrame = new Rect();
+ private final RectF mBoltFrame = new RectF();
private class BatteryTracker extends BroadcastReceiver {
public static final int UNKNOWN_LEVEL = -1;
@@ -319,10 +319,10 @@ public class BatteryMeterView extends View implements DemoMode {
if (tracker.plugged) {
// draw the bolt
- final int bl = (int)(mFrame.left + mFrame.width() / 4.5f);
- final int bt = (int)(mFrame.top + mFrame.height() / 6f);
- final int br = (int)(mFrame.right - mFrame.width() / 7f);
- final int bb = (int)(mFrame.bottom - mFrame.height() / 10f);
+ final float bl = mFrame.left + mFrame.width() / 4.5f;
+ final float bt = mFrame.top + mFrame.height() / 6f;
+ final float br = mFrame.right - mFrame.width() / 7f;
+ final float bb = mFrame.bottom - mFrame.height() / 10f;
if (mBoltFrame.left != bl || mBoltFrame.top != bt
|| mBoltFrame.right != br || mBoltFrame.bottom != bb) {
mBoltFrame.set(bl, bt, br, bb);
diff --git a/packages/SystemUI/src/com/android/systemui/DessertCase.java b/packages/SystemUI/src/com/android/systemui/DessertCase.java
index dd4c018..d797e38 100644
--- a/packages/SystemUI/src/com/android/systemui/DessertCase.java
+++ b/packages/SystemUI/src/com/android/systemui/DessertCase.java
@@ -36,7 +36,8 @@ public class DessertCase extends Activity {
if (pm.getComponentEnabledSetting(cn) != PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
Slog.v("DessertCase", "ACHIEVEMENT UNLOCKED");
pm.setComponentEnabledSetting(cn,
- PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP);
}
mView = new DessertCaseView(this);
diff --git a/packages/SystemUI/src/com/android/systemui/DessertCaseView.java b/packages/SystemUI/src/com/android/systemui/DessertCaseView.java
index 90de65e..4147155 100644
--- a/packages/SystemUI/src/com/android/systemui/DessertCaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/DessertCaseView.java
@@ -17,6 +17,7 @@
package com.android.systemui;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
@@ -166,15 +167,19 @@ public class DessertCaseView extends FrameLayout {
if (mCellSize < 512) { // assuming 512x512 images
opts.inSampleSize = 2;
}
+ opts.inMutable = true;
+ Bitmap loaded = null;
for (int[] list : new int[][] { PASTRIES, RARE_PASTRIES, XRARE_PASTRIES, XXRARE_PASTRIES }) {
for (int resid : list) {
- final BitmapDrawable d = new BitmapDrawable(res,
- convertToAlphaMask(BitmapFactory.decodeResource(res, resid, opts)));
+ opts.inBitmap = loaded;
+ loaded = BitmapFactory.decodeResource(res, resid, opts);
+ final BitmapDrawable d = new BitmapDrawable(res, convertToAlphaMask(loaded));
d.setColorFilter(new ColorMatrixColorFilter(ALPHA_MASK));
d.setBounds(0, 0, mCellSize, mCellSize);
mDrawables.append(resid, d);
}
}
+ loaded = null;
if (DEBUG) setWillNotDraw(false);
}
@@ -304,8 +309,6 @@ public class DessertCaseView extends FrameLayout {
v.getOverlay().add(d);
}
- v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-
lp.width = lp.height = mCellSize;
addView(v, lp);
place(v, pt, false);
@@ -314,7 +317,7 @@ public class DessertCaseView extends FrameLayout {
v.setScaleX(0.5f * s);
v.setScaleY(0.5f * s);
v.setAlpha(0f);
- v.animate().scaleX(s).scaleY(s).alpha(1f).setDuration(animationLen);
+ v.animate().withLayer().scaleX(s).scaleY(s).alpha(1f).setDuration(animationLen);
}
}
}
@@ -323,6 +326,21 @@ public class DessertCaseView extends FrameLayout {
place(v, new Point(irand(0, mColumns), irand(0, mRows)), animate);
}
+ // we don't have .withLayer() on general Animators
+ private final Animator.AnimatorListener makeHardwareLayerListener(final View v) {
+ return new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animator) {
+ v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ v.buildLayer();
+ }
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ v.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ };
+ }
+
private final HashSet<View> tmpSet = new HashSet<View>();
public synchronized void place(View v, Point pt, boolean animate) {
final int i = pt.x;
@@ -370,7 +388,8 @@ public class DessertCaseView extends FrameLayout {
if (squatter != v) {
squatter.setTag(TAG_POS, null);
if (animate) {
- squatter.animate().scaleX(0.5f).scaleY(0.5f).alpha(0)
+ squatter.animate().withLayer()
+ .scaleX(0.5f).scaleY(0.5f).alpha(0)
.setDuration(DURATION)
.setInterpolator(new AccelerateInterpolator())
.setListener(new Animator.AnimatorListener() {
@@ -397,6 +416,7 @@ public class DessertCaseView extends FrameLayout {
if (animate) {
v.bringToFront();
+
AnimatorSet set1 = new AnimatorSet();
set1.playTogether(
ObjectAnimator.ofFloat(v, View.SCALE_X, (float) scale),
@@ -404,7 +424,6 @@ public class DessertCaseView extends FrameLayout {
);
set1.setInterpolator(new AnticipateOvershootInterpolator());
set1.setDuration(DURATION);
- set1.start();
AnimatorSet set2 = new AnimatorSet();
set2.playTogether(
@@ -414,6 +433,10 @@ public class DessertCaseView extends FrameLayout {
);
set2.setInterpolator(new DecelerateInterpolator());
set2.setDuration(DURATION);
+
+ set1.addListener(makeHardwareLayerListener(v));
+
+ set1.start();
set2.start();
} else {
v.setX(i * mCellSize + (scale-1) * mCellSize /2);
@@ -473,7 +496,6 @@ public class DessertCaseView extends FrameLayout {
}
public static class RescalingContainer extends FrameLayout {
- private static final int SYSTEM_UI_MODE_800 = 0x00000800;
private DessertCaseView mView;
private float mDarkness;
@@ -486,7 +508,7 @@ public class DessertCaseView extends FrameLayout {
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- | SYSTEM_UI_MODE_800
+ | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java b/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java
index 9839fe9..7d3e870 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java
@@ -38,7 +38,7 @@ public class AnimatedImageView extends ImageView {
}
private void updateAnim() {
- Drawable drawable = getDrawable();
+ Drawable drawable = mAttached ? getDrawable() : null;
if (mAttached && mAnim != null) {
mAnim.stop();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 6a2bc5f..ed00398 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
-import android.app.KeyguardManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
@@ -70,6 +69,7 @@ import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SearchPanelView;
import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
import com.android.systemui.statusbar.policy.NotificationRowLayout;
import java.util.ArrayList;
@@ -128,7 +128,6 @@ public abstract class BaseStatusBar extends SystemUI implements
protected boolean mUseHeadsUp = false;
protected IDreamManager mDreamManager;
- KeyguardManager mKeyguardManager;
PowerManager mPowerManager;
protected int mRowHeight;
@@ -221,7 +220,6 @@ public abstract class BaseStatusBar extends SystemUI implements
mDreamManager = IDreamManager.Stub.asInterface(
ServiceManager.checkService(DreamService.DREAM_SERVICE));
- mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mProvisioningObserver.onChange(false); // set up
@@ -749,9 +747,7 @@ public abstract class BaseStatusBar extends SystemUI implements
Log.w(TAG, "Sending contentIntent failed: " + e);
}
- KeyguardManager kgm =
- (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- if (kgm != null) kgm.exitKeyguardSecurely(null);
+ KeyguardTouchDelegate.getInstance(mContext).dismiss();
}
try {
@@ -1056,10 +1052,12 @@ public abstract class BaseStatusBar extends SystemUI implements
boolean isAllowed = notification.extras.getInt(Notification.EXTRA_AS_HEADS_UP,
Notification.HEADS_UP_ALLOWED) != Notification.HEADS_UP_NEVER;
+ final KeyguardTouchDelegate keyguard = KeyguardTouchDelegate.getInstance(mContext);
boolean interrupt = (isFullscreen || (isHighPriority && isNoisy))
&& isAllowed
&& mPowerManager.isScreenOn()
- && !mKeyguardManager.isKeyguardLocked();
+ && !keyguard.isShowingAndNotHidden()
+ && !keyguard.isInputRestricted();
try {
interrupt = interrupt && !mDreamManager.isDreaming();
} catch (RemoteException e) {
@@ -1087,8 +1085,7 @@ public abstract class BaseStatusBar extends SystemUI implements
}
public boolean inKeyguardRestrictedInputMode() {
- KeyguardManager km = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- return km.inKeyguardRestrictedInputMode();
+ return KeyguardTouchDelegate.getInstance(mContext).isInputRestricted();
}
public void setInteracting(int barWindow, boolean interacting) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index e8173b7..39333d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -55,8 +55,7 @@ public class CommandQueue extends IStatusBar.Stub {
private static final int MSG_TOGGLE_RECENT_APPS = 13 << MSG_SHIFT;
private static final int MSG_PRELOAD_RECENT_APPS = 14 << MSG_SHIFT;
private static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 15 << MSG_SHIFT;
- private static final int MSG_SET_NAVIGATION_ICON_HINTS = 16 << MSG_SHIFT;
- private static final int MSG_SET_WINDOW_STATE = 17 << MSG_SHIFT;
+ private static final int MSG_SET_WINDOW_STATE = 16 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -98,7 +97,6 @@ public class CommandQueue extends IStatusBar.Stub {
public void showSearchPanel();
public void hideSearchPanel();
public void cancelPreloadRecentApps();
- public void setNavigationIconHints(int hints);
public void setWindowState(int window, int state);
}
@@ -227,13 +225,6 @@ public class CommandQueue extends IStatusBar.Stub {
}
}
- public void setNavigationIconHints(int hints) {
- synchronized (mList) {
- mHandler.removeMessages(MSG_SET_NAVIGATION_ICON_HINTS);
- mHandler.obtainMessage(MSG_SET_NAVIGATION_ICON_HINTS, hints, 0, null).sendToTarget();
- }
- }
-
public void setWindowState(int window, int state) {
synchronized (mList) {
// don't coalesce these
@@ -318,9 +309,6 @@ public class CommandQueue extends IStatusBar.Stub {
case MSG_CANCEL_PRELOAD_RECENT_APPS:
mCallbacks.cancelPreloadRecentApps();
break;
- case MSG_SET_NAVIGATION_ICON_HINTS:
- mCallbacks.setNavigationIconHints(msg.arg1);
- break;
case MSG_SET_WINDOW_STATE:
mCallbacks.setWindowState(msg.arg1, msg.arg2);
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index 1c8702a..cb17ac6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -16,16 +16,20 @@
package com.android.systemui.statusbar.phone;
-import android.animation.ArgbEvaluator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.animation.TimeInterpolator;
import android.app.ActivityManager;
+import android.content.Context;
import android.content.res.Resources;
-import android.graphics.drawable.ColorDrawable;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.TransitionDrawable;
+import android.os.SystemClock;
import android.util.Log;
import android.view.View;
+import android.view.animation.LinearInterpolator;
import com.android.systemui.R;
@@ -45,43 +49,16 @@ public class BarTransitions {
private final String mTag;
private final View mView;
private final boolean mSupportsTransitions = ActivityManager.isHighEndGfx();
-
- private final int mOpaque;
- private final int mSemiTransparent;
+ private final BarBackgroundDrawable mBarBackground;
private int mMode;
- private ValueAnimator mColorDrawableAnimator;
- private boolean mColorDrawableShowing;
-
- private final ColorDrawable mColorDrawable;
- private final TransitionDrawable mTransitionDrawable;
- private final AnimatorUpdateListener mAnimatorListener = new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animator) {
- mColorDrawable.setColor((Integer) animator.getAnimatedValue());
- }
- };
public BarTransitions(View view, int gradientResourceId) {
mTag = "BarTransitions." + view.getClass().getSimpleName();
mView = view;
- final Resources res = mView.getContext().getResources();
-
- if (DEBUG_COLORS) {
- mOpaque = 0xff0000ff;
- mSemiTransparent = 0x7f0000ff;
- } else {
- mOpaque = res.getColor(R.color.system_bar_background_opaque);
- mSemiTransparent = res.getColor(R.color.system_bar_background_semi_transparent);
- }
-
- mColorDrawable = new ColorDrawable(mOpaque);
- mTransitionDrawable = new TransitionDrawable(
- new Drawable[] { res.getDrawable(gradientResourceId), mColorDrawable });
- mTransitionDrawable.setCrossFadeEnabled(true);
- mTransitionDrawable.resetTransition();
+ mBarBackground = new BarBackgroundDrawable(mView.getContext(), gradientResourceId);
if (mSupportsTransitions) {
- mView.setBackground(mTransitionDrawable);
+ mView.setBackground(mBarBackground);
}
}
@@ -100,65 +77,144 @@ public class BarTransitions {
}
}
- private Integer getBackgroundColor(int mode) {
- if (mode == MODE_SEMI_TRANSPARENT) return mSemiTransparent;
- if (mode == MODE_OPAQUE) return mOpaque;
- if (mode == MODE_LIGHTS_OUT) return mOpaque;
- return null;
- }
-
protected void onTransition(int oldMode, int newMode, boolean animate) {
applyModeBackground(oldMode, newMode, animate);
}
protected void applyModeBackground(int oldMode, int newMode, boolean animate) {
- if (DEBUG) Log.d(mTag, String.format("applyModeBackground %s animate=%s",
- modeToString(newMode), animate));
- cancelColorAnimation();
- Integer oldColor = getBackgroundColor(oldMode);
- Integer newColor = getBackgroundColor(newMode);
- if (newColor != null) {
- if (animate && oldColor != null && !oldColor.equals(newColor)) {
- startColorAnimation(oldColor, newColor);
- } else if (!newColor.equals(mColorDrawable.getColor())) {
- if (DEBUG) Log.d(mTag, String.format("setColor = %08x", newColor));
- mColorDrawable.setColor(newColor);
+ if (DEBUG) Log.d(mTag, String.format("applyModeBackground oldMode=%s newMode=%s animate=%s",
+ modeToString(oldMode), modeToString(newMode), animate));
+ mBarBackground.applyModeBackground(oldMode, newMode, animate);
+ }
+
+ public static String modeToString(int mode) {
+ if (mode == MODE_OPAQUE) return "MODE_OPAQUE";
+ if (mode == MODE_SEMI_TRANSPARENT) return "MODE_SEMI_TRANSPARENT";
+ if (mode == MODE_TRANSLUCENT) return "MODE_TRANSLUCENT";
+ if (mode == MODE_LIGHTS_OUT) return "MODE_LIGHTS_OUT";
+ throw new IllegalArgumentException("Unknown mode " + mode);
+ }
+
+ public void finishAnimations() {
+ mBarBackground.finishAnimation();
+ }
+
+ public void setContentVisible(boolean visible) {
+ // for subclasses
+ }
+
+ private static class BarBackgroundDrawable extends Drawable {
+ private final int mOpaque;
+ private final int mSemiTransparent;
+ private final Drawable mGradient;
+ private final TimeInterpolator mInterpolator;
+
+ private int mMode = -1;
+ private boolean mAnimating;
+ private long mStartTime;
+ private long mEndTime;
+
+ private int mGradientAlpha;
+ private int mColor;
+
+ private int mGradientAlphaStart;
+ private int mColorStart;
+
+ public BarBackgroundDrawable(Context context, int gradientResourceId) {
+ final Resources res = context.getResources();
+ if (DEBUG_COLORS) {
+ mOpaque = 0xff0000ff;
+ mSemiTransparent = 0x7f0000ff;
+ } else {
+ mOpaque = res.getColor(R.color.system_bar_background_opaque);
+ mSemiTransparent = res.getColor(R.color.system_bar_background_semi_transparent);
}
+ mGradient = res.getDrawable(gradientResourceId);
+ mInterpolator = new LinearInterpolator();
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ // noop
}
- if (newColor == null && mColorDrawableShowing) {
- if (DEBUG) Log.d(mTag, "Hide color layer");
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ // noop
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+ mGradient.setBounds(bounds);
+ }
+
+ public void applyModeBackground(int oldMode, int newMode, boolean animate) {
+ if (mMode == newMode) return;
+ mMode = newMode;
+ mAnimating = animate;
if (animate) {
- mTransitionDrawable.reverseTransition(BACKGROUND_DURATION);
- } else {
- mTransitionDrawable.resetTransition();
+ long now = SystemClock.elapsedRealtime();
+ mStartTime = now;
+ mEndTime = now + BACKGROUND_DURATION;
+ mGradientAlphaStart = mGradientAlpha;
+ mColorStart = mColor;
}
- mColorDrawableShowing = false;
- } else if (newColor != null && !mColorDrawableShowing) {
- if (DEBUG) Log.d(mTag, "Show color layer");
- mTransitionDrawable.startTransition(animate ? BACKGROUND_DURATION : 0);
- mColorDrawableShowing = true;
+ invalidateSelf();
}
- }
- private void startColorAnimation(int from, int to) {
- if (DEBUG) Log.d(mTag, String.format("startColorAnimation %08x -> %08x", from, to));
- mColorDrawableAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), from, to);
- mColorDrawableAnimator.addUpdateListener(mAnimatorListener);
- mColorDrawableAnimator.start();
- }
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
- private void cancelColorAnimation() {
- if (mColorDrawableAnimator != null && mColorDrawableAnimator.isStarted()) {
- mColorDrawableAnimator.cancel();
- mColorDrawableAnimator = null;
+ public void finishAnimation() {
+ if (mAnimating) {
+ mAnimating = false;
+ invalidateSelf();
+ }
}
- }
- public static String modeToString(int mode) {
- if (mode == MODE_OPAQUE) return "MODE_OPAQUE";
- if (mode == MODE_SEMI_TRANSPARENT) return "MODE_SEMI_TRANSPARENT";
- if (mode == MODE_TRANSLUCENT) return "MODE_TRANSLUCENT";
- if (mode == MODE_LIGHTS_OUT) return "MODE_LIGHTS_OUT";
- throw new IllegalArgumentException("Unknown mode " + mode);
+ @Override
+ public void draw(Canvas canvas) {
+ int targetGradientAlpha = 0, targetColor = 0;
+ if (mMode == MODE_TRANSLUCENT) {
+ targetGradientAlpha = 0xff;
+ } else if (mMode == MODE_SEMI_TRANSPARENT) {
+ targetColor = mSemiTransparent;
+ } else {
+ targetColor = mOpaque;
+ }
+ if (!mAnimating) {
+ mColor = targetColor;
+ mGradientAlpha = targetGradientAlpha;
+ } else {
+ final long now = SystemClock.elapsedRealtime();
+ if (now >= mEndTime) {
+ mAnimating = false;
+ mColor = targetColor;
+ mGradientAlpha = targetGradientAlpha;
+ } else {
+ final float t = (now - mStartTime) / (float)(mEndTime - mStartTime);
+ final float v = Math.max(0, Math.min(mInterpolator.getInterpolation(t), 1));
+ mGradientAlpha = (int)(v * targetGradientAlpha + mGradientAlphaStart * (1 - v));
+ mColor = Color.argb(
+ (int)(v * Color.alpha(targetColor) + Color.alpha(mColorStart) * (1 - v)),
+ (int)(v * Color.red(targetColor) + Color.red(mColorStart) * (1 - v)),
+ (int)(v * Color.green(targetColor) + Color.green(mColorStart) * (1 - v)),
+ (int)(v * Color.blue(targetColor) + Color.blue(mColorStart) * (1 - v)));
+ }
+ }
+ if (mGradientAlpha > 0) {
+ mGradient.setAlpha(mGradientAlpha);
+ mGradient.draw(canvas);
+ }
+ if (Color.alpha(mColor) > 0) {
+ canvas.drawColor(mColor);
+ }
+ if (mAnimating) {
+ invalidateSelf(); // keep going
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java
index 5c55f0d..c1646ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java
@@ -77,10 +77,11 @@ public class KeyguardTouchDelegate {
}
public static KeyguardTouchDelegate getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new KeyguardTouchDelegate(context);
+ KeyguardTouchDelegate instance = sInstance;
+ if (instance == null) {
+ instance = sInstance = new KeyguardTouchDelegate(context);
}
- return sInstance;
+ return instance;
}
public boolean isSecure() {
@@ -165,7 +166,21 @@ public class KeyguardTouchDelegate {
Slog.e(TAG, "RemoteException launching camera!", e);
}
} else {
- Slog.w(TAG, "dispatch(event): NO SERVICE!");
+ Slog.w(TAG, "launchCamera(): NO SERVICE!");
+ }
+ }
+
+ public void dismiss() {
+ final IKeyguardService service = mService;
+ if (service != null) {
+ try {
+ service.dismiss();
+ } catch (RemoteException e) {
+ // What to do?
+ Slog.e(TAG, "RemoteException dismissing keyguard!", e);
+ }
+ } else {
+ Slog.w(TAG, "dismiss(): NO SERVICE!");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index 5d4b995..a74230b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -30,6 +30,9 @@ import com.android.systemui.statusbar.policy.KeyButtonView;
public final class NavigationBarTransitions extends BarTransitions {
+ private static final float KEYGUARD_QUIESCENT_ALPHA = 0.5f;
+ private static final int CONTENT_FADE_DURATION = 200;
+
private final NavigationBarView mView;
private final IStatusBarService mBarService;
@@ -73,18 +76,57 @@ public final class NavigationBarTransitions extends BarTransitions {
private void applyMode(int mode, boolean animate, boolean force) {
// apply to key buttons
- final boolean isOpaque = mode == MODE_OPAQUE || mode == MODE_LIGHTS_OUT;
- final float alpha = isOpaque ? KeyButtonView.DEFAULT_QUIESCENT_ALPHA : 1f;
- setKeyButtonViewQuiescentAlpha(mView.getBackButton(), alpha, animate);
+ final float alpha = alphaForMode(mode);
setKeyButtonViewQuiescentAlpha(mView.getHomeButton(), alpha, animate);
setKeyButtonViewQuiescentAlpha(mView.getRecentsButton(), alpha, animate);
setKeyButtonViewQuiescentAlpha(mView.getMenuButton(), alpha, animate);
- setKeyButtonViewQuiescentAlpha(mView.getCameraButton(), alpha, animate);
+
+ setKeyButtonViewQuiescentAlpha(mView.getSearchLight(), KEYGUARD_QUIESCENT_ALPHA, animate);
+ setKeyButtonViewQuiescentAlpha(mView.getCameraButton(), KEYGUARD_QUIESCENT_ALPHA, animate);
+
+ applyBackButtonQuiescentAlpha(mode, animate);
// apply to lights out
applyLightsOut(mode == MODE_LIGHTS_OUT, animate, force);
}
+ private float alphaForMode(int mode) {
+ final boolean isOpaque = mode == MODE_OPAQUE || mode == MODE_LIGHTS_OUT;
+ return isOpaque ? KeyButtonView.DEFAULT_QUIESCENT_ALPHA : 1f;
+ }
+
+ public void applyBackButtonQuiescentAlpha(int mode, boolean animate) {
+ float backAlpha = 0;
+ backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getSearchLight());
+ backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getCameraButton());
+ backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getHomeButton());
+ backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getRecentsButton());
+ backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getMenuButton());
+ if (backAlpha > 0) {
+ setKeyButtonViewQuiescentAlpha(mView.getBackButton(), backAlpha, animate);
+ }
+ }
+
+ private static float maxVisibleQuiescentAlpha(float max, View v) {
+ if ((v instanceof KeyButtonView) && v.isShown()) {
+ return Math.max(max, ((KeyButtonView)v).getQuiescentAlpha());
+ }
+ return max;
+ }
+
+ @Override
+ public void setContentVisible(boolean visible) {
+ final float alpha = visible ? 1 : 0;
+ fadeContent(mView.getCameraButton(), alpha);
+ fadeContent(mView.getSearchLight(), alpha);
+ }
+
+ private void fadeContent(View v, float alpha) {
+ if (v != null) {
+ v.animate().alpha(alpha).setDuration(CONTENT_FADE_DURATION);
+ }
+ }
+
private void setKeyButtonViewQuiescentAlpha(View button, float alpha, boolean animate) {
if (button instanceof KeyButtonView) {
((KeyButtonView) button).setQuiescentAlpha(alpha, animate);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index d1c4109..839016d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -17,6 +17,10 @@
package com.android.systemui.statusbar.phone;
import android.animation.LayoutTransition;
+import android.animation.LayoutTransition.TransitionListener;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
import android.app.ActivityManagerNative;
import android.app.StatusBarManager;
import android.app.admin.DevicePolicyManager;
@@ -48,12 +52,12 @@ import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.DelegateViewHelper;
import com.android.systemui.statusbar.policy.DeadZone;
+import com.android.systemui.statusbar.policy.KeyButtonView;
import java.io.FileDescriptor;
import java.io.PrintWriter;
public class NavigationBarView extends LinearLayout {
- private static final int CAMERA_BUTTON_FADE_DURATION = 200;
final static boolean DEBUG = false;
final static String TAG = "PhoneStatusBar/NavigationBarView";
@@ -89,6 +93,54 @@ public class NavigationBarView extends LinearLayout {
// used to disable the camera icon in navbar when disabled by DPM
private boolean mCameraDisabledByDpm;
+ // performs manual animation in sync with layout transitions
+ private final NavTransitionListener mTransitionListener = new NavTransitionListener();
+
+ private class NavTransitionListener implements TransitionListener {
+ private boolean mBackTransitioning;
+ private boolean mHomeAppearing;
+ private long mStartDelay;
+ private long mDuration;
+ private TimeInterpolator mInterpolator;
+
+ @Override
+ public void startTransition(LayoutTransition transition, ViewGroup container,
+ View view, int transitionType) {
+ if (view.getId() == R.id.back) {
+ mBackTransitioning = true;
+ } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) {
+ mHomeAppearing = true;
+ mStartDelay = transition.getStartDelay(transitionType);
+ mDuration = transition.getDuration(transitionType);
+ mInterpolator = transition.getInterpolator(transitionType);
+ }
+ }
+
+ @Override
+ public void endTransition(LayoutTransition transition, ViewGroup container,
+ View view, int transitionType) {
+ if (view.getId() == R.id.back) {
+ mBackTransitioning = false;
+ } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) {
+ mHomeAppearing = false;
+ }
+ }
+
+ public void onBackAltCleared() {
+ // When dismissing ime during unlock, force the back button to run the same appearance
+ // animation as home (if we catch this condition early enough).
+ if (!mBackTransitioning && getBackButton().getVisibility() == VISIBLE
+ && mHomeAppearing && getHomeButton().getAlpha() == 0) {
+ getBackButton().setAlpha(0);
+ ValueAnimator a = ObjectAnimator.ofFloat(getBackButton(), "alpha", 0, 1);
+ a.setStartDelay(mStartDelay);
+ a.setDuration(mDuration);
+ a.setInterpolator(mInterpolator);
+ a.start();
+ }
+ }
+ }
+
// simplified click handler to be used when device is in accessibility mode
private final OnClickListener mAccessibilityClickListener = new OnClickListener() {
@Override
@@ -108,12 +160,12 @@ public class NavigationBarView extends LinearLayout {
case MotionEvent.ACTION_DOWN:
// disable search gesture while interacting with camera
mDelegateHelper.setDisabled(true);
- transitionCameraAndSearchButtonAlpha(0.0f);
+ mBarTransitions.setContentVisible(false);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mDelegateHelper.setDisabled(false);
- transitionCameraAndSearchButtonAlpha(1.0f);
+ mBarTransitions.setContentVisible(true);
break;
}
return KeyguardTouchDelegate.getInstance(getContext()).dispatch(event);
@@ -163,17 +215,6 @@ public class NavigationBarView extends LinearLayout {
watchForDevicePolicyChanges();
}
- protected void transitionCameraAndSearchButtonAlpha(float alpha) {
- View cameraButtonView = getCameraButton();
- if (cameraButtonView != null) {
- cameraButtonView.animate().alpha(alpha).setDuration(CAMERA_BUTTON_FADE_DURATION);
- }
- View searchLight = getSearchLight();
- if (searchLight != null) {
- searchLight.animate().alpha(alpha).setDuration(CAMERA_BUTTON_FADE_DURATION);
- }
- }
-
private void watchForDevicePolicyChanges() {
final IntentFilter filter = new IntentFilter();
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
@@ -277,7 +318,10 @@ public class NavigationBarView extends LinearLayout {
public void setNavigationIconHints(int hints, boolean force) {
if (!force && hints == mNavigationIconHints) return;
-
+ final boolean backAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
+ if ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0 && !backAlt) {
+ mTransitionListener.onBackAltCleared();
+ }
if (DEBUG) {
android.widget.Toast.makeText(mContext,
"Navigation icon hints = " + hints,
@@ -286,15 +330,7 @@ public class NavigationBarView extends LinearLayout {
mNavigationIconHints = hints;
- getBackButton().setAlpha(
- (0 != (hints & StatusBarManager.NAVIGATION_HINT_BACK_NOP)) ? 0.5f : 1.0f);
- getHomeButton().setAlpha(
- (0 != (hints & StatusBarManager.NAVIGATION_HINT_HOME_NOP)) ? 0.5f : 1.0f);
- getRecentsButton().setAlpha(
- (0 != (hints & StatusBarManager.NAVIGATION_HINT_RECENT_NOP)) ? 0.5f : 1.0f);
-
- ((ImageView)getBackButton()).setImageDrawable(
- (0 != (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT))
+ ((ImageView)getBackButton()).setImageDrawable(backAlt
? (mVertical ? mBackAltLandIcon : mBackAltIcon)
: (mVertical ? mBackLandIcon : mBackIcon));
@@ -322,13 +358,20 @@ public class NavigationBarView extends LinearLayout {
setSlippery(disableHome && disableRecent && disableBack && disableSearch);
}
- if (!mScreenOn && mCurrentView != null) {
- ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons);
- LayoutTransition lt = navButtons == null ? null : navButtons.getLayoutTransition();
+ ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons);
+ if (navButtons != null) {
+ LayoutTransition lt = navButtons.getLayoutTransition();
if (lt != null) {
- lt.disableTransitionType(
- LayoutTransition.CHANGE_APPEARING | LayoutTransition.CHANGE_DISAPPEARING |
- LayoutTransition.APPEARING | LayoutTransition.DISAPPEARING);
+ if (!lt.getTransitionListeners().contains(mTransitionListener)) {
+ lt.addTransitionListener(mTransitionListener);
+ }
+ if (!mScreenOn && mCurrentView != null) {
+ lt.disableTransitionType(
+ LayoutTransition.CHANGE_APPEARING |
+ LayoutTransition.CHANGE_DISAPPEARING |
+ LayoutTransition.APPEARING |
+ LayoutTransition.DISAPPEARING);
+ }
}
}
@@ -336,12 +379,17 @@ public class NavigationBarView extends LinearLayout {
getHomeButton() .setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
- final boolean shouldShowSearch = disableHome && !disableSearch;
- getSearchLight().setVisibility(shouldShowSearch ? View.VISIBLE : View.GONE);
- final View cameraButton = getCameraButton();
- if (cameraButton != null) {
- cameraButton.setVisibility(
- shouldShowSearch && !mCameraDisabledByDpm ? View.VISIBLE : View.GONE);
+ final boolean showSearch = disableHome && !disableSearch;
+ final boolean showCamera = showSearch && !mCameraDisabledByDpm;
+ setVisibleOrGone(getSearchLight(), showSearch);
+ setVisibleOrGone(getCameraButton(), showCamera);
+
+ mBarTransitions.applyBackButtonQuiescentAlpha(mBarTransitions.getMode(), true /*animate*/);
+ }
+
+ private void setVisibleOrGone(View view, boolean visible) {
+ if (view != null) {
+ view.setVisibility(visible ? VISIBLE : GONE);
}
}
@@ -574,28 +622,31 @@ public class NavigationBarView extends LinearLayout {
mVertical ? "true" : "false",
mShowMenu ? "true" : "false"));
- final View back = getBackButton();
- final View home = getHomeButton();
- final View recent = getRecentsButton();
- final View menu = getMenuButton();
-
- pw.println(" back: "
- + PhoneStatusBar.viewInfo(back)
- + " " + visibilityToString(back.getVisibility())
- );
- pw.println(" home: "
- + PhoneStatusBar.viewInfo(home)
- + " " + visibilityToString(home.getVisibility())
- );
- pw.println(" rcnt: "
- + PhoneStatusBar.viewInfo(recent)
- + " " + visibilityToString(recent.getVisibility())
- );
- pw.println(" menu: "
- + PhoneStatusBar.viewInfo(menu)
- + " " + visibilityToString(menu.getVisibility())
- );
+ dumpButton(pw, "back", getBackButton());
+ dumpButton(pw, "home", getHomeButton());
+ dumpButton(pw, "rcnt", getRecentsButton());
+ dumpButton(pw, "menu", getMenuButton());
+ dumpButton(pw, "srch", getSearchLight());
+ dumpButton(pw, "cmra", getCameraButton());
+
pw.println(" }");
}
+ private static void dumpButton(PrintWriter pw, String caption, View button) {
+ pw.print(" " + caption + ": ");
+ if (button == null) {
+ pw.print("null");
+ } else {
+ pw.print(PhoneStatusBar.viewInfo(button)
+ + " " + visibilityToString(button.getVisibility())
+ + " alpha=" + button.getAlpha()
+ );
+ if (button instanceof KeyButtonView) {
+ pw.print(" drawingAlpha=" + ((KeyButtonView)button).getDrawingAlpha());
+ pw.print(" quiescentAlpha=" + ((KeyButtonView)button).getQuiescentAlpha());
+ }
+ }
+ pw.println();
+ }
+
}
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 925179d..bbac4ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -53,6 +53,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -632,6 +633,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
}
}
+ PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mBroadcastReceiver.onReceive(mContext,
+ new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
+
// receive broadcasts
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -649,14 +654,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
@Override
protected void onShowSearchPanel() {
if (mNavigationBarView != null) {
- mNavigationBarView.transitionCameraAndSearchButtonAlpha(0.0f);
+ mNavigationBarView.getBarTransitions().setContentVisible(false);
}
}
@Override
protected void onHideSearchPanel() {
if (mNavigationBarView != null) {
- mNavigationBarView.transitionCameraAndSearchButtonAlpha(1.0f);
+ mNavigationBarView.getBarTransitions().setContentVisible(true);
}
}
@@ -802,7 +807,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
}
private void repositionNavigationBar() {
- if (mNavigationBarView == null) return;
+ if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
prepareNavigationBarView();
@@ -1807,8 +1812,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
return mGestureRec;
}
- @Override // CommandQueue
- public void setNavigationIconHints(int hints) {
+ private void setNavigationIconHints(int hints) {
if (hints == mNavigationIconHints) return;
mNavigationIconHints = hints;
@@ -1945,6 +1949,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
transitions.transitionTo(mode, anim);
}
+ private void finishBarAnimations() {
+ mStatusBarView.getBarTransitions().finishAnimations();
+ if (mNavigationBarView != null) {
+ mNavigationBarView.getBarTransitions().finishAnimations();
+ }
+ }
+
private final Runnable mCheckBarModes = new Runnable() {
@Override
public void run() {
@@ -2038,7 +2049,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
boolean altBack = (backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS)
|| ((vis & InputMethodService.IME_VISIBLE) != 0);
- mCommandQueue.setNavigationIconHints(
+ setNavigationIconHints(
altBack ? (mNavigationIconHints | NAVIGATION_HINT_BACK_ALT)
: (mNavigationIconHints & ~NAVIGATION_HINT_BACK_ALT));
if (mQS != null) mQS.setImeWindowStatus(vis > 0);
@@ -2094,9 +2105,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
}
public void tickerHalting() {
- mStatusBarContents.setVisibility(View.VISIBLE);
+ if (mStatusBarContents.getVisibility() != View.VISIBLE) {
+ mStatusBarContents.setVisibility(View.VISIBLE);
+ mStatusBarContents
+ .startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
+ }
mTickerView.setVisibility(View.GONE);
- mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
// we do not animate the ticker away at this point, just get rid of it (b/6992707)
}
}
@@ -2449,6 +2463,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
makeExpandedInvisible();
notifyNavigationBarScreenOn(false);
notifyHeadsUpScreenOn(false);
+ finishBarAnimations();
}
else if (Intent.ACTION_SCREEN_ON.equals(action)) {
mScreenOn = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index d9ac7e4..d0e9a99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -170,6 +170,9 @@ public class PhoneStatusBarView extends PanelBar {
mBar.makeExpandedInvisibleSoon();
mFadingPanel = null;
mLastFullyOpenedPanel = null;
+ if (mScrimColor != 0 && ActivityManager.isHighEndGfx()) {
+ mBar.mStatusBarWindow.setBackgroundColor(0);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index 5423bb6..e7b8fa1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -37,9 +37,8 @@ import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LevelListDrawable;
import android.hardware.display.DisplayManager;
-import android.hardware.display.WifiDisplayStatus;
+import android.media.MediaRouter;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Handler;
@@ -62,6 +61,7 @@ import android.view.WindowManagerGlobal;
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.internal.app.MediaRouteDialogPresenter;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.QuickSettingsModel.ActivityState;
import com.android.systemui.statusbar.phone.QuickSettingsModel.BluetoothState;
@@ -92,9 +92,7 @@ class QuickSettings {
private QuickSettingsModel mModel;
private ViewGroup mContainerView;
- private DisplayManager mDisplayManager;
private DevicePolicyManager mDevicePolicyManager;
- private WifiDisplayStatus mWifiDisplayStatus;
private PhoneStatusBar mStatusBarService;
private BluetoothState mBluetoothState;
private BluetoothAdapter mBluetoothAdapter;
@@ -118,13 +116,11 @@ class QuickSettings {
new ArrayList<QuickSettingsTileView>();
public QuickSettings(Context context, QuickSettingsContainerView container) {
- mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
mDevicePolicyManager
= (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
mContext = context;
mContainerView = container;
mModel = new QuickSettingsModel(context);
- mWifiDisplayStatus = new WifiDisplayStatus();
mBluetoothState = new QuickSettingsModel.BluetoothState();
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
@@ -171,7 +167,6 @@ class QuickSettings {
mLocationController = locationController;
setupQuickSettings();
- updateWifiDisplayStatus();
updateResources();
applyLocationEnabledStatus();
@@ -314,11 +309,19 @@ class QuickSettings {
collapsePanels();
final UserManager um = UserManager.get(mContext);
if (um.getUsers(true).size() > 1) {
- try {
- WindowManagerGlobal.getWindowManagerService().lockNow(null);
- } catch (RemoteException e) {
- Log.e(TAG, "Couldn't show user switcher", e);
- }
+ // Since keyguard and systemui were merged into the same process to save
+ // memory, they share the same Looper and graphics context. As a result,
+ // there's no way to allow concurrent animation while keyguard inflates.
+ // The workaround is to add a slight delay to allow the animation to finish.
+ mHandler.postDelayed(new Runnable() {
+ public void run() {
+ try {
+ WindowManagerGlobal.getWindowManagerService().lockNow(null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't show user switcher", e);
+ }
+ }
+ }, 400); // TODO: ideally this would be tied to the collapse of the panel
} else {
Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent(
mContext, v, ContactsContract.Profile.CONTENT_URI,
@@ -630,8 +633,19 @@ class QuickSettings {
return true; // Consume click
}} );
}
- mModel.addLocationTile(locationTile,
- new QuickSettingsModel.BasicRefreshCallback(locationTile));
+ mModel.addLocationTile(locationTile, new QuickSettingsModel.RefreshCallback() {
+ @Override
+ public void refreshView(QuickSettingsTileView unused, State state) {
+ locationTile.setImageResource(state.iconId);
+ String locationState = mContext.getString(
+ (state.enabled) ? R.string.accessibility_desc_on
+ : R.string.accessibility_desc_off);
+ locationTile.setContentDescription(mContext.getString(
+ R.string.accessibility_quick_settings_location,
+ locationState));
+ locationTile.setText(state.label);
+ }
+ });
parent.addView(locationTile);
}
@@ -657,20 +671,33 @@ class QuickSettings {
});
parent.addView(alarmTile);
- // Wifi Display
- QuickSettingsBasicTile wifiDisplayTile
+ // Remote Display
+ QuickSettingsBasicTile remoteDisplayTile
= new QuickSettingsBasicTile(mContext);
- wifiDisplayTile.setImageResource(R.drawable.ic_qs_remote_display);
- wifiDisplayTile.setOnClickListener(new View.OnClickListener() {
+ remoteDisplayTile.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- startSettingsActivity(android.provider.Settings.ACTION_WIFI_DISPLAY_SETTINGS);
+ collapsePanels();
+
+ final Dialog[] dialog = new Dialog[1];
+ dialog[0] = MediaRouteDialogPresenter.createDialog(mContext,
+ MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ dialog[0].dismiss();
+ startSettingsActivity(
+ android.provider.Settings.ACTION_WIFI_DISPLAY_SETTINGS);
+ }
+ });
+ dialog[0].getWindow().setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
+ dialog[0].show();
}
});
- mModel.addWifiDisplayTile(wifiDisplayTile,
- new QuickSettingsModel.BasicRefreshCallback(wifiDisplayTile)
+ mModel.addRemoteDisplayTile(remoteDisplayTile,
+ new QuickSettingsModel.BasicRefreshCallback(remoteDisplayTile)
.setShowWhenEnabled(true));
- parent.addView(wifiDisplayTile);
+ parent.addView(remoteDisplayTile);
if (SHOW_IME_TILE || DEBUG_GONE_TILES) {
// IME
@@ -805,15 +832,6 @@ class QuickSettings {
dialog.show();
}
- private void updateWifiDisplayStatus() {
- mWifiDisplayStatus = mDisplayManager.getWifiDisplayStatus();
- applyWifiDisplayStatus();
- }
-
- private void applyWifiDisplayStatus() {
- mModel.onWifiDisplayStateChanged(mWifiDisplayStatus);
- }
-
private void applyBluetoothStatus() {
mModel.onBluetoothStateChange(mBluetoothState);
}
@@ -837,12 +855,7 @@ class QuickSettings {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- if (DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED.equals(action)) {
- WifiDisplayStatus status = (WifiDisplayStatus)intent.getParcelableExtra(
- DisplayManager.EXTRA_WIFI_DISPLAY_STATUS);
- mWifiDisplayStatus = status;
- applyWifiDisplayStatus();
- } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
+ if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
BluetoothAdapter.ERROR);
mBluetoothState.enabled = (state == BluetoothAdapter.STATE_ON);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
index 2026102..e1a20ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -27,7 +27,8 @@ import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
-import android.hardware.display.WifiDisplayStatus;
+import android.media.MediaRouter;
+import android.media.MediaRouter.RouteInfo;
import android.net.ConnectivityManager;
import android.os.Handler;
import android.os.UserHandle;
@@ -57,7 +58,6 @@ class QuickSettingsModel implements BluetoothStateChangeCallback,
BrightnessStateChangeCallback,
RotationLockControllerCallback,
LocationSettingsChangeCallback {
-
// Sett InputMethoManagerService
private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
@@ -199,6 +199,30 @@ class QuickSettingsModel implements BluetoothStateChangeCallback,
}
}
+ /** Callback for changes to remote display routes. */
+ private class RemoteDisplayRouteCallback extends MediaRouter.SimpleCallback {
+ @Override
+ public void onRouteAdded(MediaRouter router, RouteInfo route) {
+ updateRemoteDisplays();
+ }
+ @Override
+ public void onRouteChanged(MediaRouter router, RouteInfo route) {
+ updateRemoteDisplays();
+ }
+ @Override
+ public void onRouteRemoved(MediaRouter router, RouteInfo route) {
+ updateRemoteDisplays();
+ }
+ @Override
+ public void onRouteSelected(MediaRouter router, int type, RouteInfo route) {
+ updateRemoteDisplays();
+ }
+ @Override
+ public void onRouteUnselected(MediaRouter router, int type, RouteInfo route) {
+ updateRemoteDisplays();
+ }
+ }
+
private final Context mContext;
private final Handler mHandler;
private final CurrentUserTracker mUserTracker;
@@ -206,6 +230,9 @@ class QuickSettingsModel implements BluetoothStateChangeCallback,
private final BugreportObserver mBugreportObserver;
private final BrightnessObserver mBrightnessObserver;
+ private final MediaRouter mMediaRouter;
+ private final RemoteDisplayRouteCallback mRemoteDisplayRouteCallback;
+
private final boolean mHasMobileData;
private QuickSettingsTileView mUserTile;
@@ -228,9 +255,9 @@ class QuickSettingsModel implements BluetoothStateChangeCallback,
private RefreshCallback mWifiCallback;
private WifiState mWifiState = new WifiState();
- private QuickSettingsTileView mWifiDisplayTile;
- private RefreshCallback mWifiDisplayCallback;
- private State mWifiDisplayState = new State();
+ private QuickSettingsTileView mRemoteDisplayTile;
+ private RefreshCallback mRemoteDisplayCallback;
+ private State mRemoteDisplayState = new State();
private QuickSettingsTileView mRSSITile;
private RefreshCallback mRSSICallback;
@@ -278,12 +305,14 @@ class QuickSettingsModel implements BluetoothStateChangeCallback,
mContext = context;
mHandler = new Handler();
mUserTracker = new CurrentUserTracker(mContext) {
+ @Override
public void onUserSwitched(int newUserId) {
mBrightnessObserver.startObserving();
- onRotationLockChanged();
+ refreshRotationLockTile();
onBrightnessLevelChanged();
onNextAlarmChanged();
onBugreportChanged();
+ rebindMediaRouterAsCurrentUser();
}
};
@@ -294,6 +323,11 @@ class QuickSettingsModel implements BluetoothStateChangeCallback,
mBrightnessObserver = new BrightnessObserver(mHandler);
mBrightnessObserver.startObserving();
+ mMediaRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
+ rebindMediaRouterAsCurrentUser();
+
+ mRemoteDisplayRouteCallback = new RemoteDisplayRouteCallback();
+
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
mHasMobileData = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
@@ -621,24 +655,64 @@ class QuickSettingsModel implements BluetoothStateChangeCallback,
mBugreportCallback.refreshView(mBugreportTile, mBugreportState);
}
- // Wifi Display
- void addWifiDisplayTile(QuickSettingsTileView view, RefreshCallback cb) {
- mWifiDisplayTile = view;
- mWifiDisplayCallback = cb;
+ // Remote Display
+ void addRemoteDisplayTile(QuickSettingsTileView view, RefreshCallback cb) {
+ mRemoteDisplayTile = view;
+ mRemoteDisplayCallback = cb;
+ final int[] count = new int[1];
+ mRemoteDisplayTile.setOnPrepareListener(new QuickSettingsTileView.OnPrepareListener() {
+ @Override
+ public void onPrepare() {
+ mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
+ mRemoteDisplayRouteCallback,
+ MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
+ updateRemoteDisplays();
+ }
+ @Override
+ public void onUnprepare() {
+ mMediaRouter.removeCallback(mRemoteDisplayRouteCallback);
+ }
+ });
+
+ updateRemoteDisplays();
}
- public void onWifiDisplayStateChanged(WifiDisplayStatus status) {
- mWifiDisplayState.enabled =
- (status.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON);
- if (status.getActiveDisplay() != null) {
- mWifiDisplayState.label = status.getActiveDisplay().getFriendlyDisplayName();
- mWifiDisplayState.iconId = R.drawable.ic_qs_remote_display_connected;
+
+ private void rebindMediaRouterAsCurrentUser() {
+ mMediaRouter.rebindAsUser(mUserTracker.getCurrentUserId());
+ }
+
+ private void updateRemoteDisplays() {
+ MediaRouter.RouteInfo connectedRoute = mMediaRouter.getSelectedRoute(
+ MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
+ boolean enabled = connectedRoute != null
+ && connectedRoute.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
+ boolean connecting;
+ if (enabled) {
+ connecting = connectedRoute.isConnecting();
} else {
- mWifiDisplayState.label = mContext.getString(
- R.string.quick_settings_wifi_display_no_connection_label);
- mWifiDisplayState.iconId = R.drawable.ic_qs_remote_display;
+ connectedRoute = null;
+ connecting = false;
+ final int count = mMediaRouter.getRouteCount();
+ for (int i = 0; i < count; i++) {
+ MediaRouter.RouteInfo route = mMediaRouter.getRouteAt(i);
+ if (route.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)) {
+ enabled = true;
+ break;
+ }
+ }
}
- mWifiDisplayCallback.refreshView(mWifiDisplayTile, mWifiDisplayState);
+ mRemoteDisplayState.enabled = enabled;
+ if (connectedRoute != null) {
+ mRemoteDisplayState.label = connectedRoute.getName().toString();
+ mRemoteDisplayState.iconId = connecting ?
+ R.drawable.ic_qs_cast_connecting : R.drawable.ic_qs_cast_connected;
+ } else {
+ mRemoteDisplayState.label = mContext.getString(
+ R.string.quick_settings_remote_display_no_connection_label);
+ mRemoteDisplayState.iconId = R.drawable.ic_qs_cast_available;
+ }
+ mRemoteDisplayCallback.refreshView(mRemoteDisplayTile, mRemoteDisplayState);
}
// IME
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsTileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsTileView.java
index 3d520f7..ad18294 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsTileView.java
@@ -21,6 +21,7 @@ import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewParent;
import android.widget.FrameLayout;
/**
@@ -31,14 +32,14 @@ class QuickSettingsTileView extends FrameLayout {
private int mContentLayoutId;
private int mColSpan;
- private int mRowSpan;
+ private boolean mPrepared;
+ private OnPrepareListener mOnPrepareListener;
public QuickSettingsTileView(Context context, AttributeSet attrs) {
super(context, attrs);
mContentLayoutId = -1;
mColSpan = 1;
- mRowSpan = 1;
}
void setColumnSpan(int span) {
@@ -77,4 +78,72 @@ class QuickSettingsTileView extends FrameLayout {
}
super.setVisibility(vis);
}
+
+ public void setOnPrepareListener(OnPrepareListener listener) {
+ if (mOnPrepareListener != listener) {
+ mOnPrepareListener = listener;
+ mPrepared = false;
+ post(new Runnable() {
+ @Override
+ public void run() {
+ updatePreparedState();
+ }
+ });
+ }
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ updatePreparedState();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ updatePreparedState();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ updatePreparedState();
+ }
+
+ private void updatePreparedState() {
+ if (mOnPrepareListener != null) {
+ if (isParentVisible()) {
+ if (!mPrepared) {
+ mPrepared = true;
+ mOnPrepareListener.onPrepare();
+ }
+ } else if (mPrepared) {
+ mPrepared = false;
+ mOnPrepareListener.onUnprepare();
+ }
+ }
+ }
+
+ private boolean isParentVisible() {
+ if (!isAttachedToWindow()) {
+ return false;
+ }
+ for (ViewParent current = getParent(); current instanceof View;
+ current = current.getParent()) {
+ View view = (View)current;
+ if (view.getVisibility() != VISIBLE) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Called when the view's parent becomes visible or invisible to provide
+ * an opportunity for the client to provide new content.
+ */
+ public interface OnPrepareListener {
+ void onPrepare();
+ void onUnprepare();
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index e77b420..4901823 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -113,7 +113,7 @@ public class StatusBarWindowView extends FrameLayout
handled = super.onTouchEvent(ev);
}
final int action = ev.getAction();
- if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ if (!handled && (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL)) {
mService.setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
}
return handled;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
index dca5e41..6eb88be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
@@ -22,7 +22,7 @@ import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.os.SystemClock;
import android.util.AttributeSet;
-import android.util.Log;
+import android.util.Slog;
import android.view.MotionEvent;
import android.view.View;
@@ -75,7 +75,7 @@ public class DeadZone extends View {
mVertical = (index == VERTICAL);
if (DEBUG)
- Log.v(TAG, this + " size=[" + mSizeMin + "-" + mSizeMax + "] hold=" + mHold
+ Slog.v(TAG, this + " size=[" + mSizeMin + "-" + mSizeMax + "] hold=" + mHold
+ (mVertical ? " vertical" : " horizontal"));
setFlashOnTouchCapture(context.getResources().getBoolean(R.bool.config_dead_zone_flash));
@@ -106,7 +106,7 @@ public class DeadZone extends View {
@Override
public boolean onTouchEvent(MotionEvent event) {
if (DEBUG) {
- Log.v(TAG, this + " onTouch: " + MotionEvent.actionToString(event.getAction()));
+ Slog.v(TAG, this + " onTouch: " + MotionEvent.actionToString(event.getAction()));
}
final int action = event.getAction();
@@ -114,12 +114,12 @@ public class DeadZone extends View {
poke(event);
} else if (action == MotionEvent.ACTION_DOWN) {
if (DEBUG) {
- Log.v(TAG, this + " ACTION_DOWN: " + event.getX() + "," + event.getY());
+ Slog.v(TAG, this + " ACTION_DOWN: " + event.getX() + "," + event.getY());
}
int size = (int) getSize(event.getEventTime());
if ((mVertical && event.getX() < size) || event.getY() < size) {
if (CHATTY) {
- Log.v(TAG, "consuming errant click: (" + event.getX() + "," + event.getY() + ")");
+ Slog.v(TAG, "consuming errant click: (" + event.getX() + "," + event.getY() + ")");
}
if (mShouldFlash) {
post(mDebugFlash);
@@ -134,7 +134,7 @@ public class DeadZone extends View {
public void poke(MotionEvent event) {
mLastPokeTime = event.getEventTime();
if (DEBUG)
- Log.v(TAG, "poked! size=" + getSize(mLastPokeTime));
+ Slog.v(TAG, "poked! size=" + getSize(mLastPokeTime));
if (mShouldFlash) postInvalidate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 55fb95d..718acc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -36,6 +36,7 @@ import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewDebug;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
@@ -53,10 +54,13 @@ public class KeyButtonView extends ImageView {
int mTouchSlop;
Drawable mGlowBG;
int mGlowWidth, mGlowHeight;
- float mGlowAlpha = 0f, mGlowScale = 1f, mDrawingAlpha = 1f;
+ float mGlowAlpha = 0f, mGlowScale = 1f;
+ @ViewDebug.ExportedProperty(category = "drawing")
+ float mDrawingAlpha = 1f;
+ @ViewDebug.ExportedProperty(category = "drawing")
float mQuiescentAlpha = DEFAULT_QUIESCENT_ALPHA;
boolean mSupportsLongpress = true;
- RectF mRect = new RectF(0f,0f,0f,0f);
+ RectF mRect = new RectF();
AnimatorSet mPressedAnim;
Animator mAnimateToQuiescent = new ObjectAnimator();
@@ -90,8 +94,8 @@ public class KeyButtonView extends ImageView {
mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true);
mGlowBG = a.getDrawable(R.styleable.KeyButtonView_glowBackground);
+ setDrawingAlpha(mQuiescentAlpha);
if (mGlowBG != null) {
- setDrawingAlpha(mQuiescentAlpha);
mGlowWidth = mGlowBG.getIntrinsicWidth();
mGlowHeight = mGlowBG.getIntrinsicHeight();
}
@@ -126,16 +130,14 @@ public class KeyButtonView extends ImageView {
public void setQuiescentAlpha(float alpha, boolean animate) {
mAnimateToQuiescent.cancel();
alpha = Math.min(Math.max(alpha, 0), 1);
- if (alpha == mQuiescentAlpha) return;
+ if (alpha == mQuiescentAlpha && alpha == mDrawingAlpha) return;
mQuiescentAlpha = alpha;
if (DEBUG) Log.d(TAG, "New quiescent alpha = " + mQuiescentAlpha);
- if (mGlowBG != null) {
- if (animate) {
- mAnimateToQuiescent = animateToQuiescent();
- mAnimateToQuiescent.start();
- } else {
- setDrawingAlpha(mQuiescentAlpha);
- }
+ if (mGlowBG != null && animate) {
+ mAnimateToQuiescent = animateToQuiescent();
+ mAnimateToQuiescent.start();
+ } else {
+ setDrawingAlpha(mQuiescentAlpha);
}
}
@@ -143,13 +145,15 @@ public class KeyButtonView extends ImageView {
return ObjectAnimator.ofFloat(this, "drawingAlpha", mQuiescentAlpha);
}
+ public float getQuiescentAlpha() {
+ return mQuiescentAlpha;
+ }
+
public float getDrawingAlpha() {
- if (mGlowBG == null) return 0;
return mDrawingAlpha;
}
public void setDrawingAlpha(float x) {
- if (mGlowBG == null) return;
// Calling setAlpha(int), which is an ImageView-specific
// method that's different from setAlpha(float). This sets
// the alpha on this ImageView's drawable directly
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index a53b25a..dd13e31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -89,10 +89,6 @@ public class TvStatusBar extends BaseStatusBar {
}
@Override // CommandQueue
- public void setNavigationIconHints(int hints) {
- }
-
- @Override // CommandQueue
public void setWindowState(int window, int state) {
}
diff --git a/packages/WallpaperCropper/res/values/strings.xml b/packages/WallpaperCropper/res/values/strings.xml
index 2b8111d..091869a 100644
--- a/packages/WallpaperCropper/res/values/strings.xml
+++ b/packages/WallpaperCropper/res/values/strings.xml
@@ -17,4 +17,9 @@
<string name="crop_wallpaper">Crop wallpaper</string>
<!-- Button label on Wallpaper picker screen; user selects this button to set a specific wallpaper -->
<string name="wallpaper_instructions">Set wallpaper</string>
+ <!-- Error message when an image is selected as a wallpaper,
+ but the wallpaper cropper cannot load it. The user will
+ usually see this when using another app and trying to set
+ an image as the wallpaper -->
+ <string name="wallpaper_load_fail">Couldn\'t load image as wallpaper</string>
</resources>
diff --git a/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java b/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java
index 5f64018..8511de2 100644
--- a/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java
+++ b/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java
@@ -24,6 +24,9 @@ import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
@@ -31,14 +34,113 @@ import android.os.Build.VERSION_CODES;
import android.util.Log;
import com.android.gallery3d.common.BitmapUtils;
+import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.exif.ExifInterface;
import com.android.gallery3d.glrenderer.BasicTexture;
import com.android.gallery3d.glrenderer.BitmapTexture;
import com.android.photos.views.TiledImageRenderer;
import java.io.BufferedInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+interface SimpleBitmapRegionDecoder {
+ int getWidth();
+ int getHeight();
+ Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options);
+}
+
+class SimpleBitmapRegionDecoderWrapper implements SimpleBitmapRegionDecoder {
+ BitmapRegionDecoder mDecoder;
+ private SimpleBitmapRegionDecoderWrapper(BitmapRegionDecoder decoder) {
+ mDecoder = decoder;
+ }
+ public static SimpleBitmapRegionDecoderWrapper newInstance(
+ String pathName, boolean isShareable) {
+ try {
+ BitmapRegionDecoder d = BitmapRegionDecoder.newInstance(pathName, isShareable);
+ if (d != null) {
+ return new SimpleBitmapRegionDecoderWrapper(d);
+ }
+ } catch (IOException e) {
+ Log.w("BitmapRegionTileSource", "getting decoder failed for path " + pathName, e);
+ return null;
+ }
+ return null;
+ }
+ public static SimpleBitmapRegionDecoderWrapper newInstance(
+ InputStream is, boolean isShareable) {
+ try {
+ BitmapRegionDecoder d = BitmapRegionDecoder.newInstance(is, isShareable);
+ if (d != null) {
+ return new SimpleBitmapRegionDecoderWrapper(d);
+ }
+ } catch (IOException e) {
+ Log.w("BitmapRegionTileSource", "getting decoder failed", e);
+ return null;
+ }
+ return null;
+ }
+ public int getWidth() {
+ return mDecoder.getWidth();
+ }
+ public int getHeight() {
+ return mDecoder.getHeight();
+ }
+ public Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options) {
+ return mDecoder.decodeRegion(wantRegion, options);
+ }
+}
+
+class DumbBitmapRegionDecoder implements SimpleBitmapRegionDecoder {
+ Bitmap mBuffer;
+ Canvas mTempCanvas;
+ Paint mTempPaint;
+ private DumbBitmapRegionDecoder(Bitmap b) {
+ mBuffer = b;
+ }
+ public static DumbBitmapRegionDecoder newInstance(String pathName) {
+ Bitmap b = BitmapFactory.decodeFile(pathName);
+ if (b != null) {
+ return new DumbBitmapRegionDecoder(b);
+ }
+ return null;
+ }
+ public static DumbBitmapRegionDecoder newInstance(InputStream is) {
+ Bitmap b = BitmapFactory.decodeStream(is);
+ if (b != null) {
+ return new DumbBitmapRegionDecoder(b);
+ }
+ return null;
+ }
+ public int getWidth() {
+ return mBuffer.getWidth();
+ }
+ public int getHeight() {
+ return mBuffer.getHeight();
+ }
+ public Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options) {
+ if (mTempCanvas == null) {
+ mTempCanvas = new Canvas();
+ mTempPaint = new Paint();
+ mTempPaint.setFilterBitmap(true);
+ }
+ int sampleSize = Math.max(options.inSampleSize, 1);
+ Bitmap newBitmap = Bitmap.createBitmap(
+ wantRegion.width() / sampleSize,
+ wantRegion.height() / sampleSize,
+ Bitmap.Config.ARGB_8888);
+ mTempCanvas.setBitmap(newBitmap);
+ mTempCanvas.save();
+ mTempCanvas.scale(1f / sampleSize, 1f / sampleSize);
+ mTempCanvas.drawBitmap(mBuffer, -wantRegion.left, -wantRegion.top, mTempPaint);
+ mTempCanvas.restore();
+ mTempCanvas.setBitmap(null);
+ return newBitmap;
+ }
+}
+
/**
* A {@link com.android.photos.views.TiledImageRenderer.TileSource} using
* {@link BitmapRegionDecoder} to wrap a local file
@@ -53,9 +155,214 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
private static final int GL_SIZE_LIMIT = 2048;
// This must be no larger than half the size of the GL_SIZE_LIMIT
// due to decodePreview being allowed to be up to 2x the size of the target
- private static final int MAX_PREVIEW_SIZE = 1024;
+ public static final int MAX_PREVIEW_SIZE = GL_SIZE_LIMIT / 2;
+
+ public static abstract class BitmapSource {
+ private SimpleBitmapRegionDecoder mDecoder;
+ private Bitmap mPreview;
+ private int mPreviewSize;
+ private int mRotation;
+ public enum State { NOT_LOADED, LOADED, ERROR_LOADING };
+ private State mState = State.NOT_LOADED;
+ public BitmapSource(int previewSize) {
+ mPreviewSize = previewSize;
+ }
+ public boolean loadInBackground() {
+ ExifInterface ei = new ExifInterface();
+ if (readExif(ei)) {
+ Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
+ if (ori != null) {
+ mRotation = ExifInterface.getRotationForOrientationValue(ori.shortValue());
+ }
+ }
+ mDecoder = loadBitmapRegionDecoder();
+ if (mDecoder == null) {
+ mState = State.ERROR_LOADING;
+ return false;
+ } else {
+ int width = mDecoder.getWidth();
+ int height = mDecoder.getHeight();
+ if (mPreviewSize != 0) {
+ int previewSize = Math.min(mPreviewSize, MAX_PREVIEW_SIZE);
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ opts.inPreferQualityOverSpeed = true;
+
+ float scale = (float) previewSize / Math.max(width, height);
+ opts.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale);
+ opts.inJustDecodeBounds = false;
+ mPreview = loadPreviewBitmap(opts);
+ }
+ mState = State.LOADED;
+ return true;
+ }
+ }
- BitmapRegionDecoder mDecoder;
+ public State getLoadingState() {
+ return mState;
+ }
+
+ public SimpleBitmapRegionDecoder getBitmapRegionDecoder() {
+ return mDecoder;
+ }
+
+ public Bitmap getPreviewBitmap() {
+ return mPreview;
+ }
+
+ public int getPreviewSize() {
+ return mPreviewSize;
+ }
+
+ public int getRotation() {
+ return mRotation;
+ }
+
+ public abstract boolean readExif(ExifInterface ei);
+ public abstract SimpleBitmapRegionDecoder loadBitmapRegionDecoder();
+ public abstract Bitmap loadPreviewBitmap(BitmapFactory.Options options);
+ }
+
+ public static class FilePathBitmapSource extends BitmapSource {
+ private String mPath;
+ public FilePathBitmapSource(String path, int previewSize) {
+ super(previewSize);
+ mPath = path;
+ }
+ @Override
+ public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() {
+ SimpleBitmapRegionDecoder d;
+ d = SimpleBitmapRegionDecoderWrapper.newInstance(mPath, true);
+ if (d == null) {
+ d = DumbBitmapRegionDecoder.newInstance(mPath);
+ }
+ return d;
+ }
+ @Override
+ public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
+ return BitmapFactory.decodeFile(mPath, options);
+ }
+ @Override
+ public boolean readExif(ExifInterface ei) {
+ try {
+ ei.readExif(mPath);
+ return true;
+ } catch (IOException e) {
+ Log.w("BitmapRegionTileSource", "getting decoder failed", e);
+ return false;
+ }
+ }
+ }
+
+ public static class UriBitmapSource extends BitmapSource {
+ private Context mContext;
+ private Uri mUri;
+ public UriBitmapSource(Context context, Uri uri, int previewSize) {
+ super(previewSize);
+ mContext = context;
+ mUri = uri;
+ }
+ private InputStream regenerateInputStream() throws FileNotFoundException {
+ InputStream is = mContext.getContentResolver().openInputStream(mUri);
+ return new BufferedInputStream(is);
+ }
+ @Override
+ public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() {
+ try {
+ InputStream is = regenerateInputStream();
+ SimpleBitmapRegionDecoder regionDecoder =
+ SimpleBitmapRegionDecoderWrapper.newInstance(is, false);
+ Utils.closeSilently(is);
+ if (regionDecoder == null) {
+ is = regenerateInputStream();
+ regionDecoder = DumbBitmapRegionDecoder.newInstance(is);
+ Utils.closeSilently(is);
+ }
+ return regionDecoder;
+ } catch (FileNotFoundException e) {
+ Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
+ return null;
+ } catch (IOException e) {
+ Log.e("BitmapRegionTileSource", "Failure while reading URI " + mUri, e);
+ return null;
+ }
+ }
+ @Override
+ public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
+ try {
+ InputStream is = regenerateInputStream();
+ Bitmap b = BitmapFactory.decodeStream(is, null, options);
+ Utils.closeSilently(is);
+ return b;
+ } catch (FileNotFoundException e) {
+ Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
+ return null;
+ }
+ }
+ @Override
+ public boolean readExif(ExifInterface ei) {
+ InputStream is = null;
+ try {
+ is = regenerateInputStream();
+ ei.readExif(is);
+ Utils.closeSilently(is);
+ return true;
+ } catch (FileNotFoundException e) {
+ Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
+ return false;
+ } catch (IOException e) {
+ Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
+ return false;
+ } finally {
+ Utils.closeSilently(is);
+ }
+ }
+ }
+
+ public static class ResourceBitmapSource extends BitmapSource {
+ private Resources mRes;
+ private int mResId;
+ public ResourceBitmapSource(Resources res, int resId, int previewSize) {
+ super(previewSize);
+ mRes = res;
+ mResId = resId;
+ }
+ private InputStream regenerateInputStream() {
+ InputStream is = mRes.openRawResource(mResId);
+ return new BufferedInputStream(is);
+ }
+ @Override
+ public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() {
+ InputStream is = regenerateInputStream();
+ SimpleBitmapRegionDecoder regionDecoder =
+ SimpleBitmapRegionDecoderWrapper.newInstance(is, false);
+ Utils.closeSilently(is);
+ if (regionDecoder == null) {
+ is = regenerateInputStream();
+ regionDecoder = DumbBitmapRegionDecoder.newInstance(is);
+ Utils.closeSilently(is);
+ }
+ return regionDecoder;
+ }
+ @Override
+ public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
+ return BitmapFactory.decodeResource(mRes, mResId, options);
+ }
+ @Override
+ public boolean readExif(ExifInterface ei) {
+ try {
+ InputStream is = regenerateInputStream();
+ ei.readExif(is);
+ Utils.closeSilently(is);
+ return true;
+ } catch (IOException e) {
+ Log.e("BitmapRegionTileSource", "Error reading resource", e);
+ return false;
+ }
+ }
+ }
+
+ SimpleBitmapRegionDecoder mDecoder;
int mWidth;
int mHeight;
int mTileSize;
@@ -68,58 +375,33 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
private BitmapFactory.Options mOptions;
private Canvas mCanvas;
- public BitmapRegionTileSource(Context context, String path, int previewSize, int rotation) {
- this(null, context, path, null, 0, previewSize, rotation);
- }
-
- public BitmapRegionTileSource(Context context, Uri uri, int previewSize, int rotation) {
- this(null, context, null, uri, 0, previewSize, rotation);
- }
-
- public BitmapRegionTileSource(Resources res,
- Context context, int resId, int previewSize, int rotation) {
- this(res, context, null, null, resId, previewSize, rotation);
- }
-
- private BitmapRegionTileSource(Resources res,
- Context context, String path, Uri uri, int resId, int previewSize, int rotation) {
+ public BitmapRegionTileSource(Context context, BitmapSource source) {
mTileSize = TiledImageRenderer.suggestedTileSize(context);
- mRotation = rotation;
- try {
- if (path != null) {
- mDecoder = BitmapRegionDecoder.newInstance(path, true);
- } else if (uri != null) {
- InputStream is = context.getContentResolver().openInputStream(uri);
- BufferedInputStream bis = new BufferedInputStream(is);
- mDecoder = BitmapRegionDecoder.newInstance(bis, true);
- } else {
- InputStream is = res.openRawResource(resId);
- BufferedInputStream bis = new BufferedInputStream(is);
- mDecoder = BitmapRegionDecoder.newInstance(bis, true);
- }
+ mRotation = source.getRotation();
+ mDecoder = source.getBitmapRegionDecoder();
+ if (mDecoder != null) {
mWidth = mDecoder.getWidth();
mHeight = mDecoder.getHeight();
- } catch (IOException e) {
- Log.w("BitmapRegionTileSource", "ctor failed", e);
- }
- mOptions = new BitmapFactory.Options();
- mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
- mOptions.inPreferQualityOverSpeed = true;
- mOptions.inTempStorage = new byte[16 * 1024];
- if (previewSize != 0) {
- previewSize = Math.min(previewSize, MAX_PREVIEW_SIZE);
- // Although this is the same size as the Bitmap that is likely already
- // loaded, the lifecycle is different and interactions are on a different
- // thread. Thus to simplify, this source will decode its own bitmap.
- Bitmap preview = decodePreview(res, context, path, uri, resId, previewSize);
- if (preview.getWidth() <= GL_SIZE_LIMIT && preview.getHeight() <= GL_SIZE_LIMIT) {
- mPreview = new BitmapTexture(preview);
- } else {
- Log.w(TAG, String.format(
- "Failed to create preview of apropriate size! "
- + " in: %dx%d, out: %dx%d",
- mWidth, mHeight,
- preview.getWidth(), preview.getHeight()));
+ mOptions = new BitmapFactory.Options();
+ mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ mOptions.inPreferQualityOverSpeed = true;
+ mOptions.inTempStorage = new byte[16 * 1024];
+ int previewSize = source.getPreviewSize();
+ if (previewSize != 0) {
+ previewSize = Math.min(previewSize, MAX_PREVIEW_SIZE);
+ // Although this is the same size as the Bitmap that is likely already
+ // loaded, the lifecycle is different and interactions are on a different
+ // thread. Thus to simplify, this source will decode its own bitmap.
+ Bitmap preview = decodePreview(source, previewSize);
+ if (preview.getWidth() <= GL_SIZE_LIMIT && preview.getHeight() <= GL_SIZE_LIMIT) {
+ mPreview = new BitmapTexture(preview);
+ } else {
+ Log.w(TAG, String.format(
+ "Failed to create preview of apropriate size! "
+ + " in: %dx%d, out: %dx%d",
+ mWidth, mHeight,
+ preview.getWidth(), preview.getHeight()));
+ }
}
}
}
@@ -215,33 +497,15 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
* Note that the returned bitmap may have a long edge that's longer
* than the targetSize, but it will always be less than 2x the targetSize
*/
- private Bitmap decodePreview(
- Resources res, Context context, String file, Uri uri, int resId, int targetSize) {
- float scale = (float) targetSize / Math.max(mWidth, mHeight);
- mOptions.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale);
- mOptions.inJustDecodeBounds = false;
-
- Bitmap result = null;
- if (file != null) {
- result = BitmapFactory.decodeFile(file, mOptions);
- } else if (uri != null) {
- try {
- InputStream is = context.getContentResolver().openInputStream(uri);
- BufferedInputStream bis = new BufferedInputStream(is);
- result = BitmapFactory.decodeStream(bis, null, mOptions);
- } catch (IOException e) {
- Log.w("BitmapRegionTileSource", "getting preview failed", e);
- }
- } else {
- result = BitmapFactory.decodeResource(res, resId, mOptions);
- }
+ private Bitmap decodePreview(BitmapSource source, int targetSize) {
+ Bitmap result = source.getPreviewBitmap();
if (result == null) {
return null;
}
// We need to resize down if the decoder does not support inSampleSize
// or didn't support the specified inSampleSize (some decoders only do powers of 2)
- scale = (float) targetSize / (float) (Math.max(result.getWidth(), result.getHeight()));
+ float scale = (float) targetSize / (float) (Math.max(result.getWidth(), result.getHeight()));
if (scale <= 0.5) {
result = BitmapUtils.resizeBitmapByScale(result, scale, true);
diff --git a/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java b/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java
index 1209e56..57c0581 100644
--- a/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java
+++ b/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java
@@ -37,15 +37,16 @@ import android.graphics.RectF;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.util.FloatMath;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
+import android.widget.Toast;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.exif.ExifInterface;
import com.android.photos.BitmapRegionTileSource;
+import com.android.photos.BitmapRegionTileSource.BitmapSource;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
@@ -96,9 +97,6 @@ public class WallpaperCropActivity extends Activity {
return;
}
- int rotation = getRotationFromExif(this, imageUri);
- mCropView.setTileSource(new BitmapRegionTileSource(this, imageUri, 1024, rotation), null);
- mCropView.setTouchEnabled(true);
// Action bar
// Show the custom action bar view
final ActionBar actionBar = getActionBar();
@@ -111,6 +109,63 @@ public class WallpaperCropActivity extends Activity {
cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone);
}
});
+
+ // Load image in background
+ final BitmapRegionTileSource.UriBitmapSource bitmapSource =
+ new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024);
+ Runnable onLoad = new Runnable() {
+ public void run() {
+ if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) {
+ Toast.makeText(WallpaperCropActivity.this,
+ getString(R.string.wallpaper_load_fail),
+ Toast.LENGTH_LONG).show();
+ finish();
+ }
+ }
+ };
+ setCropViewTileSource(bitmapSource, true, false, onLoad);
+ }
+
+ public void setCropViewTileSource(
+ final BitmapRegionTileSource.BitmapSource bitmapSource, final boolean touchEnabled,
+ final boolean moveToLeft, final Runnable postExecute) {
+ final Context context = WallpaperCropActivity.this;
+ final View progressView = findViewById(R.id.loading);
+ final AsyncTask<Void, Void, Void> loadBitmapTask = new AsyncTask<Void, Void, Void>() {
+ protected Void doInBackground(Void...args) {
+ if (!isCancelled()) {
+ bitmapSource.loadInBackground();
+ }
+ return null;
+ }
+ protected void onPostExecute(Void arg) {
+ if (!isCancelled()) {
+ progressView.setVisibility(View.INVISIBLE);
+ if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
+ mCropView.setTileSource(
+ new BitmapRegionTileSource(context, bitmapSource), null);
+ mCropView.setTouchEnabled(touchEnabled);
+ if (moveToLeft) {
+ mCropView.moveToLeft();
+ }
+ }
+ }
+ if (postExecute != null) {
+ postExecute.run();
+ }
+ }
+ };
+ // We don't want to show the spinner every time we load an image, because that would be
+ // annoying; instead, only start showing the spinner if loading the image has taken
+ // longer than 1 sec (ie 1000 ms)
+ progressView.postDelayed(new Runnable() {
+ public void run() {
+ if (loadBitmapTask.getStatus() != AsyncTask.Status.FINISHED) {
+ progressView.setVisibility(View.VISIBLE);
+ }
+ }
+ }, 1000);
+ loadBitmapTask.execute();
}
public boolean enableRotation() {
@@ -192,16 +247,18 @@ public class WallpaperCropActivity extends Activity {
private static int getRotationFromExifHelper(
String path, Resources res, int resId, Context context, Uri uri) {
ExifInterface ei = new ExifInterface();
+ InputStream is = null;
+ BufferedInputStream bis = null;
try {
if (path != null) {
ei.readExif(path);
} else if (uri != null) {
- InputStream is = context.getContentResolver().openInputStream(uri);
- BufferedInputStream bis = new BufferedInputStream(is);
+ is = context.getContentResolver().openInputStream(uri);
+ bis = new BufferedInputStream(is);
ei.readExif(bis);
} else {
- InputStream is = res.openRawResource(resId);
- BufferedInputStream bis = new BufferedInputStream(is);
+ is = res.openRawResource(resId);
+ bis = new BufferedInputStream(is);
ei.readExif(bis);
}
Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
@@ -210,6 +267,9 @@ public class WallpaperCropActivity extends Activity {
}
} catch (IOException e) {
Log.w(LOGTAG, "Getting exif data failed", e);
+ } finally {
+ Utils.closeSilently(bis);
+ Utils.closeSilently(is);
}
return 0;
}
@@ -269,40 +329,15 @@ public class WallpaperCropActivity extends Activity {
// Get the crop
boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
- Point minDims = new Point();
- Point maxDims = new Point();
+
Display d = getWindowManager().getDefaultDisplay();
- d.getCurrentSizeRange(minDims, maxDims);
Point displaySize = new Point();
d.getSize(displaySize);
-
- int maxDim = Math.max(maxDims.x, maxDims.y);
- final int minDim = Math.min(minDims.x, minDims.y);
- int defaultWallpaperWidth;
- if (isScreenLarge(getResources())) {
- defaultWallpaperWidth = (int) (maxDim *
- wallpaperTravelToScreenWidthRatio(maxDim, minDim));
- } else {
- defaultWallpaperWidth = Math.max((int)
- (minDim * WALLPAPER_SCREENS_SPAN), maxDim);
- }
-
boolean isPortrait = displaySize.x < displaySize.y;
- int portraitHeight;
- if (isPortrait) {
- portraitHeight = mCropView.getHeight();
- } else {
- // TODO: how to actually get the proper portrait height?
- // This is not quite right:
- portraitHeight = Math.max(maxDims.x, maxDims.y);
- }
- if (android.os.Build.VERSION.SDK_INT >=
- android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
- Point realSize = new Point();
- d.getRealSize(realSize);
- portraitHeight = Math.max(realSize.x, realSize.y);
- }
+
+ Point defaultWallpaperSize = getDefaultWallpaperSize(getResources(),
+ getWindowManager());
// Get the crop
RectF cropRect = mCropView.getCrop();
int cropRotation = mCropView.getImageRotation();
@@ -321,7 +356,7 @@ public class WallpaperCropActivity extends Activity {
// (or all the way to the left, in RTL)
float extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left;
// Cap the amount of extra width
- float maxExtraSpace = defaultWallpaperWidth / cropScale - cropRect.width();
+ float maxExtraSpace = defaultWallpaperSize.x / cropScale - cropRect.width();
extraSpace = Math.min(extraSpace, maxExtraSpace);
if (ltr) {
@@ -332,10 +367,10 @@ public class WallpaperCropActivity extends Activity {
// ADJUST CROP HEIGHT
if (isPortrait) {
- cropRect.bottom = cropRect.top + portraitHeight / cropScale;
+ cropRect.bottom = cropRect.top + defaultWallpaperSize.y / cropScale;
} else { // LANDSCAPE
float extraPortraitHeight =
- portraitHeight / cropScale - cropRect.height();
+ defaultWallpaperSize.y / cropScale - cropRect.height();
float expandHeight =
Math.min(Math.min(rotatedInSize[1] - cropRect.bottom, cropRect.top),
extraPortraitHeight / 2);
@@ -372,7 +407,6 @@ public class WallpaperCropActivity extends Activity {
String mInFilePath;
byte[] mInImageBytes;
int mInResId = 0;
- InputStream mInStream;
RectF mCropBounds = null;
int mOutWidth, mOutHeight;
int mRotation;
@@ -445,37 +479,36 @@ public class WallpaperCropActivity extends Activity {
}
// Helper to setup input stream
- private void regenerateInputStream() {
+ private InputStream regenerateInputStream() {
if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) {
Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " +
"image byte array given");
} else {
- Utils.closeSilently(mInStream);
try {
if (mInUri != null) {
- mInStream = new BufferedInputStream(
+ return new BufferedInputStream(
mContext.getContentResolver().openInputStream(mInUri));
} else if (mInFilePath != null) {
- mInStream = mContext.openFileInput(mInFilePath);
+ return mContext.openFileInput(mInFilePath);
} else if (mInImageBytes != null) {
- mInStream = new BufferedInputStream(
- new ByteArrayInputStream(mInImageBytes));
+ return new BufferedInputStream(new ByteArrayInputStream(mInImageBytes));
} else {
- mInStream = new BufferedInputStream(
- mResources.openRawResource(mInResId));
+ return new BufferedInputStream(mResources.openRawResource(mInResId));
}
} catch (FileNotFoundException e) {
Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e);
}
}
+ return null;
}
public Point getImageBounds() {
- regenerateInputStream();
- if (mInStream != null) {
+ InputStream is = regenerateInputStream();
+ if (is != null) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
- BitmapFactory.decodeStream(mInStream, null, options);
+ BitmapFactory.decodeStream(is, null, options);
+ Utils.closeSilently(is);
if (options.outWidth != 0 && options.outHeight != 0) {
return new Point(options.outWidth, options.outHeight);
}
@@ -493,22 +526,26 @@ public class WallpaperCropActivity extends Activity {
public boolean cropBitmap() {
boolean failure = false;
- regenerateInputStream();
WallpaperManager wallpaperManager = null;
if (mSetWallpaper) {
wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext());
}
- if (mSetWallpaper && mNoCrop && mInStream != null) {
+
+
+ if (mSetWallpaper && mNoCrop) {
try {
- wallpaperManager.setStream(mInStream);
+ InputStream is = regenerateInputStream();
+ if (is != null) {
+ wallpaperManager.setStream(is);
+ Utils.closeSilently(is);
+ }
} catch (IOException e) {
Log.w(LOGTAG, "cannot write stream to wallpaper", e);
failure = true;
}
return !failure;
- }
- if (mInStream != null) {
+ } else {
// Find crop bounds (scaled to original image size)
Rect roundedTrueCrop = new Rect();
Matrix rotateMatrix = new Matrix();
@@ -521,6 +558,11 @@ public class WallpaperCropActivity extends Activity {
mCropBounds = new RectF(roundedTrueCrop);
Point bounds = getImageBounds();
+ if (bounds == null) {
+ Log.w(LOGTAG, "cannot get bounds for image");
+ failure = true;
+ return false;
+ }
float[] rotatedBounds = new float[] { bounds.x, bounds.y };
rotateMatrix.mapPoints(rotatedBounds);
@@ -531,7 +573,6 @@ public class WallpaperCropActivity extends Activity {
inverseRotateMatrix.mapRect(mCropBounds);
mCropBounds.offset(bounds.x/2, bounds.y/2);
- regenerateInputStream();
}
mCropBounds.roundOut(roundedTrueCrop);
@@ -543,15 +584,25 @@ public class WallpaperCropActivity extends Activity {
}
// See how much we're reducing the size of the image
- int scaleDownSampleSize = Math.min(roundedTrueCrop.width() / mOutWidth,
- roundedTrueCrop.height() / mOutHeight);
-
+ int scaleDownSampleSize = Math.max(1, Math.min(roundedTrueCrop.width() / mOutWidth,
+ roundedTrueCrop.height() / mOutHeight));
// Attempt to open a region decoder
BitmapRegionDecoder decoder = null;
+ InputStream is = null;
try {
- decoder = BitmapRegionDecoder.newInstance(mInStream, true);
+ is = regenerateInputStream();
+ if (is == null) {
+ Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString());
+ failure = true;
+ return false;
+ }
+ decoder = BitmapRegionDecoder.newInstance(is, false);
+ Utils.closeSilently(is);
} catch (IOException e) {
Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e);
+ } finally {
+ Utils.closeSilently(is);
+ is = null;
}
Bitmap crop = null;
@@ -567,14 +618,15 @@ public class WallpaperCropActivity extends Activity {
if (crop == null) {
// BitmapRegionDecoder has failed, try to crop in-memory
- regenerateInputStream();
+ is = regenerateInputStream();
Bitmap fullSize = null;
- if (mInStream != null) {
+ if (is != null) {
BitmapFactory.Options options = new BitmapFactory.Options();
if (scaleDownSampleSize > 1) {
options.inSampleSize = scaleDownSampleSize;
}
- fullSize = BitmapFactory.decodeStream(mInStream, null, options);
+ fullSize = BitmapFactory.decodeStream(is, null, options);
+ Utils.closeSilently(is);
}
if (fullSize != null) {
mCropBounds.left /= scaleDownSampleSize;
@@ -686,7 +738,7 @@ public class WallpaperCropActivity extends Activity {
protected void updateWallpaperDimensions(int width, int height) {
String spKey = getSharedPreferencesKey();
- SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE);
+ SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
SharedPreferences.Editor editor = sp.edit();
if (width != 0 && height != 0) {
editor.putInt(WALLPAPER_WIDTH_KEY, width);
diff --git a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
index 596435a..10bcdad 100644
--- a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
+++ b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
@@ -117,8 +117,8 @@ public class ProxyServer extends Thread {
if (!proxy.equals(Proxy.NO_PROXY)) {
// Only Inets created by PacProxySelector.
InetSocketAddress inetSocketAddress =
- (InetSocketAddress)list.get(0).address();
- server = new Socket(inetSocketAddress.getAddress(),
+ (InetSocketAddress)proxy.address();
+ server = new Socket(inetSocketAddress.getHostName(),
inetSocketAddress.getPort());
sendLine(server, requestLine);
} else {