summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidManifest.xml15
-rw-r--r--WallpaperPicker/res/anim/fade_out.xml3
-rw-r--r--WallpaperPicker/res/values-az-rAZ/strings.xml1
-rw-r--r--WallpaperPicker/res/values-gu-rIN/strings.xml1
-rw-r--r--WallpaperPicker/res/values-pa-rIN/strings.xml1
-rw-r--r--WallpaperPicker/res/values-sq-rAL/strings.xml1
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java2
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java28
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/common/ExifOrientation.java145
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/exif/ByteBufferInputStream.java48
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/exif/CountedDataInputStream.java136
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/exif/ExifData.java348
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/exif/ExifInterface.java2407
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/exif/ExifInvalidFormatException.java23
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/exif/ExifModifier.java195
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/exif/ExifOutputStream.java518
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/exif/ExifParser.java916
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/exif/ExifReader.java92
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/exif/ExifTag.java1008
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/exif/IfdData.java152
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/exif/IfdId.java31
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/exif/JpegHeader.java39
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/exif/OrderedDataOutputStream.java56
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/exif/Rational.java88
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/glrenderer/BasicTexture.java35
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/glrenderer/BitmapTexture.java6
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/glrenderer/GLCanvas.java94
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20Canvas.java537
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/glrenderer/GLPaint.java41
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/glrenderer/RawTexture.java73
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/glrenderer/Texture.java7
-rw-r--r--WallpaperPicker/src/com/android/gallery3d/glrenderer/UploadedTexture.java138
-rw-r--r--WallpaperPicker/src/com/android/launcher3/CropView.java2
-rw-r--r--WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java203
-rw-r--r--WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java95
-rw-r--r--WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java138
-rw-r--r--WallpaperPicker/src/com/android/launcher3/ToggleOnTapCallback.java67
-rw-r--r--WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java217
-rw-r--r--WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java972
-rw-r--r--WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/DefaultWallpaperInfo.java164
-rw-r--r--WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/DrawableThumbWallpaperInfo.java37
-rw-r--r--WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/FileWallpaperInfo.java52
-rw-r--r--WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/LiveWallpaperInfo.java118
-rw-r--r--WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/PickImageInfo.java74
-rw-r--r--WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/ResourceWallpaperInfo.java65
-rw-r--r--WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/ThirdPartyWallpaperInfo.java78
-rw-r--r--WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/UriWallpaperInfo.java109
-rw-r--r--WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/WallpaperTileInfo.java87
-rw-r--r--WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java66
-rw-r--r--WallpaperPicker/src/com/android/photos/views/TiledImageView.java102
-rw-r--r--proguard.flags2
-rw-r--r--res/animator-v21/overview_button_anim.xml42
-rw-r--r--res/drawable-hdpi/page_hover_left.9.pngbin102 -> 0 bytes
-rw-r--r--res/drawable-hdpi/page_hover_left_active.9.pngbin102 -> 0 bytes
-rw-r--r--res/drawable-hdpi/page_hover_right.9.pngbin100 -> 0 bytes
-rw-r--r--res/drawable-hdpi/page_hover_right_active.9.pngbin100 -> 0 bytes
-rw-r--r--res/drawable-mdpi/page_hover_left.9.pngbin99 -> 0 bytes
-rw-r--r--res/drawable-mdpi/page_hover_left_active.9.pngbin99 -> 0 bytes
-rw-r--r--res/drawable-mdpi/page_hover_right.9.pngbin97 -> 0 bytes
-rw-r--r--res/drawable-mdpi/page_hover_right_active.9.pngbin98 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/page_hover_left.9.pngbin104 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/page_hover_left_active.9.pngbin104 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/page_hover_right.9.pngbin108 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/page_hover_right_active.9.pngbin108 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/page_hover_left.9.pngbin116 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/page_hover_left_active.9.pngbin116 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/page_hover_right.9.pngbin122 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/page_hover_right_active.9.pngbin122 -> 0 bytes
-rw-r--r--res/drawable/ic_setting_pressed.xml19
-rw-r--r--res/drawable/ic_wallpaper_pressed.xml19
-rw-r--r--res/drawable/ic_widget_pressed.xml19
-rw-r--r--res/layout-land/launcher.xml11
-rw-r--r--res/layout-port/launcher.xml9
-rw-r--r--res/layout-sw720dp/launcher.xml9
-rw-r--r--res/layout-v21/overview_panel.xml69
-rw-r--r--res/layout/app_info_drop_target_bar.xml42
-rw-r--r--res/layout/search_drop_target_bar.xml16
-rw-r--r--res/layout/workspace_screen.xml5
-rw-r--r--res/values-af/strings.xml6
-rw-r--r--res/values-am/strings.xml6
-rw-r--r--res/values-ar/strings.xml6
-rw-r--r--res/values-az-rAZ/strings.xml14
-rw-r--r--res/values-bg/strings.xml6
-rw-r--r--res/values-bn-rBD/strings.xml6
-rw-r--r--res/values-ca/strings.xml6
-rw-r--r--res/values-cs/strings.xml6
-rw-r--r--res/values-da/strings.xml6
-rw-r--r--res/values-de/strings.xml6
-rw-r--r--res/values-el/strings.xml6
-rw-r--r--res/values-en-rAU/strings.xml6
-rw-r--r--res/values-en-rGB/strings.xml6
-rw-r--r--res/values-en-rIN/strings.xml6
-rw-r--r--res/values-es-rUS/strings.xml6
-rw-r--r--res/values-es/strings.xml6
-rw-r--r--res/values-et-rEE/strings.xml6
-rw-r--r--res/values-eu-rES/strings.xml6
-rw-r--r--res/values-fa/strings.xml6
-rw-r--r--res/values-fi/strings.xml6
-rw-r--r--res/values-fr-rCA/strings.xml6
-rw-r--r--res/values-fr/strings.xml6
-rw-r--r--res/values-gl-rES/strings.xml6
-rw-r--r--res/values-gu-rIN/strings.xml14
-rw-r--r--res/values-hi/strings.xml6
-rw-r--r--res/values-hr/strings.xml6
-rw-r--r--res/values-hu/strings.xml6
-rw-r--r--res/values-hy-rAM/strings.xml6
-rw-r--r--res/values-in/strings.xml6
-rw-r--r--res/values-is-rIS/strings.xml6
-rw-r--r--res/values-it/strings.xml6
-rw-r--r--res/values-iw/strings.xml6
-rw-r--r--res/values-ja/strings.xml6
-rw-r--r--res/values-ka-rGE/strings.xml6
-rw-r--r--res/values-kk-rKZ/strings.xml6
-rw-r--r--res/values-km-rKH/strings.xml6
-rw-r--r--res/values-kn-rIN/strings.xml6
-rw-r--r--res/values-ko/strings.xml6
-rw-r--r--res/values-ky-rKG/strings.xml6
-rw-r--r--res/values-land/styles.xml3
-rw-r--r--res/values-lo-rLA/strings.xml6
-rw-r--r--res/values-lt/strings.xml6
-rw-r--r--res/values-lv/strings.xml6
-rw-r--r--res/values-mk-rMK/strings.xml6
-rw-r--r--res/values-ml-rIN/strings.xml6
-rw-r--r--res/values-mn-rMN/strings.xml6
-rw-r--r--res/values-mr-rIN/strings.xml6
-rw-r--r--res/values-ms-rMY/strings.xml6
-rw-r--r--res/values-my-rMM/strings.xml6
-rw-r--r--res/values-nb/strings.xml6
-rw-r--r--res/values-ne-rNP/strings.xml6
-rw-r--r--res/values-nl/strings.xml6
-rw-r--r--res/values-pa-rIN/strings.xml14
-rw-r--r--res/values-pl/strings.xml6
-rw-r--r--res/values-pt-rPT/strings.xml6
-rw-r--r--res/values-pt/strings.xml6
-rw-r--r--res/values-ro/strings.xml6
-rw-r--r--res/values-ru/strings.xml6
-rw-r--r--res/values-si-rLK/strings.xml6
-rw-r--r--res/values-sk/strings.xml6
-rw-r--r--res/values-sl/strings.xml6
-rw-r--r--res/values-sq-rAL/strings.xml14
-rw-r--r--res/values-sr/strings.xml6
-rw-r--r--res/values-sv/strings.xml6
-rw-r--r--res/values-sw/strings.xml6
-rw-r--r--res/values-sw340dp-port/styles.xml27
-rw-r--r--res/values-sw600dp/config.xml3
-rw-r--r--res/values-sw600dp/dimens.xml6
-rw-r--r--res/values-sw720dp-land/dimens.xml21
-rw-r--r--res/values-sw720dp-port/dimens.xml22
-rw-r--r--res/values-sw720dp/styles.xml2
-rw-r--r--res/values-ta-rIN/strings.xml6
-rw-r--r--res/values-te-rIN/strings.xml6
-rw-r--r--res/values-th/strings.xml6
-rw-r--r--res/values-tl/strings.xml6
-rw-r--r--res/values-tr/strings.xml6
-rw-r--r--res/values-uk/strings.xml6
-rw-r--r--res/values-ur-rPK/strings.xml6
-rw-r--r--res/values-uz-rUZ/strings.xml6
-rw-r--r--res/values-vi/strings.xml6
-rw-r--r--res/values-zh-rCN/strings.xml6
-rw-r--r--res/values-zh-rHK/strings.xml6
-rw-r--r--res/values-zh-rTW/strings.xml6
-rw-r--r--res/values-zu/strings.xml6
-rw-r--r--res/values/attrs.xml40
-rw-r--r--res/values/config.xml7
-rw-r--r--res/values/dimens.xml15
-rw-r--r--res/values/strings.xml10
-rw-r--r--res/values/styles.xml2
-rw-r--r--src/com/android/launcher3/AnotherWindowDropTarget.java64
-rw-r--r--src/com/android/launcher3/AppInfo.java7
-rw-r--r--src/com/android/launcher3/AppInfoDropTargetBar.java74
-rw-r--r--src/com/android/launcher3/AppWidgetResizeFrame.java43
-rw-r--r--src/com/android/launcher3/BaseContainerView.java4
-rw-r--r--src/com/android/launcher3/BaseDropTargetBar.java131
-rw-r--r--src/com/android/launcher3/BubbleTextView.java4
-rw-r--r--src/com/android/launcher3/BuildInfo.java25
-rw-r--r--src/com/android/launcher3/ButtonDropTarget.java32
-rw-r--r--src/com/android/launcher3/CellLayout.java195
-rw-r--r--src/com/android/launcher3/DeleteDropTarget.java27
-rw-r--r--src/com/android/launcher3/DeviceProfile.java72
-rw-r--r--src/com/android/launcher3/DragSource.java2
-rw-r--r--src/com/android/launcher3/DropTarget.java11
-rw-r--r--src/com/android/launcher3/ExtendedEditText.java7
-rw-r--r--src/com/android/launcher3/FocusHelper.java3
-rw-r--r--src/com/android/launcher3/FocusIndicatorView.java16
-rw-r--r--src/com/android/launcher3/Folder.java109
-rw-r--r--src/com/android/launcher3/FolderIcon.java25
-rw-r--r--src/com/android/launcher3/FolderInfo.java3
-rw-r--r--src/com/android/launcher3/FolderPagedView.java10
-rw-r--r--src/com/android/launcher3/IconCache.java56
-rw-r--r--src/com/android/launcher3/InfoDropTarget.java37
-rw-r--r--src/com/android/launcher3/ItemInfo.java63
-rw-r--r--src/com/android/launcher3/Launcher.java400
-rw-r--r--src/com/android/launcher3/LauncherAnimUtils.java41
-rw-r--r--src/com/android/launcher3/LauncherAppState.java30
-rw-r--r--src/com/android/launcher3/LauncherAppWidgetHostView.java7
-rw-r--r--src/com/android/launcher3/LauncherBackupAgentHelper.java15
-rw-r--r--src/com/android/launcher3/LauncherClings.java37
-rw-r--r--src/com/android/launcher3/LauncherModel.java336
-rw-r--r--src/com/android/launcher3/LauncherProvider.java115
-rw-r--r--src/com/android/launcher3/LauncherSettings.java21
-rw-r--r--src/com/android/launcher3/LauncherStateTransitionAnimation.java193
-rw-r--r--src/com/android/launcher3/PagedView.java113
-rw-r--r--src/com/android/launcher3/SearchDropTargetBar.java119
-rw-r--r--src/com/android/launcher3/ShortcutAndWidgetContainer.java23
-rw-r--r--src/com/android/launcher3/ShortcutInfo.java14
-rw-r--r--src/com/android/launcher3/SimpleOnStylusPressListener.java25
-rw-r--r--src/com/android/launcher3/StartupReceiver.java15
-rw-r--r--src/com/android/launcher3/Stats.java4
-rw-r--r--src/com/android/launcher3/StylusEventHelper.java85
-rw-r--r--src/com/android/launcher3/UninstallDropTarget.java27
-rw-r--r--src/com/android/launcher3/Utilities.java84
-rw-r--r--src/com/android/launcher3/WallpaperChangedReceiver.java27
-rw-r--r--src/com/android/launcher3/Workspace.java689
-rw-r--r--src/com/android/launcher3/WorkspaceStateTransitionAnimation.java31
-rw-r--r--src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java12
-rw-r--r--src/com/android/launcher3/allapps/AllAppsContainerView.java4
-rw-r--r--src/com/android/launcher3/allapps/AllAppsGridAdapter.java11
-rw-r--r--src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java4
-rw-r--r--src/com/android/launcher3/allapps/AllAppsSearchBarController.java1
-rw-r--r--src/com/android/launcher3/allapps/AlphabeticalAppsList.java10
-rw-r--r--src/com/android/launcher3/compat/AlphabeticIndexCompat.java2
-rw-r--r--src/com/android/launcher3/config/ProviderConfig.java2
-rw-r--r--src/com/android/launcher3/dragndrop/DragController.java (renamed from src/com/android/launcher3/DragController.java)388
-rw-r--r--src/com/android/launcher3/dragndrop/DragDriver.java250
-rw-r--r--src/com/android/launcher3/dragndrop/DragLayer.java (renamed from src/com/android/launcher3/DragLayer.java)105
-rw-r--r--src/com/android/launcher3/dragndrop/DragScroller.java (renamed from src/com/android/launcher3/DragScroller.java)2
-rw-r--r--src/com/android/launcher3/dragndrop/DragView.java (renamed from src/com/android/launcher3/DragView.java)100
-rw-r--r--src/com/android/launcher3/dragndrop/SpringLoadedDragController.java (renamed from src/com/android/launcher3/SpringLoadedDragController.java)8
-rw-r--r--src/com/android/launcher3/model/MigrateFromRestoreTask.java5
-rw-r--r--src/com/android/launcher3/model/PackageItemInfo.java5
-rw-r--r--src/com/android/launcher3/testing/LauncherExtension.java3
-rw-r--r--src/com/android/launcher3/util/FlingAnimation.java36
-rw-r--r--src/com/android/launcher3/util/ViewOnDrawExecutor.java96
-rw-r--r--src/com/android/launcher3/util/WallpaperOffsetInterpolator.java225
-rw-r--r--src/com/android/launcher3/util/WallpaperUtils.java81
-rw-r--r--src/com/android/launcher3/widget/WidgetCell.java5
-rw-r--r--src/com/android/launcher3/widget/WidgetHostViewLoader.java7
-rw-r--r--src/com/android/launcher3/widget/WidgetsContainerView.java12
-rw-r--r--src/com/android/launcher3/widget/WidgetsListAdapter.java20
-rw-r--r--src/com/android/launcher3/widget/WidgetsRowViewHolder.java3
240 files changed, 4496 insertions, 11117 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 990bde0b1..1981b13a2 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -51,10 +51,8 @@
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
- <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.BIND_APPWIDGET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.BROADCAST_STICKY"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
@@ -127,13 +125,6 @@
android:process=":settings_process">
</activity>
- <receiver
- android:name="com.android.launcher3.WallpaperChangedReceiver">
- <intent-filter>
- <action android:name="android.intent.action.WALLPAPER_CHANGED" />
- </intent-filter>
- </receiver>
-
<!-- Intent received used to install shortcuts from other applications -->
<receiver
android:name="com.android.launcher3.InstallShortcutReceiver"
@@ -150,12 +141,6 @@
</intent-filter>
</receiver>
- <receiver android:name="com.android.launcher3.StartupReceiver" >
- <intent-filter>
- <action android:name="android.intent.action.BOOT_COMPLETED" />
- </intent-filter>
- </receiver>
-
<!-- The settings provider contains Home's data, like the workspace favorites -->
<provider
android:name="com.android.launcher3.LauncherProvider"
diff --git a/WallpaperPicker/res/anim/fade_out.xml b/WallpaperPicker/res/anim/fade_out.xml
index 2749d9280..9ca7407e7 100644
--- a/WallpaperPicker/res/anim/fade_out.xml
+++ b/WallpaperPicker/res/anim/fade_out.xml
@@ -20,5 +20,4 @@
android:startOffset="@android:integer/config_longAnimTime"
android:duration="@android:integer/config_mediumAnimTime"
android:fromAlpha="1"
- android:toAlpha="0"/>
-
+ android:toAlpha="0"/> \ No newline at end of file
diff --git a/WallpaperPicker/res/values-az-rAZ/strings.xml b/WallpaperPicker/res/values-az-rAZ/strings.xml
index 883673dd6..c42d71181 100644
--- a/WallpaperPicker/res/values-az-rAZ/strings.xml
+++ b/WallpaperPicker/res/values-az-rAZ/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Divar kağı seçin"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Şəkli yükləmək alınmadı"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Şəkli divar kağızı olaraq yükləmək alınmadı"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Şəkli divar kağızı olaraq quraşdırmaq alınmadı"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d seçilib"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d seçilib"</item>
diff --git a/WallpaperPicker/res/values-gu-rIN/strings.xml b/WallpaperPicker/res/values-gu-rIN/strings.xml
index e201d523f..f2932877c 100644
--- a/WallpaperPicker/res/values-gu-rIN/strings.xml
+++ b/WallpaperPicker/res/values-gu-rIN/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"વૉલપેપર સેટ કરો"</string>
<string name="image_load_fail" msgid="7538534580694411837">"છબી લોડ કરી શકાઈ નથી"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"વૉલપેપર તરીકે છબી લોડ કરી શકાઈ નથી"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"વૉલપેપર તરીકે છબી સેટ કરી શક્યાં નથી"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d પસંદ કર્યો"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d પસંદ કર્યો"</item>
diff --git a/WallpaperPicker/res/values-pa-rIN/strings.xml b/WallpaperPicker/res/values-pa-rIN/strings.xml
index e4225e02d..86cdf22ed 100644
--- a/WallpaperPicker/res/values-pa-rIN/strings.xml
+++ b/WallpaperPicker/res/values-pa-rIN/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"ਵਾਲਪੇਪਰ ਸੈਟ ਕਰੋ"</string>
<string name="image_load_fail" msgid="7538534580694411837">"ਚਿੱਤਰ ਲੋਡ ਨਹੀਂ ਕਰ ਸਕਿਆ"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"ਵਾਲਪੇਪਰ ਦੇ ਤੌਰ ਤੇ ਚਿੱਤਰ ਲੋਡ ਨਹੀਂ ਕਰ ਸਕਿਆ"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"ਵਾਲਪੇਪਰ ਦੇ ਤੌਰ ਤੇ ਚਿੱਤਰ ਸੈਟ ਨਹੀਂ ਕਰ ਸਕਿਆ"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d ਚੁਣਿਆ ਗਿਆ"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d ਚੁਣਿਆ ਗਿਆ"</item>
diff --git a/WallpaperPicker/res/values-sq-rAL/strings.xml b/WallpaperPicker/res/values-sq-rAL/strings.xml
index 8a9983b4b..8259d66b4 100644
--- a/WallpaperPicker/res/values-sq-rAL/strings.xml
+++ b/WallpaperPicker/res/values-sq-rAL/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Cakto imazhin e sfondit"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Nuk mund të ngarkonte imazhin"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Nuk mundi të ngarkonte imazhin si imazh sfondi"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Nuk mundi të vendoste imazhin si imazh sfondi."</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"Të përzgjedhur: %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"Të përzgjedhur: %1$d"</item>
diff --git a/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java b/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java
index 8b5144748..212fb84ea 100644
--- a/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java
+++ b/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java
@@ -126,7 +126,7 @@ public class BitmapCropTask extends AsyncTask<Void, Void, Boolean> {
mNoCrop = value;
}
- public void setOnEndRunnable(OnEndCropHandler onEndCropHandler) {
+ public void setOnEndCropHandler(OnEndCropHandler onEndCropHandler) {
mOnEndCropHandler = onEndCropHandler;
}
diff --git a/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java b/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java
index 9ac5c1bf7..6ddda87b5 100644
--- a/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java
+++ b/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java
@@ -21,8 +21,6 @@ import android.content.res.Resources;
import android.net.Uri;
import android.util.Log;
-import com.android.gallery3d.exif.ExifInterface;
-
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -42,38 +40,26 @@ public class BitmapUtils {
}
public static int getRotationFromExif(Context context, Uri uri) {
- return BitmapUtils.getRotationFromExifHelper(null, 0, context, uri);
+ return BitmapUtils.getRotationFromExifHelper(null, 0, uri, context);
}
- public static int getRotationFromExif(Resources res, int resId) {
- return BitmapUtils.getRotationFromExifHelper(res, resId, null, null);
+ public static int getRotationFromExif(Resources res, int resId, Context context) {
+ return BitmapUtils.getRotationFromExifHelper(res, resId, null, context);
}
- private static int getRotationFromExifHelper(Resources res, int resId, Context context, Uri uri) {
- ExifInterface ei = new ExifInterface();
+ private static int getRotationFromExifHelper(Resources res, int resId,
+ Uri uri, Context context) {
InputStream is = null;
- BufferedInputStream bis = null;
try {
if (uri != null) {
is = context.getContentResolver().openInputStream(uri);
- bis = new BufferedInputStream(is);
- ei.readExif(bis);
} else {
is = res.openRawResource(resId);
- bis = new BufferedInputStream(is);
- ei.readExif(bis);
- }
- Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
- if (ori != null) {
- return ExifInterface.getRotationForOrientationValue(ori.shortValue());
}
- } catch (IOException e) {
- Log.w(TAG, "Getting exif data failed", e);
- } catch (NullPointerException e) {
- // Sometimes the ExifInterface has an internal NPE if Exif data isn't valid
+ return ExifOrientation.readRotation(new BufferedInputStream(is), context);
+ } catch (IOException | NullPointerException e) {
Log.w(TAG, "Getting exif data failed", e);
} finally {
- Utils.closeSilently(bis);
Utils.closeSilently(is);
}
return 0;
diff --git a/WallpaperPicker/src/com/android/gallery3d/common/ExifOrientation.java b/WallpaperPicker/src/com/android/gallery3d/common/ExifOrientation.java
new file mode 100644
index 000000000..ad4370c37
--- /dev/null
+++ b/WallpaperPicker/src/com/android/gallery3d/common/ExifOrientation.java
@@ -0,0 +1,145 @@
+/**
+ * Copyright (C) 2015 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.gallery3d.common;
+
+import android.content.Context;
+import android.media.ExifInterface;
+import android.util.Log;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ExifOrientation {
+ private static final String TAG = "ExifOrientation";
+ private static final boolean DEBUG = false;
+
+ private static final short SOI = (short) 0xFFD8; // start of input
+ private static final short APP0 = (short) 0xFFE0;
+ private static final short APPF = (short) 0xFFEF;
+ private static final short APP1 = (short) 0xFFE1;
+ private static final short SOS = (short) 0xFFDA; // start of stream
+ private static final short EOI = (short) 0xFFD9; // end of input
+
+ // The header is available in first 64 bytes, so reading upto 128 bytes
+ // should be more than enough.
+ private static final int MAX_BYTES_TO_READ = 128 * 1024;
+
+ /**
+ * Parses the rotation of the JPEG image from the input stream.
+ */
+ public static final int readRotation(InputStream in, Context context) {
+ // Since the platform implementation only takes file input, create a temporary file
+ // with just the image header.
+ File tempFile = null;
+ DataOutputStream tempOut = null;
+
+ try {
+ DataInputStream din = new DataInputStream(in);
+ int pos = 0;
+ if (din.readShort() == SOI) {
+ pos += 2;
+
+ short marker = din.readShort();
+ pos += 2;
+
+ while ((marker >= APP0 && marker <= APPF) && pos < MAX_BYTES_TO_READ) {
+ int length = din.readUnsignedShort();
+ if (length < 2) {
+ throw new IOException("Invalid header size");
+ }
+
+ // We only want APP1 headers
+ if (length > 2) {
+ if (marker == APP1) {
+ // Copy the header
+ if (tempFile == null) {
+ tempFile = File.createTempFile(TAG, ".jpg", context.getCacheDir());
+ tempOut = new DataOutputStream(new FileOutputStream(tempFile));
+ tempOut.writeShort(SOI);
+ }
+
+ tempOut.writeShort(marker);
+ tempOut.writeShort(length);
+
+ byte[] header = new byte[length - 2];
+ din.read(header);
+ tempOut.write(header);
+ } else {
+ din.skip(length - 2);
+ }
+ }
+ pos += length;
+
+ marker = din.readShort();
+ pos += 2;
+ }
+
+ if (tempOut != null) {
+ // Write empty image data.
+ tempOut.writeShort(SOS);
+ // Write the frame size as 2. Since this includes the size bytes as well
+ // (short = 2 bytes), it implies there is 0 byte of image data.
+ tempOut.writeShort(2);
+
+ // End of input
+ tempOut.writeShort(EOI);
+ tempOut.close();
+
+ return readRotation(tempFile.getAbsolutePath());
+ }
+ }
+ } catch (IOException e) {
+ if (DEBUG) {
+ Log.d(TAG, "Error parsing input stream", e);
+ }
+ } finally {
+ Utils.closeSilently(in);
+ Utils.closeSilently(tempOut);
+ if (tempFile != null) {
+ tempFile.delete();
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Parses the rotation of the JPEG image.
+ */
+ public static final int readRotation(String filePath) {
+ try {
+ ExifInterface exif = new ExifInterface(filePath);
+ switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0)) {
+ case ExifInterface.ORIENTATION_ROTATE_90:
+ return 90;
+ case ExifInterface.ORIENTATION_ROTATE_270:
+ return 270;
+ case ExifInterface.ORIENTATION_ROTATE_180:
+ return 180;
+ default:
+ return 0;
+ }
+ } catch (IOException e) {
+ if (DEBUG) {
+ Log.d(TAG, "Error reading file", e);
+ }
+ }
+ return 0;
+ }
+}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ByteBufferInputStream.java b/WallpaperPicker/src/com/android/gallery3d/exif/ByteBufferInputStream.java
deleted file mode 100644
index 7fb9f22cc..000000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ByteBufferInputStream.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2012 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.gallery3d.exif;
-
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-
-class ByteBufferInputStream extends InputStream {
-
- private ByteBuffer mBuf;
-
- public ByteBufferInputStream(ByteBuffer buf) {
- mBuf = buf;
- }
-
- @Override
- public int read() {
- if (!mBuf.hasRemaining()) {
- return -1;
- }
- return mBuf.get() & 0xFF;
- }
-
- @Override
- public int read(byte[] bytes, int off, int len) {
- if (!mBuf.hasRemaining()) {
- return -1;
- }
-
- len = Math.min(len, mBuf.remaining());
- mBuf.get(bytes, off, len);
- return len;
- }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/CountedDataInputStream.java b/WallpaperPicker/src/com/android/gallery3d/exif/CountedDataInputStream.java
deleted file mode 100644
index dfd4a1a10..000000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/CountedDataInputStream.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2012 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.gallery3d.exif;
-
-import java.io.EOFException;
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.charset.Charset;
-
-class CountedDataInputStream extends FilterInputStream {
-
- private int mCount = 0;
-
- // allocate a byte buffer for a long value;
- private final byte mByteArray[] = new byte[8];
- private final ByteBuffer mByteBuffer = ByteBuffer.wrap(mByteArray);
-
- protected CountedDataInputStream(InputStream in) {
- super(in);
- }
-
- public int getReadByteCount() {
- return mCount;
- }
-
- @Override
- public int read(byte[] b) throws IOException {
- int r = in.read(b);
- mCount += (r >= 0) ? r : 0;
- return r;
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- int r = in.read(b, off, len);
- mCount += (r >= 0) ? r : 0;
- return r;
- }
-
- @Override
- public int read() throws IOException {
- int r = in.read();
- mCount += (r >= 0) ? 1 : 0;
- return r;
- }
-
- @Override
- public long skip(long length) throws IOException {
- long skip = in.skip(length);
- mCount += skip;
- return skip;
- }
-
- public void skipOrThrow(long length) throws IOException {
- if (skip(length) != length) throw new EOFException();
- }
-
- public void skipTo(long target) throws IOException {
- long cur = mCount;
- long diff = target - cur;
- assert(diff >= 0);
- skipOrThrow(diff);
- }
-
- public void readOrThrow(byte[] b, int off, int len) throws IOException {
- int r = read(b, off, len);
- if (r != len) throw new EOFException();
- }
-
- public void readOrThrow(byte[] b) throws IOException {
- readOrThrow(b, 0, b.length);
- }
-
- public void setByteOrder(ByteOrder order) {
- mByteBuffer.order(order);
- }
-
- public ByteOrder getByteOrder() {
- return mByteBuffer.order();
- }
-
- public short readShort() throws IOException {
- readOrThrow(mByteArray, 0 ,2);
- mByteBuffer.rewind();
- return mByteBuffer.getShort();
- }
-
- public int readUnsignedShort() throws IOException {
- return readShort() & 0xffff;
- }
-
- public int readInt() throws IOException {
- readOrThrow(mByteArray, 0 , 4);
- mByteBuffer.rewind();
- return mByteBuffer.getInt();
- }
-
- public long readUnsignedInt() throws IOException {
- return readInt() & 0xffffffffL;
- }
-
- public long readLong() throws IOException {
- readOrThrow(mByteArray, 0 , 8);
- mByteBuffer.rewind();
- return mByteBuffer.getLong();
- }
-
- public String readString(int n) throws IOException {
- byte buf[] = new byte[n];
- readOrThrow(buf);
- return new String(buf, "UTF8");
- }
-
- public String readString(int n, Charset charset) throws IOException {
- byte buf[] = new byte[n];
- readOrThrow(buf);
- return new String(buf, charset);
- }
-} \ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ExifData.java b/WallpaperPicker/src/com/android/gallery3d/exif/ExifData.java
deleted file mode 100644
index 8422382bb..000000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ExifData.java
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * Copyright (C) 2012 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.gallery3d.exif;
-
-import android.util.Log;
-
-import java.io.UnsupportedEncodingException;
-import java.nio.ByteOrder;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * This class stores the EXIF header in IFDs according to the JPEG
- * specification. It is the result produced by {@link ExifReader}.
- *
- * @see ExifReader
- * @see IfdData
- */
-class ExifData {
- private static final String TAG = "ExifData";
- private static final byte[] USER_COMMENT_ASCII = {
- 0x41, 0x53, 0x43, 0x49, 0x49, 0x00, 0x00, 0x00
- };
- private static final byte[] USER_COMMENT_JIS = {
- 0x4A, 0x49, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00
- };
- private static final byte[] USER_COMMENT_UNICODE = {
- 0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0x00
- };
-
- private final IfdData[] mIfdDatas = new IfdData[IfdId.TYPE_IFD_COUNT];
- private byte[] mThumbnail;
- private ArrayList<byte[]> mStripBytes = new ArrayList<byte[]>();
- private final ByteOrder mByteOrder;
-
- ExifData(ByteOrder order) {
- mByteOrder = order;
- }
-
- /**
- * Gets the compressed thumbnail. Returns null if there is no compressed
- * thumbnail.
- *
- * @see #hasCompressedThumbnail()
- */
- protected byte[] getCompressedThumbnail() {
- return mThumbnail;
- }
-
- /**
- * Sets the compressed thumbnail.
- */
- protected void setCompressedThumbnail(byte[] thumbnail) {
- mThumbnail = thumbnail;
- }
-
- /**
- * Returns true it this header contains a compressed thumbnail.
- */
- protected boolean hasCompressedThumbnail() {
- return mThumbnail != null;
- }
-
- /**
- * Adds an uncompressed strip.
- */
- protected void setStripBytes(int index, byte[] strip) {
- if (index < mStripBytes.size()) {
- mStripBytes.set(index, strip);
- } else {
- for (int i = mStripBytes.size(); i < index; i++) {
- mStripBytes.add(null);
- }
- mStripBytes.add(strip);
- }
- }
-
- /**
- * Gets the strip count.
- */
- protected int getStripCount() {
- return mStripBytes.size();
- }
-
- /**
- * Gets the strip at the specified index.
- *
- * @exceptions #IndexOutOfBoundException
- */
- protected byte[] getStrip(int index) {
- return mStripBytes.get(index);
- }
-
- /**
- * Returns true if this header contains uncompressed strip.
- */
- protected boolean hasUncompressedStrip() {
- return mStripBytes.size() != 0;
- }
-
- /**
- * Gets the byte order.
- */
- protected ByteOrder getByteOrder() {
- return mByteOrder;
- }
-
- /**
- * Returns the {@link IfdData} object corresponding to a given IFD if it
- * exists or null.
- */
- protected IfdData getIfdData(int ifdId) {
- if (ExifTag.isValidIfd(ifdId)) {
- return mIfdDatas[ifdId];
- }
- return null;
- }
-
- /**
- * Adds IFD data. If IFD data of the same type already exists, it will be
- * replaced by the new data.
- */
- protected void addIfdData(IfdData data) {
- mIfdDatas[data.getId()] = data;
- }
-
- /**
- * Returns the {@link IfdData} object corresponding to a given IFD or
- * generates one if none exist.
- */
- protected IfdData getOrCreateIfdData(int ifdId) {
- IfdData ifdData = mIfdDatas[ifdId];
- if (ifdData == null) {
- ifdData = new IfdData(ifdId);
- mIfdDatas[ifdId] = ifdData;
- }
- return ifdData;
- }
-
- /**
- * Returns the tag with a given TID in the given IFD if the tag exists.
- * Otherwise returns null.
- */
- protected ExifTag getTag(short tag, int ifd) {
- IfdData ifdData = mIfdDatas[ifd];
- return (ifdData == null) ? null : ifdData.getTag(tag);
- }
-
- /**
- * Adds the given ExifTag to its default IFD and returns an existing ExifTag
- * with the same TID or null if none exist.
- */
- protected ExifTag addTag(ExifTag tag) {
- if (tag != null) {
- int ifd = tag.getIfd();
- return addTag(tag, ifd);
- }
- return null;
- }
-
- /**
- * Adds the given ExifTag to the given IFD and returns an existing ExifTag
- * with the same TID or null if none exist.
- */
- protected ExifTag addTag(ExifTag tag, int ifdId) {
- if (tag != null && ExifTag.isValidIfd(ifdId)) {
- IfdData ifdData = getOrCreateIfdData(ifdId);
- return ifdData.setTag(tag);
- }
- return null;
- }
-
- protected void clearThumbnailAndStrips() {
- mThumbnail = null;
- mStripBytes.clear();
- }
-
- /**
- * Removes the thumbnail and its related tags. IFD1 will be removed.
- */
- protected void removeThumbnailData() {
- clearThumbnailAndStrips();
- mIfdDatas[IfdId.TYPE_IFD_1] = null;
- }
-
- /**
- * Removes the tag with a given TID and IFD.
- */
- protected void removeTag(short tagId, int ifdId) {
- IfdData ifdData = mIfdDatas[ifdId];
- if (ifdData == null) {
- return;
- }
- ifdData.removeTag(tagId);
- }
-
- /**
- * Decodes the user comment tag into string as specified in the EXIF
- * standard. Returns null if decoding failed.
- */
- protected String getUserComment() {
- IfdData ifdData = mIfdDatas[IfdId.TYPE_IFD_0];
- if (ifdData == null) {
- return null;
- }
- ExifTag tag = ifdData.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_USER_COMMENT));
- if (tag == null) {
- return null;
- }
- if (tag.getComponentCount() < 8) {
- return null;
- }
-
- byte[] buf = new byte[tag.getComponentCount()];
- tag.getBytes(buf);
-
- byte[] code = new byte[8];
- System.arraycopy(buf, 0, code, 0, 8);
-
- try {
- if (Arrays.equals(code, USER_COMMENT_ASCII)) {
- return new String(buf, 8, buf.length - 8, "US-ASCII");
- } else if (Arrays.equals(code, USER_COMMENT_JIS)) {
- return new String(buf, 8, buf.length - 8, "EUC-JP");
- } else if (Arrays.equals(code, USER_COMMENT_UNICODE)) {
- return new String(buf, 8, buf.length - 8, "UTF-16");
- } else {
- return null;
- }
- } catch (UnsupportedEncodingException e) {
- Log.w(TAG, "Failed to decode the user comment");
- return null;
- }
- }
-
- /**
- * Returns a list of all {@link ExifTag}s in the ExifData or null if there
- * are none.
- */
- protected List<ExifTag> getAllTags() {
- ArrayList<ExifTag> ret = new ArrayList<ExifTag>();
- for (IfdData d : mIfdDatas) {
- if (d != null) {
- ExifTag[] tags = d.getAllTags();
- if (tags != null) {
- for (ExifTag t : tags) {
- ret.add(t);
- }
- }
- }
- }
- if (ret.size() == 0) {
- return null;
- }
- return ret;
- }
-
- /**
- * Returns a list of all {@link ExifTag}s in a given IFD or null if there
- * are none.
- */
- protected List<ExifTag> getAllTagsForIfd(int ifd) {
- IfdData d = mIfdDatas[ifd];
- if (d == null) {
- return null;
- }
- ExifTag[] tags = d.getAllTags();
- if (tags == null) {
- return null;
- }
- ArrayList<ExifTag> ret = new ArrayList<ExifTag>(tags.length);
- for (ExifTag t : tags) {
- ret.add(t);
- }
- if (ret.size() == 0) {
- return null;
- }
- return ret;
- }
-
- /**
- * Returns a list of all {@link ExifTag}s with a given TID or null if there
- * are none.
- */
- protected List<ExifTag> getAllTagsForTagId(short tag) {
- ArrayList<ExifTag> ret = new ArrayList<ExifTag>();
- for (IfdData d : mIfdDatas) {
- if (d != null) {
- ExifTag t = d.getTag(tag);
- if (t != null) {
- ret.add(t);
- }
- }
- }
- if (ret.size() == 0) {
- return null;
- }
- return ret;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (obj instanceof ExifData) {
- ExifData data = (ExifData) obj;
- if (data.mByteOrder != mByteOrder ||
- data.mStripBytes.size() != mStripBytes.size() ||
- !Arrays.equals(data.mThumbnail, mThumbnail)) {
- return false;
- }
- for (int i = 0; i < mStripBytes.size(); i++) {
- if (!Arrays.equals(data.mStripBytes.get(i), mStripBytes.get(i))) {
- return false;
- }
- }
- for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
- IfdData ifd1 = data.getIfdData(i);
- IfdData ifd2 = getIfdData(i);
- if (ifd1 != ifd2 && ifd1 != null && !ifd1.equals(ifd2)) {
- return false;
- }
- }
- return true;
- }
- return false;
- }
-
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ExifInterface.java b/WallpaperPicker/src/com/android/gallery3d/exif/ExifInterface.java
deleted file mode 100644
index 9247e879f..000000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ExifInterface.java
+++ /dev/null
@@ -1,2407 +0,0 @@
-/*
- * 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.gallery3d.exif;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.util.SparseIntArray;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.channels.FileChannel.MapMode;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.TimeZone;
-
-/**
- * This class provides methods and constants for reading and writing jpeg file
- * metadata. It contains a collection of ExifTags, and a collection of
- * definitions for creating valid ExifTags. The collection of ExifTags can be
- * updated by: reading new ones from a file, deleting or adding existing ones,
- * or building new ExifTags from a tag definition. These ExifTags can be written
- * to a valid jpeg image as exif metadata.
- * <p>
- * Each ExifTag has a tag ID (TID) and is stored in a specific image file
- * directory (IFD) as specified by the exif standard. A tag definition can be
- * looked up with a constant that is a combination of TID and IFD. This
- * definition has information about the type, number of components, and valid
- * IFDs for a tag.
- *
- * @see ExifTag
- */
-public class ExifInterface {
- public static final int TAG_NULL = -1;
- public static final int IFD_NULL = -1;
- public static final int DEFINITION_NULL = 0;
-
- /**
- * Tag constants for Jeita EXIF 2.2
- */
-
- // IFD 0
- public static final int TAG_IMAGE_WIDTH =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0100);
- public static final int TAG_IMAGE_LENGTH =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0101); // Image height
- public static final int TAG_BITS_PER_SAMPLE =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0102);
- public static final int TAG_COMPRESSION =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0103);
- public static final int TAG_PHOTOMETRIC_INTERPRETATION =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0106);
- public static final int TAG_IMAGE_DESCRIPTION =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x010E);
- public static final int TAG_MAKE =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x010F);
- public static final int TAG_MODEL =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0110);
- public static final int TAG_STRIP_OFFSETS =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0111);
- public static final int TAG_ORIENTATION =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0112);
- public static final int TAG_SAMPLES_PER_PIXEL =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0115);
- public static final int TAG_ROWS_PER_STRIP =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0116);
- public static final int TAG_STRIP_BYTE_COUNTS =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0117);
- public static final int TAG_X_RESOLUTION =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x011A);
- public static final int TAG_Y_RESOLUTION =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x011B);
- public static final int TAG_PLANAR_CONFIGURATION =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x011C);
- public static final int TAG_RESOLUTION_UNIT =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0128);
- public static final int TAG_TRANSFER_FUNCTION =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x012D);
- public static final int TAG_SOFTWARE =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0131);
- public static final int TAG_DATE_TIME =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0132);
- public static final int TAG_ARTIST =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x013B);
- public static final int TAG_WHITE_POINT =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x013E);
- public static final int TAG_PRIMARY_CHROMATICITIES =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x013F);
- public static final int TAG_Y_CB_CR_COEFFICIENTS =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0211);
- public static final int TAG_Y_CB_CR_SUB_SAMPLING =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0212);
- public static final int TAG_Y_CB_CR_POSITIONING =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0213);
- public static final int TAG_REFERENCE_BLACK_WHITE =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0214);
- public static final int TAG_COPYRIGHT =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x8298);
- public static final int TAG_EXIF_IFD =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x8769);
- public static final int TAG_GPS_IFD =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x8825);
- // IFD 1
- public static final int TAG_JPEG_INTERCHANGE_FORMAT =
- defineTag(IfdId.TYPE_IFD_1, (short) 0x0201);
- public static final int TAG_JPEG_INTERCHANGE_FORMAT_LENGTH =
- defineTag(IfdId.TYPE_IFD_1, (short) 0x0202);
- // IFD Exif Tags
- public static final int TAG_EXPOSURE_TIME =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x829A);
- public static final int TAG_F_NUMBER =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x829D);
- public static final int TAG_EXPOSURE_PROGRAM =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x8822);
- public static final int TAG_SPECTRAL_SENSITIVITY =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x8824);
- public static final int TAG_ISO_SPEED_RATINGS =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x8827);
- public static final int TAG_OECF =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x8828);
- public static final int TAG_EXIF_VERSION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9000);
- public static final int TAG_DATE_TIME_ORIGINAL =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9003);
- public static final int TAG_DATE_TIME_DIGITIZED =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9004);
- public static final int TAG_COMPONENTS_CONFIGURATION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9101);
- public static final int TAG_COMPRESSED_BITS_PER_PIXEL =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9102);
- public static final int TAG_SHUTTER_SPEED_VALUE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9201);
- public static final int TAG_APERTURE_VALUE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9202);
- public static final int TAG_BRIGHTNESS_VALUE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9203);
- public static final int TAG_EXPOSURE_BIAS_VALUE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9204);
- public static final int TAG_MAX_APERTURE_VALUE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9205);
- public static final int TAG_SUBJECT_DISTANCE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9206);
- public static final int TAG_METERING_MODE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9207);
- public static final int TAG_LIGHT_SOURCE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9208);
- public static final int TAG_FLASH =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9209);
- public static final int TAG_FOCAL_LENGTH =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x920A);
- public static final int TAG_SUBJECT_AREA =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9214);
- public static final int TAG_MAKER_NOTE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x927C);
- public static final int TAG_USER_COMMENT =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9286);
- public static final int TAG_SUB_SEC_TIME =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9290);
- public static final int TAG_SUB_SEC_TIME_ORIGINAL =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9291);
- public static final int TAG_SUB_SEC_TIME_DIGITIZED =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9292);
- public static final int TAG_FLASHPIX_VERSION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA000);
- public static final int TAG_COLOR_SPACE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA001);
- public static final int TAG_PIXEL_X_DIMENSION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA002);
- public static final int TAG_PIXEL_Y_DIMENSION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA003);
- public static final int TAG_RELATED_SOUND_FILE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA004);
- public static final int TAG_INTEROPERABILITY_IFD =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA005);
- public static final int TAG_FLASH_ENERGY =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA20B);
- public static final int TAG_SPATIAL_FREQUENCY_RESPONSE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA20C);
- public static final int TAG_FOCAL_PLANE_X_RESOLUTION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA20E);
- public static final int TAG_FOCAL_PLANE_Y_RESOLUTION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA20F);
- public static final int TAG_FOCAL_PLANE_RESOLUTION_UNIT =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA210);
- public static final int TAG_SUBJECT_LOCATION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA214);
- public static final int TAG_EXPOSURE_INDEX =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA215);
- public static final int TAG_SENSING_METHOD =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA217);
- public static final int TAG_FILE_SOURCE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA300);
- public static final int TAG_SCENE_TYPE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA301);
- public static final int TAG_CFA_PATTERN =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA302);
- public static final int TAG_CUSTOM_RENDERED =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA401);
- public static final int TAG_EXPOSURE_MODE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA402);
- public static final int TAG_WHITE_BALANCE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA403);
- public static final int TAG_DIGITAL_ZOOM_RATIO =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA404);
- public static final int TAG_FOCAL_LENGTH_IN_35_MM_FILE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA405);
- public static final int TAG_SCENE_CAPTURE_TYPE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA406);
- public static final int TAG_GAIN_CONTROL =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA407);
- public static final int TAG_CONTRAST =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA408);
- public static final int TAG_SATURATION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA409);
- public static final int TAG_SHARPNESS =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA40A);
- public static final int TAG_DEVICE_SETTING_DESCRIPTION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA40B);
- public static final int TAG_SUBJECT_DISTANCE_RANGE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA40C);
- public static final int TAG_IMAGE_UNIQUE_ID =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA420);
- // IFD GPS tags
- public static final int TAG_GPS_VERSION_ID =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 0);
- public static final int TAG_GPS_LATITUDE_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 1);
- public static final int TAG_GPS_LATITUDE =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 2);
- public static final int TAG_GPS_LONGITUDE_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 3);
- public static final int TAG_GPS_LONGITUDE =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 4);
- public static final int TAG_GPS_ALTITUDE_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 5);
- public static final int TAG_GPS_ALTITUDE =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 6);
- public static final int TAG_GPS_TIME_STAMP =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 7);
- public static final int TAG_GPS_SATTELLITES =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 8);
- public static final int TAG_GPS_STATUS =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 9);
- public static final int TAG_GPS_MEASURE_MODE =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 10);
- public static final int TAG_GPS_DOP =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 11);
- public static final int TAG_GPS_SPEED_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 12);
- public static final int TAG_GPS_SPEED =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 13);
- public static final int TAG_GPS_TRACK_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 14);
- public static final int TAG_GPS_TRACK =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 15);
- public static final int TAG_GPS_IMG_DIRECTION_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 16);
- public static final int TAG_GPS_IMG_DIRECTION =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 17);
- public static final int TAG_GPS_MAP_DATUM =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 18);
- public static final int TAG_GPS_DEST_LATITUDE_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 19);
- public static final int TAG_GPS_DEST_LATITUDE =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 20);
- public static final int TAG_GPS_DEST_LONGITUDE_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 21);
- public static final int TAG_GPS_DEST_LONGITUDE =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 22);
- public static final int TAG_GPS_DEST_BEARING_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 23);
- public static final int TAG_GPS_DEST_BEARING =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 24);
- public static final int TAG_GPS_DEST_DISTANCE_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 25);
- public static final int TAG_GPS_DEST_DISTANCE =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 26);
- public static final int TAG_GPS_PROCESSING_METHOD =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 27);
- public static final int TAG_GPS_AREA_INFORMATION =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 28);
- public static final int TAG_GPS_DATE_STAMP =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 29);
- public static final int TAG_GPS_DIFFERENTIAL =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 30);
- // IFD Interoperability tags
- public static final int TAG_INTEROPERABILITY_INDEX =
- defineTag(IfdId.TYPE_IFD_INTEROPERABILITY, (short) 1);
-
- /**
- * Tags that contain offset markers. These are included in the banned
- * defines.
- */
- private static HashSet<Short> sOffsetTags = new HashSet<Short>();
- static {
- sOffsetTags.add(getTrueTagKey(TAG_GPS_IFD));
- sOffsetTags.add(getTrueTagKey(TAG_EXIF_IFD));
- sOffsetTags.add(getTrueTagKey(TAG_JPEG_INTERCHANGE_FORMAT));
- sOffsetTags.add(getTrueTagKey(TAG_INTEROPERABILITY_IFD));
- sOffsetTags.add(getTrueTagKey(TAG_STRIP_OFFSETS));
- }
-
- /**
- * Tags with definitions that cannot be overridden (banned defines).
- */
- protected static HashSet<Short> sBannedDefines = new HashSet<Short>(sOffsetTags);
- static {
- sBannedDefines.add(getTrueTagKey(TAG_NULL));
- sBannedDefines.add(getTrueTagKey(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH));
- sBannedDefines.add(getTrueTagKey(TAG_STRIP_BYTE_COUNTS));
- }
-
- /**
- * Returns the constant representing a tag with a given TID and default IFD.
- */
- public static int defineTag(int ifdId, short tagId) {
- return (tagId & 0x0000ffff) | (ifdId << 16);
- }
-
- /**
- * Returns the TID for a tag constant.
- */
- public static short getTrueTagKey(int tag) {
- // Truncate
- return (short) tag;
- }
-
- /**
- * Returns the default IFD for a tag constant.
- */
- public static int getTrueIfd(int tag) {
- return tag >>> 16;
- }
-
- /**
- * Constants for {@link TAG_ORIENTATION}. They can be interpreted as
- * follows:
- * <ul>
- * <li>TOP_LEFT is the normal orientation.</li>
- * <li>TOP_RIGHT is a left-right mirror.</li>
- * <li>BOTTOM_LEFT is a 180 degree rotation.</li>
- * <li>BOTTOM_RIGHT is a top-bottom mirror.</li>
- * <li>LEFT_TOP is mirrored about the top-left<->bottom-right axis.</li>
- * <li>RIGHT_TOP is a 90 degree clockwise rotation.</li>
- * <li>LEFT_BOTTOM is mirrored about the top-right<->bottom-left axis.</li>
- * <li>RIGHT_BOTTOM is a 270 degree clockwise rotation.</li>
- * </ul>
- */
- public static interface Orientation {
- public static final short TOP_LEFT = 1;
- public static final short TOP_RIGHT = 2;
- public static final short BOTTOM_LEFT = 3;
- public static final short BOTTOM_RIGHT = 4;
- public static final short LEFT_TOP = 5;
- public static final short RIGHT_TOP = 6;
- public static final short LEFT_BOTTOM = 7;
- public static final short RIGHT_BOTTOM = 8;
- }
-
- /**
- * Constants for {@link TAG_Y_CB_CR_POSITIONING}
- */
- public static interface YCbCrPositioning {
- public static final short CENTERED = 1;
- public static final short CO_SITED = 2;
- }
-
- /**
- * Constants for {@link TAG_COMPRESSION}
- */
- public static interface Compression {
- public static final short UNCOMPRESSION = 1;
- public static final short JPEG = 6;
- }
-
- /**
- * Constants for {@link TAG_RESOLUTION_UNIT}
- */
- public static interface ResolutionUnit {
- public static final short INCHES = 2;
- public static final short CENTIMETERS = 3;
- }
-
- /**
- * Constants for {@link TAG_PHOTOMETRIC_INTERPRETATION}
- */
- public static interface PhotometricInterpretation {
- public static final short RGB = 2;
- public static final short YCBCR = 6;
- }
-
- /**
- * Constants for {@link TAG_PLANAR_CONFIGURATION}
- */
- public static interface PlanarConfiguration {
- public static final short CHUNKY = 1;
- public static final short PLANAR = 2;
- }
-
- /**
- * Constants for {@link TAG_EXPOSURE_PROGRAM}
- */
- public static interface ExposureProgram {
- public static final short NOT_DEFINED = 0;
- public static final short MANUAL = 1;
- public static final short NORMAL_PROGRAM = 2;
- public static final short APERTURE_PRIORITY = 3;
- public static final short SHUTTER_PRIORITY = 4;
- public static final short CREATIVE_PROGRAM = 5;
- public static final short ACTION_PROGRAM = 6;
- public static final short PROTRAIT_MODE = 7;
- public static final short LANDSCAPE_MODE = 8;
- }
-
- /**
- * Constants for {@link TAG_METERING_MODE}
- */
- public static interface MeteringMode {
- public static final short UNKNOWN = 0;
- public static final short AVERAGE = 1;
- public static final short CENTER_WEIGHTED_AVERAGE = 2;
- public static final short SPOT = 3;
- public static final short MULTISPOT = 4;
- public static final short PATTERN = 5;
- public static final short PARTAIL = 6;
- public static final short OTHER = 255;
- }
-
- /**
- * Constants for {@link TAG_FLASH} As the definition in Jeita EXIF 2.2
- * standard, we can treat this constant as bitwise flag.
- * <p>
- * e.g.
- * <p>
- * short flash = FIRED | RETURN_STROBE_RETURN_LIGHT_DETECTED |
- * MODE_AUTO_MODE
- */
- public static interface Flash {
- // LSB
- public static final short DID_NOT_FIRED = 0;
- public static final short FIRED = 1;
- // 1st~2nd bits
- public static final short RETURN_NO_STROBE_RETURN_DETECTION_FUNCTION = 0 << 1;
- public static final short RETURN_STROBE_RETURN_LIGHT_NOT_DETECTED = 2 << 1;
- public static final short RETURN_STROBE_RETURN_LIGHT_DETECTED = 3 << 1;
- // 3rd~4th bits
- public static final short MODE_UNKNOWN = 0 << 3;
- public static final short MODE_COMPULSORY_FLASH_FIRING = 1 << 3;
- public static final short MODE_COMPULSORY_FLASH_SUPPRESSION = 2 << 3;
- public static final short MODE_AUTO_MODE = 3 << 3;
- // 5th bit
- public static final short FUNCTION_PRESENT = 0 << 5;
- public static final short FUNCTION_NO_FUNCTION = 1 << 5;
- // 6th bit
- public static final short RED_EYE_REDUCTION_NO_OR_UNKNOWN = 0 << 6;
- public static final short RED_EYE_REDUCTION_SUPPORT = 1 << 6;
- }
-
- /**
- * Constants for {@link TAG_COLOR_SPACE}
- */
- public static interface ColorSpace {
- public static final short SRGB = 1;
- public static final short UNCALIBRATED = (short) 0xFFFF;
- }
-
- /**
- * Constants for {@link TAG_EXPOSURE_MODE}
- */
- public static interface ExposureMode {
- public static final short AUTO_EXPOSURE = 0;
- public static final short MANUAL_EXPOSURE = 1;
- public static final short AUTO_BRACKET = 2;
- }
-
- /**
- * Constants for {@link TAG_WHITE_BALANCE}
- */
- public static interface WhiteBalance {
- public static final short AUTO = 0;
- public static final short MANUAL = 1;
- }
-
- /**
- * Constants for {@link TAG_SCENE_CAPTURE_TYPE}
- */
- public static interface SceneCapture {
- public static final short STANDARD = 0;
- public static final short LANDSCAPE = 1;
- public static final short PROTRAIT = 2;
- public static final short NIGHT_SCENE = 3;
- }
-
- /**
- * Constants for {@link TAG_COMPONENTS_CONFIGURATION}
- */
- public static interface ComponentsConfiguration {
- public static final short NOT_EXIST = 0;
- public static final short Y = 1;
- public static final short CB = 2;
- public static final short CR = 3;
- public static final short R = 4;
- public static final short G = 5;
- public static final short B = 6;
- }
-
- /**
- * Constants for {@link TAG_LIGHT_SOURCE}
- */
- public static interface LightSource {
- public static final short UNKNOWN = 0;
- public static final short DAYLIGHT = 1;
- public static final short FLUORESCENT = 2;
- public static final short TUNGSTEN = 3;
- public static final short FLASH = 4;
- public static final short FINE_WEATHER = 9;
- public static final short CLOUDY_WEATHER = 10;
- public static final short SHADE = 11;
- public static final short DAYLIGHT_FLUORESCENT = 12;
- public static final short DAY_WHITE_FLUORESCENT = 13;
- public static final short COOL_WHITE_FLUORESCENT = 14;
- public static final short WHITE_FLUORESCENT = 15;
- public static final short STANDARD_LIGHT_A = 17;
- public static final short STANDARD_LIGHT_B = 18;
- public static final short STANDARD_LIGHT_C = 19;
- public static final short D55 = 20;
- public static final short D65 = 21;
- public static final short D75 = 22;
- public static final short D50 = 23;
- public static final short ISO_STUDIO_TUNGSTEN = 24;
- public static final short OTHER = 255;
- }
-
- /**
- * Constants for {@link TAG_SENSING_METHOD}
- */
- public static interface SensingMethod {
- public static final short NOT_DEFINED = 1;
- public static final short ONE_CHIP_COLOR = 2;
- public static final short TWO_CHIP_COLOR = 3;
- public static final short THREE_CHIP_COLOR = 4;
- public static final short COLOR_SEQUENTIAL_AREA = 5;
- public static final short TRILINEAR = 7;
- public static final short COLOR_SEQUENTIAL_LINEAR = 8;
- }
-
- /**
- * Constants for {@link TAG_FILE_SOURCE}
- */
- public static interface FileSource {
- public static final short DSC = 3;
- }
-
- /**
- * Constants for {@link TAG_SCENE_TYPE}
- */
- public static interface SceneType {
- public static final short DIRECT_PHOTOGRAPHED = 1;
- }
-
- /**
- * Constants for {@link TAG_GAIN_CONTROL}
- */
- public static interface GainControl {
- public static final short NONE = 0;
- public static final short LOW_UP = 1;
- public static final short HIGH_UP = 2;
- public static final short LOW_DOWN = 3;
- public static final short HIGH_DOWN = 4;
- }
-
- /**
- * Constants for {@link TAG_CONTRAST}
- */
- public static interface Contrast {
- public static final short NORMAL = 0;
- public static final short SOFT = 1;
- public static final short HARD = 2;
- }
-
- /**
- * Constants for {@link TAG_SATURATION}
- */
- public static interface Saturation {
- public static final short NORMAL = 0;
- public static final short LOW = 1;
- public static final short HIGH = 2;
- }
-
- /**
- * Constants for {@link TAG_SHARPNESS}
- */
- public static interface Sharpness {
- public static final short NORMAL = 0;
- public static final short SOFT = 1;
- public static final short HARD = 2;
- }
-
- /**
- * Constants for {@link TAG_SUBJECT_DISTANCE}
- */
- public static interface SubjectDistance {
- public static final short UNKNOWN = 0;
- public static final short MACRO = 1;
- public static final short CLOSE_VIEW = 2;
- public static final short DISTANT_VIEW = 3;
- }
-
- /**
- * Constants for {@link TAG_GPS_LATITUDE_REF},
- * {@link TAG_GPS_DEST_LATITUDE_REF}
- */
- public static interface GpsLatitudeRef {
- public static final String NORTH = "N";
- public static final String SOUTH = "S";
- }
-
- /**
- * Constants for {@link TAG_GPS_LONGITUDE_REF},
- * {@link TAG_GPS_DEST_LONGITUDE_REF}
- */
- public static interface GpsLongitudeRef {
- public static final String EAST = "E";
- public static final String WEST = "W";
- }
-
- /**
- * Constants for {@link TAG_GPS_ALTITUDE_REF}
- */
- public static interface GpsAltitudeRef {
- public static final short SEA_LEVEL = 0;
- public static final short SEA_LEVEL_NEGATIVE = 1;
- }
-
- /**
- * Constants for {@link TAG_GPS_STATUS}
- */
- public static interface GpsStatus {
- public static final String IN_PROGRESS = "A";
- public static final String INTEROPERABILITY = "V";
- }
-
- /**
- * Constants for {@link TAG_GPS_MEASURE_MODE}
- */
- public static interface GpsMeasureMode {
- public static final String MODE_2_DIMENSIONAL = "2";
- public static final String MODE_3_DIMENSIONAL = "3";
- }
-
- /**
- * Constants for {@link TAG_GPS_SPEED_REF},
- * {@link TAG_GPS_DEST_DISTANCE_REF}
- */
- public static interface GpsSpeedRef {
- public static final String KILOMETERS = "K";
- public static final String MILES = "M";
- public static final String KNOTS = "N";
- }
-
- /**
- * Constants for {@link TAG_GPS_TRACK_REF},
- * {@link TAG_GPS_IMG_DIRECTION_REF}, {@link TAG_GPS_DEST_BEARING_REF}
- */
- public static interface GpsTrackRef {
- public static final String TRUE_DIRECTION = "T";
- public static final String MAGNETIC_DIRECTION = "M";
- }
-
- /**
- * Constants for {@link TAG_GPS_DIFFERENTIAL}
- */
- public static interface GpsDifferential {
- public static final short WITHOUT_DIFFERENTIAL_CORRECTION = 0;
- public static final short DIFFERENTIAL_CORRECTION_APPLIED = 1;
- }
-
- private static final String NULL_ARGUMENT_STRING = "Argument is null";
- private ExifData mData = new ExifData(DEFAULT_BYTE_ORDER);
- public static final ByteOrder DEFAULT_BYTE_ORDER = ByteOrder.BIG_ENDIAN;
-
- public ExifInterface() {
- mGPSDateStampFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- }
-
- /**
- * Reads the exif tags from a byte array, clearing this ExifInterface
- * object's existing exif tags.
- *
- * @param jpeg a byte array containing a jpeg compressed image.
- * @throws IOException
- */
- public void readExif(byte[] jpeg) throws IOException {
- readExif(new ByteArrayInputStream(jpeg));
- }
-
- /**
- * Reads the exif tags from an InputStream, clearing this ExifInterface
- * object's existing exif tags.
- *
- * @param inStream an InputStream containing a jpeg compressed image.
- * @throws IOException
- */
- public void readExif(InputStream inStream) throws IOException {
- if (inStream == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- ExifData d = null;
- try {
- d = new ExifReader(this).read(inStream);
- } catch (ExifInvalidFormatException e) {
- throw new IOException("Invalid exif format : " + e);
- }
- mData = d;
- }
-
- /**
- * Reads the exif tags from a file, clearing this ExifInterface object's
- * existing exif tags.
- *
- * @param inFileName a string representing the filepath to jpeg file.
- * @throws FileNotFoundException
- * @throws IOException
- */
- public void readExif(String inFileName) throws FileNotFoundException, IOException {
- if (inFileName == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- InputStream is = null;
- try {
- is = (InputStream) new BufferedInputStream(new FileInputStream(inFileName));
- readExif(is);
- } catch (IOException e) {
- closeSilently(is);
- throw e;
- }
- is.close();
- }
-
- /**
- * Sets the exif tags, clearing this ExifInterface object's existing exif
- * tags.
- *
- * @param tags a collection of exif tags to set.
- */
- public void setExif(Collection<ExifTag> tags) {
- clearExif();
- setTags(tags);
- }
-
- /**
- * Clears this ExifInterface object's existing exif tags.
- */
- public void clearExif() {
- mData = new ExifData(DEFAULT_BYTE_ORDER);
- }
-
- /**
- * Writes the tags from this ExifInterface object into a jpeg image,
- * removing prior exif tags.
- *
- * @param jpeg a byte array containing a jpeg compressed image.
- * @param exifOutStream an OutputStream to which the jpeg image with added
- * exif tags will be written.
- * @throws IOException
- */
- public void writeExif(byte[] jpeg, OutputStream exifOutStream) throws IOException {
- if (jpeg == null || exifOutStream == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- OutputStream s = getExifWriterStream(exifOutStream);
- s.write(jpeg, 0, jpeg.length);
- s.flush();
- }
-
- /**
- * Writes the tags from this ExifInterface object into a jpeg compressed
- * bitmap, removing prior exif tags.
- *
- * @param bmap a bitmap to compress and write exif into.
- * @param exifOutStream the OutputStream to which the jpeg image with added
- * exif tags will be written.
- * @throws IOException
- */
- public void writeExif(Bitmap bmap, OutputStream exifOutStream) throws IOException {
- if (bmap == null || exifOutStream == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- OutputStream s = getExifWriterStream(exifOutStream);
- bmap.compress(Bitmap.CompressFormat.JPEG, 90, s);
- s.flush();
- }
-
- /**
- * Writes the tags from this ExifInterface object into a jpeg stream,
- * removing prior exif tags.
- *
- * @param jpegStream an InputStream containing a jpeg compressed image.
- * @param exifOutStream an OutputStream to which the jpeg image with added
- * exif tags will be written.
- * @throws IOException
- */
- public void writeExif(InputStream jpegStream, OutputStream exifOutStream) throws IOException {
- if (jpegStream == null || exifOutStream == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- OutputStream s = getExifWriterStream(exifOutStream);
- doExifStreamIO(jpegStream, s);
- s.flush();
- }
-
- /**
- * Writes the tags from this ExifInterface object into a jpeg image,
- * removing prior exif tags.
- *
- * @param jpeg a byte array containing a jpeg compressed image.
- * @param exifOutFileName a String containing the filepath to which the jpeg
- * image with added exif tags will be written.
- * @throws FileNotFoundException
- * @throws IOException
- */
- public void writeExif(byte[] jpeg, String exifOutFileName) throws FileNotFoundException,
- IOException {
- if (jpeg == null || exifOutFileName == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- OutputStream s = null;
- try {
- s = getExifWriterStream(exifOutFileName);
- s.write(jpeg, 0, jpeg.length);
- s.flush();
- } catch (IOException e) {
- closeSilently(s);
- throw e;
- }
- s.close();
- }
-
- /**
- * Writes the tags from this ExifInterface object into a jpeg compressed
- * bitmap, removing prior exif tags.
- *
- * @param bmap a bitmap to compress and write exif into.
- * @param exifOutFileName a String containing the filepath to which the jpeg
- * image with added exif tags will be written.
- * @throws FileNotFoundException
- * @throws IOException
- */
- public void writeExif(Bitmap bmap, String exifOutFileName) throws FileNotFoundException,
- IOException {
- if (bmap == null || exifOutFileName == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- OutputStream s = null;
- try {
- s = getExifWriterStream(exifOutFileName);
- bmap.compress(Bitmap.CompressFormat.JPEG, 90, s);
- s.flush();
- } catch (IOException e) {
- closeSilently(s);
- throw e;
- }
- s.close();
- }
-
- /**
- * Writes the tags from this ExifInterface object into a jpeg stream,
- * removing prior exif tags.
- *
- * @param jpegStream an InputStream containing a jpeg compressed image.
- * @param exifOutFileName a String containing the filepath to which the jpeg
- * image with added exif tags will be written.
- * @throws FileNotFoundException
- * @throws IOException
- */
- public void writeExif(InputStream jpegStream, String exifOutFileName)
- throws FileNotFoundException, IOException {
- if (jpegStream == null || exifOutFileName == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- OutputStream s = null;
- try {
- s = getExifWriterStream(exifOutFileName);
- doExifStreamIO(jpegStream, s);
- s.flush();
- } catch (IOException e) {
- closeSilently(s);
- throw e;
- }
- s.close();
- }
-
- /**
- * Writes the tags from this ExifInterface object into a jpeg file, removing
- * prior exif tags.
- *
- * @param jpegFileName a String containing the filepath for a jpeg file.
- * @param exifOutFileName a String containing the filepath to which the jpeg
- * image with added exif tags will be written.
- * @throws FileNotFoundException
- * @throws IOException
- */
- public void writeExif(String jpegFileName, String exifOutFileName)
- throws FileNotFoundException, IOException {
- if (jpegFileName == null || exifOutFileName == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- InputStream is = null;
- try {
- is = new FileInputStream(jpegFileName);
- writeExif(is, exifOutFileName);
- } catch (IOException e) {
- closeSilently(is);
- throw e;
- }
- is.close();
- }
-
- /**
- * Wraps an OutputStream object with an ExifOutputStream. Exif tags in this
- * ExifInterface object will be added to a jpeg image written to this
- * stream, removing prior exif tags. Other methods of this ExifInterface
- * object should not be called until the returned OutputStream has been
- * closed.
- *
- * @param outStream an OutputStream to wrap.
- * @return an OutputStream that wraps the outStream parameter, and adds exif
- * metadata. A jpeg image should be written to this stream.
- */
- public OutputStream getExifWriterStream(OutputStream outStream) {
- if (outStream == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- ExifOutputStream eos = new ExifOutputStream(outStream, this);
- eos.setExifData(mData);
- return eos;
- }
-
- /**
- * Returns an OutputStream object that writes to a file. Exif tags in this
- * ExifInterface object will be added to a jpeg image written to this
- * stream, removing prior exif tags. Other methods of this ExifInterface
- * object should not be called until the returned OutputStream has been
- * closed.
- *
- * @param exifOutFileName an String containing a filepath for a jpeg file.
- * @return an OutputStream that writes to the exifOutFileName file, and adds
- * exif metadata. A jpeg image should be written to this stream.
- * @throws FileNotFoundException
- */
- public OutputStream getExifWriterStream(String exifOutFileName) throws FileNotFoundException {
- if (exifOutFileName == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- OutputStream out = null;
- try {
- out = (OutputStream) new FileOutputStream(exifOutFileName);
- } catch (FileNotFoundException e) {
- closeSilently(out);
- throw e;
- }
- return getExifWriterStream(out);
- }
-
- /**
- * Attempts to do an in-place rewrite the exif metadata in a file for the
- * given tags. If tags do not exist or do not have the same size as the
- * existing exif tags, this method will fail.
- *
- * @param filename a String containing a filepath for a jpeg file with exif
- * tags to rewrite.
- * @param tags tags that will be written into the jpeg file over existing
- * tags if possible.
- * @return true if success, false if could not overwrite. If false, no
- * changes are made to the file.
- * @throws FileNotFoundException
- * @throws IOException
- */
- public boolean rewriteExif(String filename, Collection<ExifTag> tags)
- throws FileNotFoundException, IOException {
- RandomAccessFile file = null;
- InputStream is = null;
- boolean ret;
- try {
- File temp = new File(filename);
- is = new BufferedInputStream(new FileInputStream(temp));
-
- // Parse beginning of APP1 in exif to find size of exif header.
- ExifParser parser = null;
- try {
- parser = ExifParser.parse(is, this);
- } catch (ExifInvalidFormatException e) {
- throw new IOException("Invalid exif format : ", e);
- }
- long exifSize = parser.getOffsetToExifEndFromSOF();
-
- // Free up resources
- is.close();
- is = null;
-
- // Open file for memory mapping.
- file = new RandomAccessFile(temp, "rw");
- long fileLength = file.length();
- if (fileLength < exifSize) {
- throw new IOException("Filesize changed during operation");
- }
-
- // Map only exif header into memory.
- ByteBuffer buf = file.getChannel().map(MapMode.READ_WRITE, 0, exifSize);
-
- // Attempt to overwrite tag values without changing lengths (avoids
- // file copy).
- ret = rewriteExif(buf, tags);
- } catch (IOException e) {
- closeSilently(file);
- throw e;
- } finally {
- closeSilently(is);
- }
- file.close();
- return ret;
- }
-
- /**
- * Attempts to do an in-place rewrite the exif metadata in a ByteBuffer for
- * the given tags. If tags do not exist or do not have the same size as the
- * existing exif tags, this method will fail.
- *
- * @param buf a ByteBuffer containing a jpeg file with existing exif tags to
- * rewrite.
- * @param tags tags that will be written into the jpeg ByteBuffer over
- * existing tags if possible.
- * @return true if success, false if could not overwrite. If false, no
- * changes are made to the ByteBuffer.
- * @throws IOException
- */
- public boolean rewriteExif(ByteBuffer buf, Collection<ExifTag> tags) throws IOException {
- ExifModifier mod = null;
- try {
- mod = new ExifModifier(buf, this);
- for (ExifTag t : tags) {
- mod.modifyTag(t);
- }
- return mod.commit();
- } catch (ExifInvalidFormatException e) {
- throw new IOException("Invalid exif format : " + e);
- }
- }
-
- /**
- * Attempts to do an in-place rewrite of the exif metadata. If this fails,
- * fall back to overwriting file. This preserves tags that are not being
- * rewritten.
- *
- * @param filename a String containing a filepath for a jpeg file.
- * @param tags tags that will be written into the jpeg file over existing
- * tags if possible.
- * @throws FileNotFoundException
- * @throws IOException
- * @see #rewriteExif
- */
- public void forceRewriteExif(String filename, Collection<ExifTag> tags)
- throws FileNotFoundException,
- IOException {
- // Attempt in-place write
- if (!rewriteExif(filename, tags)) {
- // Fall back to doing a copy
- ExifData tempData = mData;
- mData = new ExifData(DEFAULT_BYTE_ORDER);
- FileInputStream is = null;
- ByteArrayOutputStream bytes = null;
- try {
- is = new FileInputStream(filename);
- bytes = new ByteArrayOutputStream();
- doExifStreamIO(is, bytes);
- byte[] imageBytes = bytes.toByteArray();
- readExif(imageBytes);
- setTags(tags);
- writeExif(imageBytes, filename);
- } catch (IOException e) {
- closeSilently(is);
- throw e;
- } finally {
- is.close();
- // Prevent clobbering of mData
- mData = tempData;
- }
- }
- }
-
- /**
- * Attempts to do an in-place rewrite of the exif metadata using the tags in
- * this ExifInterface object. If this fails, fall back to overwriting file.
- * This preserves tags that are not being rewritten.
- *
- * @param filename a String containing a filepath for a jpeg file.
- * @throws FileNotFoundException
- * @throws IOException
- * @see #rewriteExif
- */
- public void forceRewriteExif(String filename) throws FileNotFoundException, IOException {
- forceRewriteExif(filename, getAllTags());
- }
-
- /**
- * Get the exif tags in this ExifInterface object or null if none exist.
- *
- * @return a List of {@link ExifTag}s.
- */
- public List<ExifTag> getAllTags() {
- return mData.getAllTags();
- }
-
- /**
- * Returns a list of ExifTags that share a TID (which can be obtained by
- * calling {@link #getTrueTagKey} on a defined tag constant) or null if none
- * exist.
- *
- * @param tagId a TID as defined in the exif standard (or with
- * {@link #defineTag}).
- * @return a List of {@link ExifTag}s.
- */
- public List<ExifTag> getTagsForTagId(short tagId) {
- return mData.getAllTagsForTagId(tagId);
- }
-
- /**
- * Returns a list of ExifTags that share an IFD (which can be obtained by
- * calling {@link #getTrueIFD} on a defined tag constant) or null if none
- * exist.
- *
- * @param ifdId an IFD as defined in the exif standard (or with
- * {@link #defineTag}).
- * @return a List of {@link ExifTag}s.
- */
- public List<ExifTag> getTagsForIfdId(int ifdId) {
- return mData.getAllTagsForIfd(ifdId);
- }
-
- /**
- * Gets an ExifTag for an IFD other than the tag's default.
- *
- * @see #getTag
- */
- public ExifTag getTag(int tagId, int ifdId) {
- if (!ExifTag.isValidIfd(ifdId)) {
- return null;
- }
- return mData.getTag(getTrueTagKey(tagId), ifdId);
- }
-
- /**
- * Returns the ExifTag in that tag's default IFD for a defined tag constant
- * or null if none exists.
- *
- * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @return an {@link ExifTag} or null if none exists.
- */
- public ExifTag getTag(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTag(tagId, ifdId);
- }
-
- /**
- * Gets a tag value for an IFD other than the tag's default.
- *
- * @see #getTagValue
- */
- public Object getTagValue(int tagId, int ifdId) {
- ExifTag t = getTag(tagId, ifdId);
- return (t == null) ? null : t.getValue();
- }
-
- /**
- * Returns the value of the ExifTag in that tag's default IFD for a defined
- * tag constant or null if none exists or the value could not be cast into
- * the return type.
- *
- * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @return the value of the ExifTag or null if none exists.
- */
- public Object getTagValue(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagValue(tagId, ifdId);
- }
-
- /*
- * Getter methods that are similar to getTagValue. Null is returned if the
- * tag value cannot be cast into the return type.
- */
-
- /**
- * @see #getTagValue
- */
- public String getTagStringValue(int tagId, int ifdId) {
- ExifTag t = getTag(tagId, ifdId);
- if (t == null) {
- return null;
- }
- return t.getValueAsString();
- }
-
- /**
- * @see #getTagValue
- */
- public String getTagStringValue(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagStringValue(tagId, ifdId);
- }
-
- /**
- * @see #getTagValue
- */
- public Long getTagLongValue(int tagId, int ifdId) {
- long[] l = getTagLongValues(tagId, ifdId);
- if (l == null || l.length <= 0) {
- return null;
- }
- return Long.valueOf(l[0]);
- }
-
- /**
- * @see #getTagValue
- */
- public Long getTagLongValue(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagLongValue(tagId, ifdId);
- }
-
- /**
- * @see #getTagValue
- */
- public Integer getTagIntValue(int tagId, int ifdId) {
- int[] l = getTagIntValues(tagId, ifdId);
- if (l == null || l.length <= 0) {
- return null;
- }
- return Integer.valueOf(l[0]);
- }
-
- /**
- * @see #getTagValue
- */
- public Integer getTagIntValue(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagIntValue(tagId, ifdId);
- }
-
- /**
- * @see #getTagValue
- */
- public Byte getTagByteValue(int tagId, int ifdId) {
- byte[] l = getTagByteValues(tagId, ifdId);
- if (l == null || l.length <= 0) {
- return null;
- }
- return Byte.valueOf(l[0]);
- }
-
- /**
- * @see #getTagValue
- */
- public Byte getTagByteValue(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagByteValue(tagId, ifdId);
- }
-
- /**
- * @see #getTagValue
- */
- public Rational getTagRationalValue(int tagId, int ifdId) {
- Rational[] l = getTagRationalValues(tagId, ifdId);
- if (l == null || l.length == 0) {
- return null;
- }
- return new Rational(l[0]);
- }
-
- /**
- * @see #getTagValue
- */
- public Rational getTagRationalValue(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagRationalValue(tagId, ifdId);
- }
-
- /**
- * @see #getTagValue
- */
- public long[] getTagLongValues(int tagId, int ifdId) {
- ExifTag t = getTag(tagId, ifdId);
- if (t == null) {
- return null;
- }
- return t.getValueAsLongs();
- }
-
- /**
- * @see #getTagValue
- */
- public long[] getTagLongValues(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagLongValues(tagId, ifdId);
- }
-
- /**
- * @see #getTagValue
- */
- public int[] getTagIntValues(int tagId, int ifdId) {
- ExifTag t = getTag(tagId, ifdId);
- if (t == null) {
- return null;
- }
- return t.getValueAsInts();
- }
-
- /**
- * @see #getTagValue
- */
- public int[] getTagIntValues(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagIntValues(tagId, ifdId);
- }
-
- /**
- * @see #getTagValue
- */
- public byte[] getTagByteValues(int tagId, int ifdId) {
- ExifTag t = getTag(tagId, ifdId);
- if (t == null) {
- return null;
- }
- return t.getValueAsBytes();
- }
-
- /**
- * @see #getTagValue
- */
- public byte[] getTagByteValues(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagByteValues(tagId, ifdId);
- }
-
- /**
- * @see #getTagValue
- */
- public Rational[] getTagRationalValues(int tagId, int ifdId) {
- ExifTag t = getTag(tagId, ifdId);
- if (t == null) {
- return null;
- }
- return t.getValueAsRationals();
- }
-
- /**
- * @see #getTagValue
- */
- public Rational[] getTagRationalValues(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagRationalValues(tagId, ifdId);
- }
-
- /**
- * Checks whether a tag has a defined number of elements.
- *
- * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @return true if the tag has a defined number of elements.
- */
- public boolean isTagCountDefined(int tagId) {
- int info = getTagInfo().get(tagId);
- // No value in info can be zero, as all tags have a non-zero type
- if (info == 0) {
- return false;
- }
- return getComponentCountFromInfo(info) != ExifTag.SIZE_UNDEFINED;
- }
-
- /**
- * Gets the defined number of elements for a tag.
- *
- * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @return the number of elements or {@link ExifTag#SIZE_UNDEFINED} if the
- * tag or the number of elements is not defined.
- */
- public int getDefinedTagCount(int tagId) {
- int info = getTagInfo().get(tagId);
- if (info == 0) {
- return ExifTag.SIZE_UNDEFINED;
- }
- return getComponentCountFromInfo(info);
- }
-
- /**
- * Gets the number of elements for an ExifTag in a given IFD.
- *
- * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @param ifdId the IFD containing the ExifTag to check.
- * @return the number of elements in the ExifTag, if the tag's size is
- * undefined this will return the actual number of elements that is
- * in the ExifTag's value.
- */
- public int getActualTagCount(int tagId, int ifdId) {
- ExifTag t = getTag(tagId, ifdId);
- if (t == null) {
- return 0;
- }
- return t.getComponentCount();
- }
-
- /**
- * Gets the default IFD for a tag.
- *
- * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @return the default IFD for a tag definition or {@link #IFD_NULL} if no
- * definition exists.
- */
- public int getDefinedTagDefaultIfd(int tagId) {
- int info = getTagInfo().get(tagId);
- if (info == DEFINITION_NULL) {
- return IFD_NULL;
- }
- return getTrueIfd(tagId);
- }
-
- /**
- * Gets the defined type for a tag.
- *
- * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @return the type.
- * @see ExifTag#getDataType()
- */
- public short getDefinedTagType(int tagId) {
- int info = getTagInfo().get(tagId);
- if (info == 0) {
- return -1;
- }
- return getTypeFromInfo(info);
- }
-
- /**
- * Returns true if tag TID is one of the following: {@link TAG_EXIF_IFD},
- * {@link TAG_GPS_IFD}, {@link TAG_JPEG_INTERCHANGE_FORMAT},
- * {@link TAG_STRIP_OFFSETS}, {@link TAG_INTEROPERABILITY_IFD}
- * <p>
- * Note: defining tags with these TID's is disallowed.
- *
- * @param tag a tag's TID (can be obtained from a defined tag constant with
- * {@link #getTrueTagKey}).
- * @return true if the TID is that of an offset tag.
- */
- protected static boolean isOffsetTag(short tag) {
- return sOffsetTags.contains(tag);
- }
-
- /**
- * Creates a tag for a defined tag constant in a given IFD if that IFD is
- * allowed for the tag. This method will fail anytime the appropriate
- * {@link ExifTag#setValue} for this tag's datatype would fail.
- *
- * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @param ifdId the IFD that the tag should be in.
- * @param val the value of the tag to set.
- * @return an ExifTag object or null if one could not be constructed.
- * @see #buildTag
- */
- public ExifTag buildTag(int tagId, int ifdId, Object val) {
- int info = getTagInfo().get(tagId);
- if (info == 0 || val == null) {
- return null;
- }
- short type = getTypeFromInfo(info);
- int definedCount = getComponentCountFromInfo(info);
- boolean hasDefinedCount = (definedCount != ExifTag.SIZE_UNDEFINED);
- if (!ExifInterface.isIfdAllowed(info, ifdId)) {
- return null;
- }
- ExifTag t = new ExifTag(getTrueTagKey(tagId), type, definedCount, ifdId, hasDefinedCount);
- if (!t.setValue(val)) {
- return null;
- }
- return t;
- }
-
- /**
- * Creates a tag for a defined tag constant in the tag's default IFD.
- *
- * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @param val the tag's value.
- * @return an ExifTag object.
- */
- public ExifTag buildTag(int tagId, Object val) {
- int ifdId = getTrueIfd(tagId);
- return buildTag(tagId, ifdId, val);
- }
-
- protected ExifTag buildUninitializedTag(int tagId) {
- int info = getTagInfo().get(tagId);
- if (info == 0) {
- return null;
- }
- short type = getTypeFromInfo(info);
- int definedCount = getComponentCountFromInfo(info);
- boolean hasDefinedCount = (definedCount != ExifTag.SIZE_UNDEFINED);
- int ifdId = getTrueIfd(tagId);
- ExifTag t = new ExifTag(getTrueTagKey(tagId), type, definedCount, ifdId, hasDefinedCount);
- return t;
- }
-
- /**
- * Sets the value of an ExifTag if it exists in the given IFD. The value
- * must be the correct type and length for that ExifTag.
- *
- * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @param ifdId the IFD that the ExifTag is in.
- * @param val the value to set.
- * @return true if success, false if the ExifTag doesn't exist or the value
- * is the wrong type/length.
- * @see #setTagValue
- */
- public boolean setTagValue(int tagId, int ifdId, Object val) {
- ExifTag t = getTag(tagId, ifdId);
- if (t == null) {
- return false;
- }
- return t.setValue(val);
- }
-
- /**
- * Sets the value of an ExifTag if it exists it's default IFD. The value
- * must be the correct type and length for that ExifTag.
- *
- * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @param val the value to set.
- * @return true if success, false if the ExifTag doesn't exist or the value
- * is the wrong type/length.
- */
- public boolean setTagValue(int tagId, Object val) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return setTagValue(tagId, ifdId, val);
- }
-
- /**
- * Puts an ExifTag into this ExifInterface object's tags, removing a
- * previous ExifTag with the same TID and IFD. The IFD it is put into will
- * be the one the tag was created with in {@link #buildTag}.
- *
- * @param tag an ExifTag to put into this ExifInterface's tags.
- * @return the previous ExifTag with the same TID and IFD or null if none
- * exists.
- */
- public ExifTag setTag(ExifTag tag) {
- return mData.addTag(tag);
- }
-
- /**
- * Puts a collection of ExifTags into this ExifInterface objects's tags. Any
- * previous ExifTags with the same TID and IFDs will be removed.
- *
- * @param tags a Collection of ExifTags.
- * @see #setTag
- */
- public void setTags(Collection<ExifTag> tags) {
- for (ExifTag t : tags) {
- setTag(t);
- }
- }
-
- /**
- * Removes the ExifTag for a tag constant from the given IFD.
- *
- * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @param ifdId the IFD of the ExifTag to remove.
- */
- public void deleteTag(int tagId, int ifdId) {
- mData.removeTag(getTrueTagKey(tagId), ifdId);
- }
-
- /**
- * Removes the ExifTag for a tag constant from that tag's default IFD.
- *
- * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- */
- public void deleteTag(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- deleteTag(tagId, ifdId);
- }
-
- /**
- * Creates a new tag definition in this ExifInterface object for a given TID
- * and default IFD. Creating a definition with the same TID and default IFD
- * as a previous definition will override it.
- *
- * @param tagId the TID for the tag.
- * @param defaultIfd the default IFD for the tag.
- * @param tagType the type of the tag (see {@link ExifTag#getDataType()}).
- * @param defaultComponentCount the number of elements of this tag's type in
- * the tags value.
- * @param allowedIfds the IFD's this tag is allowed to be put in.
- * @return the defined tag constant (e.g. {@link #TAG_IMAGE_WIDTH}) or
- * {@link #TAG_NULL} if the definition could not be made.
- */
- public int setTagDefinition(short tagId, int defaultIfd, short tagType,
- short defaultComponentCount, int[] allowedIfds) {
- if (sBannedDefines.contains(tagId)) {
- return TAG_NULL;
- }
- if (ExifTag.isValidType(tagType) && ExifTag.isValidIfd(defaultIfd)) {
- int tagDef = defineTag(defaultIfd, tagId);
- if (tagDef == TAG_NULL) {
- return TAG_NULL;
- }
- int[] otherDefs = getTagDefinitionsForTagId(tagId);
- SparseIntArray infos = getTagInfo();
- // Make sure defaultIfd is in allowedIfds
- boolean defaultCheck = false;
- for (int i : allowedIfds) {
- if (defaultIfd == i) {
- defaultCheck = true;
- }
- if (!ExifTag.isValidIfd(i)) {
- return TAG_NULL;
- }
- }
- if (!defaultCheck) {
- return TAG_NULL;
- }
-
- int ifdFlags = getFlagsFromAllowedIfds(allowedIfds);
- // Make sure no identical tags can exist in allowedIfds
- if (otherDefs != null) {
- for (int def : otherDefs) {
- int tagInfo = infos.get(def);
- int allowedFlags = getAllowedIfdFlagsFromInfo(tagInfo);
- if ((ifdFlags & allowedFlags) != 0) {
- return TAG_NULL;
- }
- }
- }
- getTagInfo().put(tagDef, ifdFlags << 24 | (tagType << 16) | defaultComponentCount);
- return tagDef;
- }
- return TAG_NULL;
- }
-
- protected int getTagDefinition(short tagId, int defaultIfd) {
- return getTagInfo().get(defineTag(defaultIfd, tagId));
- }
-
- protected int[] getTagDefinitionsForTagId(short tagId) {
- int[] ifds = IfdData.getIfds();
- int[] defs = new int[ifds.length];
- int counter = 0;
- SparseIntArray infos = getTagInfo();
- for (int i : ifds) {
- int def = defineTag(i, tagId);
- if (infos.get(def) != DEFINITION_NULL) {
- defs[counter++] = def;
- }
- }
- if (counter == 0) {
- return null;
- }
-
- return Arrays.copyOfRange(defs, 0, counter);
- }
-
- protected int getTagDefinitionForTag(ExifTag tag) {
- short type = tag.getDataType();
- int count = tag.getComponentCount();
- int ifd = tag.getIfd();
- return getTagDefinitionForTag(tag.getTagId(), type, count, ifd);
- }
-
- protected int getTagDefinitionForTag(short tagId, short type, int count, int ifd) {
- int[] defs = getTagDefinitionsForTagId(tagId);
- if (defs == null) {
- return TAG_NULL;
- }
- SparseIntArray infos = getTagInfo();
- int ret = TAG_NULL;
- for (int i : defs) {
- int info = infos.get(i);
- short def_type = getTypeFromInfo(info);
- int def_count = getComponentCountFromInfo(info);
- int[] def_ifds = getAllowedIfdsFromInfo(info);
- boolean valid_ifd = false;
- for (int j : def_ifds) {
- if (j == ifd) {
- valid_ifd = true;
- break;
- }
- }
- if (valid_ifd && type == def_type
- && (count == def_count || def_count == ExifTag.SIZE_UNDEFINED)) {
- ret = i;
- break;
- }
- }
- return ret;
- }
-
- /**
- * Removes a tag definition for given defined tag constant.
- *
- * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- */
- public void removeTagDefinition(int tagId) {
- getTagInfo().delete(tagId);
- }
-
- /**
- * Resets tag definitions to the default ones.
- */
- public void resetTagDefinitions() {
- mTagInfo = null;
- }
-
- /**
- * Returns the thumbnail from IFD1 as a bitmap, or null if none exists.
- *
- * @return the thumbnail as a bitmap.
- */
- public Bitmap getThumbnailBitmap() {
- if (mData.hasCompressedThumbnail()) {
- byte[] thumb = mData.getCompressedThumbnail();
- return BitmapFactory.decodeByteArray(thumb, 0, thumb.length);
- } else if (mData.hasUncompressedStrip()) {
- // TODO: implement uncompressed
- }
- return null;
- }
-
- /**
- * Returns the thumbnail from IFD1 as a byte array, or null if none exists.
- * The bytes may either be an uncompressed strip as specified in the exif
- * standard or a jpeg compressed image.
- *
- * @return the thumbnail as a byte array.
- */
- public byte[] getThumbnailBytes() {
- if (mData.hasCompressedThumbnail()) {
- return mData.getCompressedThumbnail();
- } else if (mData.hasUncompressedStrip()) {
- // TODO: implement this
- }
- return null;
- }
-
- /**
- * Returns the thumbnail if it is jpeg compressed, or null if none exists.
- *
- * @return the thumbnail as a byte array.
- */
- public byte[] getThumbnail() {
- return mData.getCompressedThumbnail();
- }
-
- /**
- * Check if thumbnail is compressed.
- *
- * @return true if the thumbnail is compressed.
- */
- public boolean isThumbnailCompressed() {
- return mData.hasCompressedThumbnail();
- }
-
- /**
- * Check if thumbnail exists.
- *
- * @return true if a compressed thumbnail exists.
- */
- public boolean hasThumbnail() {
- // TODO: add back in uncompressed strip
- return mData.hasCompressedThumbnail();
- }
-
- // TODO: uncompressed thumbnail setters
-
- /**
- * Sets the thumbnail to be a jpeg compressed image. Clears any prior
- * thumbnail.
- *
- * @param thumb a byte array containing a jpeg compressed image.
- * @return true if the thumbnail was set.
- */
- public boolean setCompressedThumbnail(byte[] thumb) {
- mData.clearThumbnailAndStrips();
- mData.setCompressedThumbnail(thumb);
- return true;
- }
-
- /**
- * Sets the thumbnail to be a jpeg compressed bitmap. Clears any prior
- * thumbnail.
- *
- * @param thumb a bitmap to compress to a jpeg thumbnail.
- * @return true if the thumbnail was set.
- */
- public boolean setCompressedThumbnail(Bitmap thumb) {
- ByteArrayOutputStream thumbnail = new ByteArrayOutputStream();
- if (!thumb.compress(Bitmap.CompressFormat.JPEG, 90, thumbnail)) {
- return false;
- }
- return setCompressedThumbnail(thumbnail.toByteArray());
- }
-
- /**
- * Clears the compressed thumbnail if it exists.
- */
- public void removeCompressedThumbnail() {
- mData.setCompressedThumbnail(null);
- }
-
- // Convenience methods:
-
- /**
- * Decodes the user comment tag into string as specified in the EXIF
- * standard. Returns null if decoding failed.
- */
- public String getUserComment() {
- return mData.getUserComment();
- }
-
- /**
- * Returns the Orientation ExifTag value for a given number of degrees.
- *
- * @param degrees the amount an image is rotated in degrees.
- */
- public static short getOrientationValueForRotation(int degrees) {
- degrees %= 360;
- if (degrees < 0) {
- degrees += 360;
- }
- if (degrees < 90) {
- return Orientation.TOP_LEFT; // 0 degrees
- } else if (degrees < 180) {
- return Orientation.RIGHT_TOP; // 90 degrees cw
- } else if (degrees < 270) {
- return Orientation.BOTTOM_LEFT; // 180 degrees
- } else {
- return Orientation.RIGHT_BOTTOM; // 270 degrees cw
- }
- }
-
- /**
- * Returns the rotation degrees corresponding to an ExifTag Orientation
- * value.
- *
- * @param orientation the ExifTag Orientation value.
- */
- public static int getRotationForOrientationValue(short orientation) {
- switch (orientation) {
- case Orientation.TOP_LEFT:
- return 0;
- case Orientation.RIGHT_TOP:
- return 90;
- case Orientation.BOTTOM_LEFT:
- return 180;
- case Orientation.RIGHT_BOTTOM:
- return 270;
- default:
- return 0;
- }
- }
-
- /**
- * Gets the double representation of the GPS latitude or longitude
- * coordinate.
- *
- * @param coordinate an array of 3 Rationals representing the degrees,
- * minutes, and seconds of the GPS location as defined in the
- * exif specification.
- * @param reference a GPS reference reperesented by a String containing "N",
- * "S", "E", or "W".
- * @return the GPS coordinate represented as degrees + minutes/60 +
- * seconds/3600
- */
- public static double convertLatOrLongToDouble(Rational[] coordinate, String reference) {
- try {
- double degrees = coordinate[0].toDouble();
- double minutes = coordinate[1].toDouble();
- double seconds = coordinate[2].toDouble();
- double result = degrees + minutes / 60.0 + seconds / 3600.0;
- if ((reference.equals("S") || reference.equals("W"))) {
- return -result;
- }
- return result;
- } catch (ArrayIndexOutOfBoundsException e) {
- throw new IllegalArgumentException();
- }
- }
-
- /**
- * Gets the GPS latitude and longitude as a pair of doubles from this
- * ExifInterface object's tags, or null if the necessary tags do not exist.
- *
- * @return an array of 2 doubles containing the latitude, and longitude
- * respectively.
- * @see #convertLatOrLongToDouble
- */
- public double[] getLatLongAsDoubles() {
- Rational[] latitude = getTagRationalValues(TAG_GPS_LATITUDE);
- String latitudeRef = getTagStringValue(TAG_GPS_LATITUDE_REF);
- Rational[] longitude = getTagRationalValues(TAG_GPS_LONGITUDE);
- String longitudeRef = getTagStringValue(TAG_GPS_LONGITUDE_REF);
- if (latitude == null || longitude == null || latitudeRef == null || longitudeRef == null
- || latitude.length < 3 || longitude.length < 3) {
- return null;
- }
- double[] latLon = new double[2];
- latLon[0] = convertLatOrLongToDouble(latitude, latitudeRef);
- latLon[1] = convertLatOrLongToDouble(longitude, longitudeRef);
- return latLon;
- }
-
- private static final String GPS_DATE_FORMAT_STR = "yyyy:MM:dd";
- private static final String DATETIME_FORMAT_STR = "yyyy:MM:dd kk:mm:ss";
- private final DateFormat mDateTimeStampFormat = new SimpleDateFormat(DATETIME_FORMAT_STR);
- private final DateFormat mGPSDateStampFormat = new SimpleDateFormat(GPS_DATE_FORMAT_STR);
- private final Calendar mGPSTimeStampCalendar = Calendar
- .getInstance(TimeZone.getTimeZone("UTC"));
-
- /**
- * Creates, formats, and sets the DateTimeStamp tag for one of:
- * {@link #TAG_DATE_TIME}, {@link #TAG_DATE_TIME_DIGITIZED},
- * {@link #TAG_DATE_TIME_ORIGINAL}.
- *
- * @param tagId one of the DateTimeStamp tags.
- * @param timestamp a timestamp to format.
- * @param timezone a TimeZone object.
- * @return true if success, false if the tag could not be set.
- */
- public boolean addDateTimeStampTag(int tagId, long timestamp, TimeZone timezone) {
- if (tagId == TAG_DATE_TIME || tagId == TAG_DATE_TIME_DIGITIZED
- || tagId == TAG_DATE_TIME_ORIGINAL) {
- mDateTimeStampFormat.setTimeZone(timezone);
- ExifTag t = buildTag(tagId, mDateTimeStampFormat.format(timestamp));
- if (t == null) {
- return false;
- }
- setTag(t);
- } else {
- return false;
- }
- return true;
- }
-
- /**
- * Creates and sets all to the GPS tags for a give latitude and longitude.
- *
- * @param latitude a GPS latitude coordinate.
- * @param longitude a GPS longitude coordinate.
- * @return true if success, false if they could not be created or set.
- */
- public boolean addGpsTags(double latitude, double longitude) {
- ExifTag latTag = buildTag(TAG_GPS_LATITUDE, toExifLatLong(latitude));
- ExifTag longTag = buildTag(TAG_GPS_LONGITUDE, toExifLatLong(longitude));
- ExifTag latRefTag = buildTag(TAG_GPS_LATITUDE_REF,
- latitude >= 0 ? ExifInterface.GpsLatitudeRef.NORTH
- : ExifInterface.GpsLatitudeRef.SOUTH);
- ExifTag longRefTag = buildTag(TAG_GPS_LONGITUDE_REF,
- longitude >= 0 ? ExifInterface.GpsLongitudeRef.EAST
- : ExifInterface.GpsLongitudeRef.WEST);
- if (latTag == null || longTag == null || latRefTag == null || longRefTag == null) {
- return false;
- }
- setTag(latTag);
- setTag(longTag);
- setTag(latRefTag);
- setTag(longRefTag);
- return true;
- }
-
- /**
- * Creates and sets the GPS timestamp tag.
- *
- * @param timestamp a GPS timestamp.
- * @return true if success, false if could not be created or set.
- */
- public boolean addGpsDateTimeStampTag(long timestamp) {
- ExifTag t = buildTag(TAG_GPS_DATE_STAMP, mGPSDateStampFormat.format(timestamp));
- if (t == null) {
- return false;
- }
- setTag(t);
- mGPSTimeStampCalendar.setTimeInMillis(timestamp);
- t = buildTag(TAG_GPS_TIME_STAMP, new Rational[] {
- new Rational(mGPSTimeStampCalendar.get(Calendar.HOUR_OF_DAY), 1),
- new Rational(mGPSTimeStampCalendar.get(Calendar.MINUTE), 1),
- new Rational(mGPSTimeStampCalendar.get(Calendar.SECOND), 1)
- });
- if (t == null) {
- return false;
- }
- setTag(t);
- return true;
- }
-
- private static Rational[] toExifLatLong(double value) {
- // convert to the format dd/1 mm/1 ssss/100
- value = Math.abs(value);
- int degrees = (int) value;
- value = (value - degrees) * 60;
- int minutes = (int) value;
- value = (value - minutes) * 6000;
- int seconds = (int) value;
- return new Rational[] {
- new Rational(degrees, 1), new Rational(minutes, 1), new Rational(seconds, 100)
- };
- }
-
- private void doExifStreamIO(InputStream is, OutputStream os) throws IOException {
- byte[] buf = new byte[1024];
- int ret = is.read(buf, 0, 1024);
- while (ret != -1) {
- os.write(buf, 0, ret);
- ret = is.read(buf, 0, 1024);
- }
- }
-
- protected static void closeSilently(Closeable c) {
- if (c != null) {
- try {
- c.close();
- } catch (Throwable e) {
- // ignored
- }
- }
- }
-
- private SparseIntArray mTagInfo = null;
-
- protected SparseIntArray getTagInfo() {
- if (mTagInfo == null) {
- mTagInfo = new SparseIntArray();
- initTagInfo();
- }
- return mTagInfo;
- }
-
- private void initTagInfo() {
- /**
- * We put tag information in a 4-bytes integer. The first byte a bitmask
- * representing the allowed IFDs of the tag, the second byte is the data
- * type, and the last two byte are a short value indicating the default
- * component count of this tag.
- */
- // IFD0 tags
- int[] ifdAllowedIfds = {
- IfdId.TYPE_IFD_0, IfdId.TYPE_IFD_1
- };
- int ifdFlags = getFlagsFromAllowedIfds(ifdAllowedIfds) << 24;
- mTagInfo.put(ExifInterface.TAG_MAKE,
- ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_IMAGE_WIDTH,
- ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_IMAGE_LENGTH,
- ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_BITS_PER_SAMPLE,
- ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 3);
- mTagInfo.put(ExifInterface.TAG_COMPRESSION,
- ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_PHOTOMETRIC_INTERPRETATION,
- ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_ORIENTATION, ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16
- | 1);
- mTagInfo.put(ExifInterface.TAG_SAMPLES_PER_PIXEL,
- ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_PLANAR_CONFIGURATION,
- ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_Y_CB_CR_SUB_SAMPLING,
- ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_Y_CB_CR_POSITIONING,
- ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_X_RESOLUTION,
- ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_Y_RESOLUTION,
- ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_RESOLUTION_UNIT,
- ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_STRIP_OFFSETS,
- ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_ROWS_PER_STRIP,
- ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_STRIP_BYTE_COUNTS,
- ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_TRANSFER_FUNCTION,
- ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 3 * 256);
- mTagInfo.put(ExifInterface.TAG_WHITE_POINT,
- ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_PRIMARY_CHROMATICITIES,
- ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 6);
- mTagInfo.put(ExifInterface.TAG_Y_CB_CR_COEFFICIENTS,
- ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 3);
- mTagInfo.put(ExifInterface.TAG_REFERENCE_BLACK_WHITE,
- ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 6);
- mTagInfo.put(ExifInterface.TAG_DATE_TIME,
- ifdFlags | ExifTag.TYPE_ASCII << 16 | 20);
- mTagInfo.put(ExifInterface.TAG_IMAGE_DESCRIPTION,
- ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_MAKE,
- ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_MODEL,
- ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_SOFTWARE,
- ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_ARTIST,
- ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_COPYRIGHT,
- ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_EXIF_IFD,
- ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_IFD,
- ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- // IFD1 tags
- int[] ifd1AllowedIfds = {
- IfdId.TYPE_IFD_1
- };
- int ifdFlags1 = getFlagsFromAllowedIfds(ifd1AllowedIfds) << 24;
- mTagInfo.put(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT,
- ifdFlags1 | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
- ifdFlags1 | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- // Exif tags
- int[] exifAllowedIfds = {
- IfdId.TYPE_IFD_EXIF
- };
- int exifFlags = getFlagsFromAllowedIfds(exifAllowedIfds) << 24;
- mTagInfo.put(ExifInterface.TAG_EXIF_VERSION,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 4);
- mTagInfo.put(ExifInterface.TAG_FLASHPIX_VERSION,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 4);
- mTagInfo.put(ExifInterface.TAG_COLOR_SPACE,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_COMPONENTS_CONFIGURATION,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 4);
- mTagInfo.put(ExifInterface.TAG_COMPRESSED_BITS_PER_PIXEL,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_PIXEL_X_DIMENSION,
- exifFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_PIXEL_Y_DIMENSION,
- exifFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_MAKER_NOTE,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_USER_COMMENT,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_RELATED_SOUND_FILE,
- exifFlags | ExifTag.TYPE_ASCII << 16 | 13);
- mTagInfo.put(ExifInterface.TAG_DATE_TIME_ORIGINAL,
- exifFlags | ExifTag.TYPE_ASCII << 16 | 20);
- mTagInfo.put(ExifInterface.TAG_DATE_TIME_DIGITIZED,
- exifFlags | ExifTag.TYPE_ASCII << 16 | 20);
- mTagInfo.put(ExifInterface.TAG_SUB_SEC_TIME,
- exifFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_SUB_SEC_TIME_ORIGINAL,
- exifFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_SUB_SEC_TIME_DIGITIZED,
- exifFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_IMAGE_UNIQUE_ID,
- exifFlags | ExifTag.TYPE_ASCII << 16 | 33);
- mTagInfo.put(ExifInterface.TAG_EXPOSURE_TIME,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_F_NUMBER,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_EXPOSURE_PROGRAM,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SPECTRAL_SENSITIVITY,
- exifFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_ISO_SPEED_RATINGS,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_OECF,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_SHUTTER_SPEED_VALUE,
- exifFlags | ExifTag.TYPE_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_APERTURE_VALUE,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_BRIGHTNESS_VALUE,
- exifFlags | ExifTag.TYPE_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_EXPOSURE_BIAS_VALUE,
- exifFlags | ExifTag.TYPE_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_MAX_APERTURE_VALUE,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SUBJECT_DISTANCE,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_METERING_MODE,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_LIGHT_SOURCE,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_FLASH,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_FOCAL_LENGTH,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SUBJECT_AREA,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_FLASH_ENERGY,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SPATIAL_FREQUENCY_RESPONSE,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_FOCAL_PLANE_X_RESOLUTION,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_FOCAL_PLANE_Y_RESOLUTION,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_FOCAL_PLANE_RESOLUTION_UNIT,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SUBJECT_LOCATION,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_EXPOSURE_INDEX,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SENSING_METHOD,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_FILE_SOURCE,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SCENE_TYPE,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_CFA_PATTERN,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_CUSTOM_RENDERED,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_EXPOSURE_MODE,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_WHITE_BALANCE,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_DIGITAL_ZOOM_RATIO,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_FOCAL_LENGTH_IN_35_MM_FILE,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SCENE_CAPTURE_TYPE,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GAIN_CONTROL,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_CONTRAST,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SATURATION,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SHARPNESS,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_DEVICE_SETTING_DESCRIPTION,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_SUBJECT_DISTANCE_RANGE,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_INTEROPERABILITY_IFD, exifFlags
- | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- // GPS tag
- int[] gpsAllowedIfds = {
- IfdId.TYPE_IFD_GPS
- };
- int gpsFlags = getFlagsFromAllowedIfds(gpsAllowedIfds) << 24;
- mTagInfo.put(ExifInterface.TAG_GPS_VERSION_ID,
- gpsFlags | ExifTag.TYPE_UNSIGNED_BYTE << 16 | 4);
- mTagInfo.put(ExifInterface.TAG_GPS_LATITUDE_REF,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_LONGITUDE_REF,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_LATITUDE,
- gpsFlags | ExifTag.TYPE_RATIONAL << 16 | 3);
- mTagInfo.put(ExifInterface.TAG_GPS_LONGITUDE,
- gpsFlags | ExifTag.TYPE_RATIONAL << 16 | 3);
- mTagInfo.put(ExifInterface.TAG_GPS_ALTITUDE_REF,
- gpsFlags | ExifTag.TYPE_UNSIGNED_BYTE << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_ALTITUDE,
- gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_TIME_STAMP,
- gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 3);
- mTagInfo.put(ExifInterface.TAG_GPS_SATTELLITES,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_GPS_STATUS,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_MEASURE_MODE,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_DOP,
- gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_SPEED_REF,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_SPEED,
- gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_TRACK_REF,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_TRACK,
- gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_IMG_DIRECTION,
- gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_MAP_DATUM,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_GPS_DEST_LATITUDE_REF,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_DEST_LATITUDE,
- gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_DEST_BEARING_REF,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_DEST_BEARING,
- gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_DEST_DISTANCE_REF,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_DEST_DISTANCE,
- gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_PROCESSING_METHOD,
- gpsFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_GPS_AREA_INFORMATION,
- gpsFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_GPS_DATE_STAMP,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 11);
- mTagInfo.put(ExifInterface.TAG_GPS_DIFFERENTIAL,
- gpsFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 11);
- // Interoperability tag
- int[] interopAllowedIfds = {
- IfdId.TYPE_IFD_INTEROPERABILITY
- };
- int interopFlags = getFlagsFromAllowedIfds(interopAllowedIfds) << 24;
- mTagInfo.put(TAG_INTEROPERABILITY_INDEX, interopFlags | ExifTag.TYPE_ASCII << 16
- | ExifTag.SIZE_UNDEFINED);
- }
-
- protected static int getAllowedIfdFlagsFromInfo(int info) {
- return info >>> 24;
- }
-
- protected static int[] getAllowedIfdsFromInfo(int info) {
- int ifdFlags = getAllowedIfdFlagsFromInfo(info);
- int[] ifds = IfdData.getIfds();
- ArrayList<Integer> l = new ArrayList<Integer>();
- for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
- int flag = (ifdFlags >> i) & 1;
- if (flag == 1) {
- l.add(ifds[i]);
- }
- }
- if (l.size() <= 0) {
- return null;
- }
- int[] ret = new int[l.size()];
- int j = 0;
- for (int i : l) {
- ret[j++] = i;
- }
- return ret;
- }
-
- protected static boolean isIfdAllowed(int info, int ifd) {
- int[] ifds = IfdData.getIfds();
- int ifdFlags = getAllowedIfdFlagsFromInfo(info);
- for (int i = 0; i < ifds.length; i++) {
- if (ifd == ifds[i] && ((ifdFlags >> i) & 1) == 1) {
- return true;
- }
- }
- return false;
- }
-
- protected static int getFlagsFromAllowedIfds(int[] allowedIfds) {
- if (allowedIfds == null || allowedIfds.length == 0) {
- return 0;
- }
- int flags = 0;
- int[] ifds = IfdData.getIfds();
- for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
- for (int j : allowedIfds) {
- if (ifds[i] == j) {
- flags |= 1 << i;
- break;
- }
- }
- }
- return flags;
- }
-
- protected static short getTypeFromInfo(int info) {
- return (short) ((info >> 16) & 0x0ff);
- }
-
- protected static int getComponentCountFromInfo(int info) {
- return info & 0x0ffff;
- }
-
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ExifInvalidFormatException.java b/WallpaperPicker/src/com/android/gallery3d/exif/ExifInvalidFormatException.java
deleted file mode 100644
index bf923ec26..000000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ExifInvalidFormatException.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2012 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.gallery3d.exif;
-
-public class ExifInvalidFormatException extends Exception {
- public ExifInvalidFormatException(String meg) {
- super(meg);
- }
-} \ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ExifModifier.java b/WallpaperPicker/src/com/android/gallery3d/exif/ExifModifier.java
deleted file mode 100644
index 0531cbad9..000000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ExifModifier.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2012 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.gallery3d.exif;
-
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.ArrayList;
-import java.util.List;
-
-class ExifModifier {
- public static final String TAG = "ExifModifier";
- public static final boolean DEBUG = false;
- private final ByteBuffer mByteBuffer;
- private final ExifData mTagToModified;
- private final List<TagOffset> mTagOffsets = new ArrayList<TagOffset>();
- private final ExifInterface mInterface;
- private int mOffsetBase;
-
- private static class TagOffset {
- final int mOffset;
- final ExifTag mTag;
-
- TagOffset(ExifTag tag, int offset) {
- mTag = tag;
- mOffset = offset;
- }
- }
-
- protected ExifModifier(ByteBuffer byteBuffer, ExifInterface iRef) throws IOException,
- ExifInvalidFormatException {
- mByteBuffer = byteBuffer;
- mOffsetBase = byteBuffer.position();
- mInterface = iRef;
- InputStream is = null;
- try {
- is = new ByteBufferInputStream(byteBuffer);
- // Do not require any IFD;
- ExifParser parser = ExifParser.parse(is, mInterface);
- mTagToModified = new ExifData(parser.getByteOrder());
- mOffsetBase += parser.getTiffStartPosition();
- mByteBuffer.position(0);
- } finally {
- ExifInterface.closeSilently(is);
- }
- }
-
- protected ByteOrder getByteOrder() {
- return mTagToModified.getByteOrder();
- }
-
- protected boolean commit() throws IOException, ExifInvalidFormatException {
- InputStream is = null;
- try {
- is = new ByteBufferInputStream(mByteBuffer);
- int flag = 0;
- IfdData[] ifdDatas = new IfdData[] {
- mTagToModified.getIfdData(IfdId.TYPE_IFD_0),
- mTagToModified.getIfdData(IfdId.TYPE_IFD_1),
- mTagToModified.getIfdData(IfdId.TYPE_IFD_EXIF),
- mTagToModified.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY),
- mTagToModified.getIfdData(IfdId.TYPE_IFD_GPS)
- };
-
- if (ifdDatas[IfdId.TYPE_IFD_0] != null) {
- flag |= ExifParser.OPTION_IFD_0;
- }
- if (ifdDatas[IfdId.TYPE_IFD_1] != null) {
- flag |= ExifParser.OPTION_IFD_1;
- }
- if (ifdDatas[IfdId.TYPE_IFD_EXIF] != null) {
- flag |= ExifParser.OPTION_IFD_EXIF;
- }
- if (ifdDatas[IfdId.TYPE_IFD_GPS] != null) {
- flag |= ExifParser.OPTION_IFD_GPS;
- }
- if (ifdDatas[IfdId.TYPE_IFD_INTEROPERABILITY] != null) {
- flag |= ExifParser.OPTION_IFD_INTEROPERABILITY;
- }
-
- ExifParser parser = ExifParser.parse(is, flag, mInterface);
- int event = parser.next();
- IfdData currIfd = null;
- while (event != ExifParser.EVENT_END) {
- switch (event) {
- case ExifParser.EVENT_START_OF_IFD:
- currIfd = ifdDatas[parser.getCurrentIfd()];
- if (currIfd == null) {
- parser.skipRemainingTagsInCurrentIfd();
- }
- break;
- case ExifParser.EVENT_NEW_TAG:
- ExifTag oldTag = parser.getTag();
- ExifTag newTag = currIfd.getTag(oldTag.getTagId());
- if (newTag != null) {
- if (newTag.getComponentCount() != oldTag.getComponentCount()
- || newTag.getDataType() != oldTag.getDataType()) {
- return false;
- } else {
- mTagOffsets.add(new TagOffset(newTag, oldTag.getOffset()));
- currIfd.removeTag(oldTag.getTagId());
- if (currIfd.getTagCount() == 0) {
- parser.skipRemainingTagsInCurrentIfd();
- }
- }
- }
- break;
- }
- event = parser.next();
- }
- for (IfdData ifd : ifdDatas) {
- if (ifd != null && ifd.getTagCount() > 0) {
- return false;
- }
- }
- modify();
- } finally {
- ExifInterface.closeSilently(is);
- }
- return true;
- }
-
- private void modify() {
- mByteBuffer.order(getByteOrder());
- for (TagOffset tagOffset : mTagOffsets) {
- writeTagValue(tagOffset.mTag, tagOffset.mOffset);
- }
- }
-
- private void writeTagValue(ExifTag tag, int offset) {
- if (DEBUG) {
- Log.v(TAG, "modifying tag to: \n" + tag.toString());
- Log.v(TAG, "at offset: " + offset);
- }
- mByteBuffer.position(offset + mOffsetBase);
- switch (tag.getDataType()) {
- case ExifTag.TYPE_ASCII:
- byte buf[] = tag.getStringByte();
- if (buf.length == tag.getComponentCount()) {
- buf[buf.length - 1] = 0;
- mByteBuffer.put(buf);
- } else {
- mByteBuffer.put(buf);
- mByteBuffer.put((byte) 0);
- }
- break;
- case ExifTag.TYPE_LONG:
- case ExifTag.TYPE_UNSIGNED_LONG:
- for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
- mByteBuffer.putInt((int) tag.getValueAt(i));
- }
- break;
- case ExifTag.TYPE_RATIONAL:
- case ExifTag.TYPE_UNSIGNED_RATIONAL:
- for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
- Rational v = tag.getRational(i);
- mByteBuffer.putInt((int) v.getNumerator());
- mByteBuffer.putInt((int) v.getDenominator());
- }
- break;
- case ExifTag.TYPE_UNDEFINED:
- case ExifTag.TYPE_UNSIGNED_BYTE:
- buf = new byte[tag.getComponentCount()];
- tag.getBytes(buf);
- mByteBuffer.put(buf);
- break;
- case ExifTag.TYPE_UNSIGNED_SHORT:
- for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
- mByteBuffer.putShort((short) tag.getValueAt(i));
- }
- break;
- }
- }
-
- public void modifyTag(ExifTag tag) {
- mTagToModified.addTag(tag);
- }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ExifOutputStream.java b/WallpaperPicker/src/com/android/gallery3d/exif/ExifOutputStream.java
deleted file mode 100644
index 7ca05f2e0..000000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ExifOutputStream.java
+++ /dev/null
@@ -1,518 +0,0 @@
-/*
- * Copyright (C) 2012 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.gallery3d.exif;
-
-import android.util.Log;
-
-import java.io.BufferedOutputStream;
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.ArrayList;
-
-/**
- * This class provides a way to replace the Exif header of a JPEG image.
- * <p>
- * Below is an example of writing EXIF data into a file
- *
- * <pre>
- * public static void writeExif(byte[] jpeg, ExifData exif, String path) {
- * OutputStream os = null;
- * try {
- * os = new FileOutputStream(path);
- * ExifOutputStream eos = new ExifOutputStream(os);
- * // Set the exif header
- * eos.setExifData(exif);
- * // Write the original jpeg out, the header will be add into the file.
- * eos.write(jpeg);
- * } catch (FileNotFoundException e) {
- * e.printStackTrace();
- * } catch (IOException e) {
- * e.printStackTrace();
- * } finally {
- * if (os != null) {
- * try {
- * os.close();
- * } catch (IOException e) {
- * e.printStackTrace();
- * }
- * }
- * }
- * }
- * </pre>
- */
-class ExifOutputStream extends FilterOutputStream {
- private static final String TAG = "ExifOutputStream";
- private static final boolean DEBUG = false;
- private static final int STREAMBUFFER_SIZE = 0x00010000; // 64Kb
-
- private static final int STATE_SOI = 0;
- private static final int STATE_FRAME_HEADER = 1;
- private static final int STATE_JPEG_DATA = 2;
-
- private static final int EXIF_HEADER = 0x45786966;
- private static final short TIFF_HEADER = 0x002A;
- private static final short TIFF_BIG_ENDIAN = 0x4d4d;
- private static final short TIFF_LITTLE_ENDIAN = 0x4949;
- private static final short TAG_SIZE = 12;
- private static final short TIFF_HEADER_SIZE = 8;
- private static final int MAX_EXIF_SIZE = 65535;
-
- private ExifData mExifData;
- private int mState = STATE_SOI;
- private int mByteToSkip;
- private int mByteToCopy;
- private byte[] mSingleByteArray = new byte[1];
- private ByteBuffer mBuffer = ByteBuffer.allocate(4);
- private final ExifInterface mInterface;
-
- protected ExifOutputStream(OutputStream ou, ExifInterface iRef) {
- super(new BufferedOutputStream(ou, STREAMBUFFER_SIZE));
- mInterface = iRef;
- }
-
- /**
- * Sets the ExifData to be written into the JPEG file. Should be called
- * before writing image data.
- */
- protected void setExifData(ExifData exifData) {
- mExifData = exifData;
- }
-
- /**
- * Gets the Exif header to be written into the JPEF file.
- */
- protected ExifData getExifData() {
- return mExifData;
- }
-
- private int requestByteToBuffer(int requestByteCount, byte[] buffer
- , int offset, int length) {
- int byteNeeded = requestByteCount - mBuffer.position();
- int byteToRead = length > byteNeeded ? byteNeeded : length;
- mBuffer.put(buffer, offset, byteToRead);
- return byteToRead;
- }
-
- /**
- * Writes the image out. The input data should be a valid JPEG format. After
- * writing, it's Exif header will be replaced by the given header.
- */
- @Override
- public void write(byte[] buffer, int offset, int length) throws IOException {
- while ((mByteToSkip > 0 || mByteToCopy > 0 || mState != STATE_JPEG_DATA)
- && length > 0) {
- if (mByteToSkip > 0) {
- int byteToProcess = length > mByteToSkip ? mByteToSkip : length;
- length -= byteToProcess;
- mByteToSkip -= byteToProcess;
- offset += byteToProcess;
- }
- if (mByteToCopy > 0) {
- int byteToProcess = length > mByteToCopy ? mByteToCopy : length;
- out.write(buffer, offset, byteToProcess);
- length -= byteToProcess;
- mByteToCopy -= byteToProcess;
- offset += byteToProcess;
- }
- if (length == 0) {
- return;
- }
- switch (mState) {
- case STATE_SOI:
- int byteRead = requestByteToBuffer(2, buffer, offset, length);
- offset += byteRead;
- length -= byteRead;
- if (mBuffer.position() < 2) {
- return;
- }
- mBuffer.rewind();
- if (mBuffer.getShort() != JpegHeader.SOI) {
- throw new IOException("Not a valid jpeg image, cannot write exif");
- }
- out.write(mBuffer.array(), 0, 2);
- mState = STATE_FRAME_HEADER;
- mBuffer.rewind();
- writeExifData();
- break;
- case STATE_FRAME_HEADER:
- // We ignore the APP1 segment and copy all other segments
- // until SOF tag.
- byteRead = requestByteToBuffer(4, buffer, offset, length);
- offset += byteRead;
- length -= byteRead;
- // Check if this image data doesn't contain SOF.
- if (mBuffer.position() == 2) {
- short tag = mBuffer.getShort();
- if (tag == JpegHeader.EOI) {
- out.write(mBuffer.array(), 0, 2);
- mBuffer.rewind();
- }
- }
- if (mBuffer.position() < 4) {
- return;
- }
- mBuffer.rewind();
- short marker = mBuffer.getShort();
- if (marker == JpegHeader.APP1) {
- mByteToSkip = (mBuffer.getShort() & 0x0000ffff) - 2;
- mState = STATE_JPEG_DATA;
- } else if (!JpegHeader.isSofMarker(marker)) {
- out.write(mBuffer.array(), 0, 4);
- mByteToCopy = (mBuffer.getShort() & 0x0000ffff) - 2;
- } else {
- out.write(mBuffer.array(), 0, 4);
- mState = STATE_JPEG_DATA;
- }
- mBuffer.rewind();
- }
- }
- if (length > 0) {
- out.write(buffer, offset, length);
- }
- }
-
- /**
- * Writes the one bytes out. The input data should be a valid JPEG format.
- * After writing, it's Exif header will be replaced by the given header.
- */
- @Override
- public void write(int oneByte) throws IOException {
- mSingleByteArray[0] = (byte) (0xff & oneByte);
- write(mSingleByteArray);
- }
-
- /**
- * Equivalent to calling write(buffer, 0, buffer.length).
- */
- @Override
- public void write(byte[] buffer) throws IOException {
- write(buffer, 0, buffer.length);
- }
-
- private void writeExifData() throws IOException {
- if (mExifData == null) {
- return;
- }
- if (DEBUG) {
- Log.v(TAG, "Writing exif data...");
- }
- ArrayList<ExifTag> nullTags = stripNullValueTags(mExifData);
- createRequiredIfdAndTag();
- int exifSize = calculateAllOffset();
- if (exifSize + 8 > MAX_EXIF_SIZE) {
- throw new IOException("Exif header is too large (>64Kb)");
- }
- OrderedDataOutputStream dataOutputStream = new OrderedDataOutputStream(out);
- dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN);
- dataOutputStream.writeShort(JpegHeader.APP1);
- dataOutputStream.writeShort((short) (exifSize + 8));
- dataOutputStream.writeInt(EXIF_HEADER);
- dataOutputStream.writeShort((short) 0x0000);
- if (mExifData.getByteOrder() == ByteOrder.BIG_ENDIAN) {
- dataOutputStream.writeShort(TIFF_BIG_ENDIAN);
- } else {
- dataOutputStream.writeShort(TIFF_LITTLE_ENDIAN);
- }
- dataOutputStream.setByteOrder(mExifData.getByteOrder());
- dataOutputStream.writeShort(TIFF_HEADER);
- dataOutputStream.writeInt(8);
- writeAllTags(dataOutputStream);
- writeThumbnail(dataOutputStream);
- for (ExifTag t : nullTags) {
- mExifData.addTag(t);
- }
- }
-
- private ArrayList<ExifTag> stripNullValueTags(ExifData data) {
- ArrayList<ExifTag> nullTags = new ArrayList<ExifTag>();
- for(ExifTag t : data.getAllTags()) {
- if (t.getValue() == null && !ExifInterface.isOffsetTag(t.getTagId())) {
- data.removeTag(t.getTagId(), t.getIfd());
- nullTags.add(t);
- }
- }
- return nullTags;
- }
-
- private void writeThumbnail(OrderedDataOutputStream dataOutputStream) throws IOException {
- if (mExifData.hasCompressedThumbnail()) {
- dataOutputStream.write(mExifData.getCompressedThumbnail());
- } else if (mExifData.hasUncompressedStrip()) {
- for (int i = 0; i < mExifData.getStripCount(); i++) {
- dataOutputStream.write(mExifData.getStrip(i));
- }
- }
- }
-
- private void writeAllTags(OrderedDataOutputStream dataOutputStream) throws IOException {
- writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_0), dataOutputStream);
- writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_EXIF), dataOutputStream);
- IfdData interoperabilityIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY);
- if (interoperabilityIfd != null) {
- writeIfd(interoperabilityIfd, dataOutputStream);
- }
- IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS);
- if (gpsIfd != null) {
- writeIfd(gpsIfd, dataOutputStream);
- }
- IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1);
- if (ifd1 != null) {
- writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_1), dataOutputStream);
- }
- }
-
- private void writeIfd(IfdData ifd, OrderedDataOutputStream dataOutputStream)
- throws IOException {
- ExifTag[] tags = ifd.getAllTags();
- dataOutputStream.writeShort((short) tags.length);
- for (ExifTag tag : tags) {
- dataOutputStream.writeShort(tag.getTagId());
- dataOutputStream.writeShort(tag.getDataType());
- dataOutputStream.writeInt(tag.getComponentCount());
- if (DEBUG) {
- Log.v(TAG, "\n" + tag.toString());
- }
- if (tag.getDataSize() > 4) {
- dataOutputStream.writeInt(tag.getOffset());
- } else {
- ExifOutputStream.writeTagValue(tag, dataOutputStream);
- for (int i = 0, n = 4 - tag.getDataSize(); i < n; i++) {
- dataOutputStream.write(0);
- }
- }
- }
- dataOutputStream.writeInt(ifd.getOffsetToNextIfd());
- for (ExifTag tag : tags) {
- if (tag.getDataSize() > 4) {
- ExifOutputStream.writeTagValue(tag, dataOutputStream);
- }
- }
- }
-
- private int calculateOffsetOfIfd(IfdData ifd, int offset) {
- offset += 2 + ifd.getTagCount() * TAG_SIZE + 4;
- ExifTag[] tags = ifd.getAllTags();
- for (ExifTag tag : tags) {
- if (tag.getDataSize() > 4) {
- tag.setOffset(offset);
- offset += tag.getDataSize();
- }
- }
- return offset;
- }
-
- private void createRequiredIfdAndTag() throws IOException {
- // IFD0 is required for all file
- IfdData ifd0 = mExifData.getIfdData(IfdId.TYPE_IFD_0);
- if (ifd0 == null) {
- ifd0 = new IfdData(IfdId.TYPE_IFD_0);
- mExifData.addIfdData(ifd0);
- }
- ExifTag exifOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_EXIF_IFD);
- if (exifOffsetTag == null) {
- throw new IOException("No definition for crucial exif tag: "
- + ExifInterface.TAG_EXIF_IFD);
- }
- ifd0.setTag(exifOffsetTag);
-
- // Exif IFD is required for all files.
- IfdData exifIfd = mExifData.getIfdData(IfdId.TYPE_IFD_EXIF);
- if (exifIfd == null) {
- exifIfd = new IfdData(IfdId.TYPE_IFD_EXIF);
- mExifData.addIfdData(exifIfd);
- }
-
- // GPS IFD
- IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS);
- if (gpsIfd != null) {
- ExifTag gpsOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_GPS_IFD);
- if (gpsOffsetTag == null) {
- throw new IOException("No definition for crucial exif tag: "
- + ExifInterface.TAG_GPS_IFD);
- }
- ifd0.setTag(gpsOffsetTag);
- }
-
- // Interoperability IFD
- IfdData interIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY);
- if (interIfd != null) {
- ExifTag interOffsetTag = mInterface
- .buildUninitializedTag(ExifInterface.TAG_INTEROPERABILITY_IFD);
- if (interOffsetTag == null) {
- throw new IOException("No definition for crucial exif tag: "
- + ExifInterface.TAG_INTEROPERABILITY_IFD);
- }
- exifIfd.setTag(interOffsetTag);
- }
-
- IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1);
-
- // thumbnail
- if (mExifData.hasCompressedThumbnail()) {
-
- if (ifd1 == null) {
- ifd1 = new IfdData(IfdId.TYPE_IFD_1);
- mExifData.addIfdData(ifd1);
- }
-
- ExifTag offsetTag = mInterface
- .buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT);
- if (offsetTag == null) {
- throw new IOException("No definition for crucial exif tag: "
- + ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT);
- }
-
- ifd1.setTag(offsetTag);
- ExifTag lengthTag = mInterface
- .buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
- if (lengthTag == null) {
- throw new IOException("No definition for crucial exif tag: "
- + ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
- }
-
- lengthTag.setValue(mExifData.getCompressedThumbnail().length);
- ifd1.setTag(lengthTag);
-
- // Get rid of tags for uncompressed if they exist.
- ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS));
- ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS));
- } else if (mExifData.hasUncompressedStrip()) {
- if (ifd1 == null) {
- ifd1 = new IfdData(IfdId.TYPE_IFD_1);
- mExifData.addIfdData(ifd1);
- }
- int stripCount = mExifData.getStripCount();
- ExifTag offsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_STRIP_OFFSETS);
- if (offsetTag == null) {
- throw new IOException("No definition for crucial exif tag: "
- + ExifInterface.TAG_STRIP_OFFSETS);
- }
- ExifTag lengthTag = mInterface
- .buildUninitializedTag(ExifInterface.TAG_STRIP_BYTE_COUNTS);
- if (lengthTag == null) {
- throw new IOException("No definition for crucial exif tag: "
- + ExifInterface.TAG_STRIP_BYTE_COUNTS);
- }
- long[] lengths = new long[stripCount];
- for (int i = 0; i < mExifData.getStripCount(); i++) {
- lengths[i] = mExifData.getStrip(i).length;
- }
- lengthTag.setValue(lengths);
- ifd1.setTag(offsetTag);
- ifd1.setTag(lengthTag);
- // Get rid of tags for compressed if they exist.
- ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT));
- ifd1.removeTag(ExifInterface
- .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH));
- } else if (ifd1 != null) {
- // Get rid of offset and length tags if there is no thumbnail.
- ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS));
- ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS));
- ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT));
- ifd1.removeTag(ExifInterface
- .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH));
- }
- }
-
- private int calculateAllOffset() {
- int offset = TIFF_HEADER_SIZE;
- IfdData ifd0 = mExifData.getIfdData(IfdId.TYPE_IFD_0);
- offset = calculateOffsetOfIfd(ifd0, offset);
- ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_EXIF_IFD)).setValue(offset);
-
- IfdData exifIfd = mExifData.getIfdData(IfdId.TYPE_IFD_EXIF);
- offset = calculateOffsetOfIfd(exifIfd, offset);
-
- IfdData interIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY);
- if (interIfd != null) {
- exifIfd.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD))
- .setValue(offset);
- offset = calculateOffsetOfIfd(interIfd, offset);
- }
-
- IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS);
- if (gpsIfd != null) {
- ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD)).setValue(offset);
- offset = calculateOffsetOfIfd(gpsIfd, offset);
- }
-
- IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1);
- if (ifd1 != null) {
- ifd0.setOffsetToNextIfd(offset);
- offset = calculateOffsetOfIfd(ifd1, offset);
- }
-
- // thumbnail
- if (mExifData.hasCompressedThumbnail()) {
- ifd1.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT))
- .setValue(offset);
- offset += mExifData.getCompressedThumbnail().length;
- } else if (mExifData.hasUncompressedStrip()) {
- int stripCount = mExifData.getStripCount();
- long[] offsets = new long[stripCount];
- for (int i = 0; i < mExifData.getStripCount(); i++) {
- offsets[i] = offset;
- offset += mExifData.getStrip(i).length;
- }
- ifd1.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS)).setValue(
- offsets);
- }
- return offset;
- }
-
- static void writeTagValue(ExifTag tag, OrderedDataOutputStream dataOutputStream)
- throws IOException {
- switch (tag.getDataType()) {
- case ExifTag.TYPE_ASCII:
- byte buf[] = tag.getStringByte();
- if (buf.length == tag.getComponentCount()) {
- buf[buf.length - 1] = 0;
- dataOutputStream.write(buf);
- } else {
- dataOutputStream.write(buf);
- dataOutputStream.write(0);
- }
- break;
- case ExifTag.TYPE_LONG:
- case ExifTag.TYPE_UNSIGNED_LONG:
- for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
- dataOutputStream.writeInt((int) tag.getValueAt(i));
- }
- break;
- case ExifTag.TYPE_RATIONAL:
- case ExifTag.TYPE_UNSIGNED_RATIONAL:
- for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
- dataOutputStream.writeRational(tag.getRational(i));
- }
- break;
- case ExifTag.TYPE_UNDEFINED:
- case ExifTag.TYPE_UNSIGNED_BYTE:
- buf = new byte[tag.getComponentCount()];
- tag.getBytes(buf);
- dataOutputStream.write(buf);
- break;
- case ExifTag.TYPE_UNSIGNED_SHORT:
- for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
- dataOutputStream.writeShort((short) tag.getValueAt(i));
- }
- break;
- }
- }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ExifParser.java b/WallpaperPicker/src/com/android/gallery3d/exif/ExifParser.java
deleted file mode 100644
index 5467d423d..000000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ExifParser.java
+++ /dev/null
@@ -1,916 +0,0 @@
-/*
- * Copyright (C) 2012 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.gallery3d.exif;
-
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteOrder;
-import java.nio.charset.Charset;
-import java.util.Map.Entry;
-import java.util.TreeMap;
-
-/**
- * This class provides a low-level EXIF parsing API. Given a JPEG format
- * InputStream, the caller can request which IFD's to read via
- * {@link #parse(InputStream, int)} with given options.
- * <p>
- * Below is an example of getting EXIF data from IFD 0 and EXIF IFD using the
- * parser.
- *
- * <pre>
- * void parse() {
- * ExifParser parser = ExifParser.parse(mImageInputStream,
- * ExifParser.OPTION_IFD_0 | ExifParser.OPTIONS_IFD_EXIF);
- * int event = parser.next();
- * while (event != ExifParser.EVENT_END) {
- * switch (event) {
- * case ExifParser.EVENT_START_OF_IFD:
- * break;
- * case ExifParser.EVENT_NEW_TAG:
- * ExifTag tag = parser.getTag();
- * if (!tag.hasValue()) {
- * parser.registerForTagValue(tag);
- * } else {
- * processTag(tag);
- * }
- * break;
- * case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
- * tag = parser.getTag();
- * if (tag.getDataType() != ExifTag.TYPE_UNDEFINED) {
- * processTag(tag);
- * }
- * break;
- * }
- * event = parser.next();
- * }
- * }
- *
- * void processTag(ExifTag tag) {
- * // process the tag as you like.
- * }
- * </pre>
- */
-class ExifParser {
- private static final boolean LOGV = false;
- private static final String TAG = "ExifParser";
- /**
- * When the parser reaches a new IFD area. Call {@link #getCurrentIfd()} to
- * know which IFD we are in.
- */
- public static final int EVENT_START_OF_IFD = 0;
- /**
- * When the parser reaches a new tag. Call {@link #getTag()}to get the
- * corresponding tag.
- */
- public static final int EVENT_NEW_TAG = 1;
- /**
- * When the parser reaches the value area of tag that is registered by
- * {@link #registerForTagValue(ExifTag)} previously. Call {@link #getTag()}
- * to get the corresponding tag.
- */
- public static final int EVENT_VALUE_OF_REGISTERED_TAG = 2;
-
- /**
- * When the parser reaches the compressed image area.
- */
- public static final int EVENT_COMPRESSED_IMAGE = 3;
- /**
- * When the parser reaches the uncompressed image strip. Call
- * {@link #getStripIndex()} to get the index of the strip.
- *
- * @see #getStripIndex()
- * @see #getStripCount()
- */
- public static final int EVENT_UNCOMPRESSED_STRIP = 4;
- /**
- * When there is nothing more to parse.
- */
- public static final int EVENT_END = 5;
-
- /**
- * Option bit to request to parse IFD0.
- */
- public static final int OPTION_IFD_0 = 1 << 0;
- /**
- * Option bit to request to parse IFD1.
- */
- public static final int OPTION_IFD_1 = 1 << 1;
- /**
- * Option bit to request to parse Exif-IFD.
- */
- public static final int OPTION_IFD_EXIF = 1 << 2;
- /**
- * Option bit to request to parse GPS-IFD.
- */
- public static final int OPTION_IFD_GPS = 1 << 3;
- /**
- * Option bit to request to parse Interoperability-IFD.
- */
- public static final int OPTION_IFD_INTEROPERABILITY = 1 << 4;
- /**
- * Option bit to request to parse thumbnail.
- */
- public static final int OPTION_THUMBNAIL = 1 << 5;
-
- protected static final int EXIF_HEADER = 0x45786966; // EXIF header "Exif"
- protected static final short EXIF_HEADER_TAIL = (short) 0x0000; // EXIF header in APP1
-
- // TIFF header
- protected static final short LITTLE_ENDIAN_TAG = (short) 0x4949; // "II"
- protected static final short BIG_ENDIAN_TAG = (short) 0x4d4d; // "MM"
- protected static final short TIFF_HEADER_TAIL = 0x002A;
-
- protected static final int TAG_SIZE = 12;
- protected static final int OFFSET_SIZE = 2;
-
- private static final Charset US_ASCII = Charset.forName("US-ASCII");
-
- protected static final int DEFAULT_IFD0_OFFSET = 8;
-
- private final CountedDataInputStream mTiffStream;
- private final int mOptions;
- private int mIfdStartOffset = 0;
- private int mNumOfTagInIfd = 0;
- private int mIfdType;
- private ExifTag mTag;
- private ImageEvent mImageEvent;
- private int mStripCount;
- private ExifTag mStripSizeTag;
- private ExifTag mJpegSizeTag;
- private boolean mNeedToParseOffsetsInCurrentIfd;
- private boolean mContainExifData = false;
- private int mApp1End;
- private int mOffsetToApp1EndFromSOF = 0;
- private byte[] mDataAboveIfd0;
- private int mIfd0Position;
- private int mTiffStartPosition;
- private final ExifInterface mInterface;
-
- private static final short TAG_EXIF_IFD = ExifInterface
- .getTrueTagKey(ExifInterface.TAG_EXIF_IFD);
- private static final short TAG_GPS_IFD = ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD);
- private static final short TAG_INTEROPERABILITY_IFD = ExifInterface
- .getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD);
- private static final short TAG_JPEG_INTERCHANGE_FORMAT = ExifInterface
- .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT);
- private static final short TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = ExifInterface
- .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
- private static final short TAG_STRIP_OFFSETS = ExifInterface
- .getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS);
- private static final short TAG_STRIP_BYTE_COUNTS = ExifInterface
- .getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS);
-
- private final TreeMap<Integer, Object> mCorrespondingEvent = new TreeMap<Integer, Object>();
-
- private boolean isIfdRequested(int ifdType) {
- switch (ifdType) {
- case IfdId.TYPE_IFD_0:
- return (mOptions & OPTION_IFD_0) != 0;
- case IfdId.TYPE_IFD_1:
- return (mOptions & OPTION_IFD_1) != 0;
- case IfdId.TYPE_IFD_EXIF:
- return (mOptions & OPTION_IFD_EXIF) != 0;
- case IfdId.TYPE_IFD_GPS:
- return (mOptions & OPTION_IFD_GPS) != 0;
- case IfdId.TYPE_IFD_INTEROPERABILITY:
- return (mOptions & OPTION_IFD_INTEROPERABILITY) != 0;
- }
- return false;
- }
-
- private boolean isThumbnailRequested() {
- return (mOptions & OPTION_THUMBNAIL) != 0;
- }
-
- private ExifParser(InputStream inputStream, int options, ExifInterface iRef)
- throws IOException, ExifInvalidFormatException {
- if (inputStream == null) {
- throw new IOException("Null argument inputStream to ExifParser");
- }
- if (LOGV) {
- Log.v(TAG, "Reading exif...");
- }
- mInterface = iRef;
- mContainExifData = seekTiffData(inputStream);
- mTiffStream = new CountedDataInputStream(inputStream);
- mOptions = options;
- if (!mContainExifData) {
- return;
- }
-
- parseTiffHeader();
- long offset = mTiffStream.readUnsignedInt();
- if (offset > Integer.MAX_VALUE) {
- throw new ExifInvalidFormatException("Invalid offset " + offset);
- }
- mIfd0Position = (int) offset;
- mIfdType = IfdId.TYPE_IFD_0;
- if (isIfdRequested(IfdId.TYPE_IFD_0) || needToParseOffsetsInCurrentIfd()) {
- registerIfd(IfdId.TYPE_IFD_0, offset);
- if (offset != DEFAULT_IFD0_OFFSET) {
- mDataAboveIfd0 = new byte[(int) offset - DEFAULT_IFD0_OFFSET];
- read(mDataAboveIfd0);
- }
- }
- }
-
- /**
- * Parses the the given InputStream with the given options
- *
- * @exception IOException
- * @exception ExifInvalidFormatException
- */
- protected static ExifParser parse(InputStream inputStream, int options, ExifInterface iRef)
- throws IOException, ExifInvalidFormatException {
- return new ExifParser(inputStream, options, iRef);
- }
-
- /**
- * Parses the the given InputStream with default options; that is, every IFD
- * and thumbnaill will be parsed.
- *
- * @exception IOException
- * @exception ExifInvalidFormatException
- * @see #parse(InputStream, int)
- */
- protected static ExifParser parse(InputStream inputStream, ExifInterface iRef)
- throws IOException, ExifInvalidFormatException {
- return new ExifParser(inputStream, OPTION_IFD_0 | OPTION_IFD_1
- | OPTION_IFD_EXIF | OPTION_IFD_GPS | OPTION_IFD_INTEROPERABILITY
- | OPTION_THUMBNAIL, iRef);
- }
-
- /**
- * Moves the parser forward and returns the next parsing event
- *
- * @exception IOException
- * @exception ExifInvalidFormatException
- * @see #EVENT_START_OF_IFD
- * @see #EVENT_NEW_TAG
- * @see #EVENT_VALUE_OF_REGISTERED_TAG
- * @see #EVENT_COMPRESSED_IMAGE
- * @see #EVENT_UNCOMPRESSED_STRIP
- * @see #EVENT_END
- */
- protected int next() throws IOException, ExifInvalidFormatException {
- if (!mContainExifData) {
- return EVENT_END;
- }
- int offset = mTiffStream.getReadByteCount();
- int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd;
- if (offset < endOfTags) {
- mTag = readTag();
- if (mTag == null) {
- return next();
- }
- if (mNeedToParseOffsetsInCurrentIfd) {
- checkOffsetOrImageTag(mTag);
- }
- return EVENT_NEW_TAG;
- } else if (offset == endOfTags) {
- // There is a link to ifd1 at the end of ifd0
- if (mIfdType == IfdId.TYPE_IFD_0) {
- long ifdOffset = readUnsignedLong();
- if (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested()) {
- if (ifdOffset != 0) {
- registerIfd(IfdId.TYPE_IFD_1, ifdOffset);
- }
- }
- } else {
- int offsetSize = 4;
- // Some camera models use invalid length of the offset
- if (mCorrespondingEvent.size() > 0) {
- offsetSize = mCorrespondingEvent.firstEntry().getKey() -
- mTiffStream.getReadByteCount();
- }
- if (offsetSize < 4) {
- Log.w(TAG, "Invalid size of link to next IFD: " + offsetSize);
- } else {
- long ifdOffset = readUnsignedLong();
- if (ifdOffset != 0) {
- Log.w(TAG, "Invalid link to next IFD: " + ifdOffset);
- }
- }
- }
- }
- while (mCorrespondingEvent.size() != 0) {
- Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry();
- Object event = entry.getValue();
- try {
- skipTo(entry.getKey());
- } catch (IOException e) {
- Log.w(TAG, "Failed to skip to data at: " + entry.getKey() +
- " for " + event.getClass().getName() + ", the file may be broken.");
- continue;
- }
- if (event instanceof IfdEvent) {
- mIfdType = ((IfdEvent) event).ifd;
- mNumOfTagInIfd = mTiffStream.readUnsignedShort();
- mIfdStartOffset = entry.getKey();
-
- if (mNumOfTagInIfd * TAG_SIZE + mIfdStartOffset + OFFSET_SIZE > mApp1End) {
- Log.w(TAG, "Invalid size of IFD " + mIfdType);
- return EVENT_END;
- }
-
- mNeedToParseOffsetsInCurrentIfd = needToParseOffsetsInCurrentIfd();
- if (((IfdEvent) event).isRequested) {
- return EVENT_START_OF_IFD;
- } else {
- skipRemainingTagsInCurrentIfd();
- }
- } else if (event instanceof ImageEvent) {
- mImageEvent = (ImageEvent) event;
- return mImageEvent.type;
- } else {
- ExifTagEvent tagEvent = (ExifTagEvent) event;
- mTag = tagEvent.tag;
- if (mTag.getDataType() != ExifTag.TYPE_UNDEFINED) {
- readFullTagValue(mTag);
- checkOffsetOrImageTag(mTag);
- }
- if (tagEvent.isRequested) {
- return EVENT_VALUE_OF_REGISTERED_TAG;
- }
- }
- }
- return EVENT_END;
- }
-
- /**
- * Skips the tags area of current IFD, if the parser is not in the tag area,
- * nothing will happen.
- *
- * @throws IOException
- * @throws ExifInvalidFormatException
- */
- protected void skipRemainingTagsInCurrentIfd() throws IOException, ExifInvalidFormatException {
- int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd;
- int offset = mTiffStream.getReadByteCount();
- if (offset > endOfTags) {
- return;
- }
- if (mNeedToParseOffsetsInCurrentIfd) {
- while (offset < endOfTags) {
- mTag = readTag();
- offset += TAG_SIZE;
- if (mTag == null) {
- continue;
- }
- checkOffsetOrImageTag(mTag);
- }
- } else {
- skipTo(endOfTags);
- }
- long ifdOffset = readUnsignedLong();
- // For ifd0, there is a link to ifd1 in the end of all tags
- if (mIfdType == IfdId.TYPE_IFD_0
- && (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested())) {
- if (ifdOffset > 0) {
- registerIfd(IfdId.TYPE_IFD_1, ifdOffset);
- }
- }
- }
-
- private boolean needToParseOffsetsInCurrentIfd() {
- switch (mIfdType) {
- case IfdId.TYPE_IFD_0:
- return isIfdRequested(IfdId.TYPE_IFD_EXIF) || isIfdRequested(IfdId.TYPE_IFD_GPS)
- || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)
- || isIfdRequested(IfdId.TYPE_IFD_1);
- case IfdId.TYPE_IFD_1:
- return isThumbnailRequested();
- case IfdId.TYPE_IFD_EXIF:
- // The offset to interoperability IFD is located in Exif IFD
- return isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY);
- default:
- return false;
- }
- }
-
- /**
- * If {@link #next()} return {@link #EVENT_NEW_TAG} or
- * {@link #EVENT_VALUE_OF_REGISTERED_TAG}, call this function to get the
- * corresponding tag.
- * <p>
- * For {@link #EVENT_NEW_TAG}, the tag may not contain the value if the size
- * of the value is greater than 4 bytes. One should call
- * {@link ExifTag#hasValue()} to check if the tag contains value. If there
- * is no value,call {@link #registerForTagValue(ExifTag)} to have the parser
- * emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area
- * pointed by the offset.
- * <p>
- * When {@link #EVENT_VALUE_OF_REGISTERED_TAG} is emitted, the value of the
- * tag will have already been read except for tags of undefined type. For
- * tags of undefined type, call one of the read methods to get the value.
- *
- * @see #registerForTagValue(ExifTag)
- * @see #read(byte[])
- * @see #read(byte[], int, int)
- * @see #readLong()
- * @see #readRational()
- * @see #readString(int)
- * @see #readString(int, Charset)
- */
- protected ExifTag getTag() {
- return mTag;
- }
-
- /**
- * Gets number of tags in the current IFD area.
- */
- protected int getTagCountInCurrentIfd() {
- return mNumOfTagInIfd;
- }
-
- /**
- * Gets the ID of current IFD.
- *
- * @see IfdId#TYPE_IFD_0
- * @see IfdId#TYPE_IFD_1
- * @see IfdId#TYPE_IFD_GPS
- * @see IfdId#TYPE_IFD_INTEROPERABILITY
- * @see IfdId#TYPE_IFD_EXIF
- */
- protected int getCurrentIfd() {
- return mIfdType;
- }
-
- /**
- * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
- * get the index of this strip.
- *
- * @see #getStripCount()
- */
- protected int getStripIndex() {
- return mImageEvent.stripIndex;
- }
-
- /**
- * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
- * get the number of strip data.
- *
- * @see #getStripIndex()
- */
- protected int getStripCount() {
- return mStripCount;
- }
-
- /**
- * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
- * get the strip size.
- */
- protected int getStripSize() {
- if (mStripSizeTag == null)
- return 0;
- return (int) mStripSizeTag.getValueAt(0);
- }
-
- /**
- * When receiving {@link #EVENT_COMPRESSED_IMAGE}, call this function to get
- * the image data size.
- */
- protected int getCompressedImageSize() {
- if (mJpegSizeTag == null) {
- return 0;
- }
- return (int) mJpegSizeTag.getValueAt(0);
- }
-
- private void skipTo(int offset) throws IOException {
- mTiffStream.skipTo(offset);
- while (!mCorrespondingEvent.isEmpty() && mCorrespondingEvent.firstKey() < offset) {
- mCorrespondingEvent.pollFirstEntry();
- }
- }
-
- /**
- * When getting {@link #EVENT_NEW_TAG} in the tag area of IFD, the tag may
- * not contain the value if the size of the value is greater than 4 bytes.
- * When the value is not available here, call this method so that the parser
- * will emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area
- * where the value is located.
- *
- * @see #EVENT_VALUE_OF_REGISTERED_TAG
- */
- protected void registerForTagValue(ExifTag tag) {
- if (tag.getOffset() >= mTiffStream.getReadByteCount()) {
- mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, true));
- }
- }
-
- private void registerIfd(int ifdType, long offset) {
- // Cast unsigned int to int since the offset is always smaller
- // than the size of APP1 (65536)
- mCorrespondingEvent.put((int) offset, new IfdEvent(ifdType, isIfdRequested(ifdType)));
- }
-
- private void registerCompressedImage(long offset) {
- mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_COMPRESSED_IMAGE));
- }
-
- private void registerUncompressedStrip(int stripIndex, long offset) {
- mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_UNCOMPRESSED_STRIP
- , stripIndex));
- }
-
- private ExifTag readTag() throws IOException, ExifInvalidFormatException {
- short tagId = mTiffStream.readShort();
- short dataFormat = mTiffStream.readShort();
- long numOfComp = mTiffStream.readUnsignedInt();
- if (numOfComp > Integer.MAX_VALUE) {
- throw new ExifInvalidFormatException(
- "Number of component is larger then Integer.MAX_VALUE");
- }
- // Some invalid image file contains invalid data type. Ignore those tags
- if (!ExifTag.isValidType(dataFormat)) {
- Log.w(TAG, String.format("Tag %04x: Invalid data type %d", tagId, dataFormat));
- mTiffStream.skip(4);
- return null;
- }
- // TODO: handle numOfComp overflow
- ExifTag tag = new ExifTag(tagId, dataFormat, (int) numOfComp, mIfdType,
- ((int) numOfComp) != ExifTag.SIZE_UNDEFINED);
- int dataSize = tag.getDataSize();
- if (dataSize > 4) {
- long offset = mTiffStream.readUnsignedInt();
- if (offset > Integer.MAX_VALUE) {
- throw new ExifInvalidFormatException(
- "offset is larger then Integer.MAX_VALUE");
- }
- // Some invalid images put some undefined data before IFD0.
- // Read the data here.
- if ((offset < mIfd0Position) && (dataFormat == ExifTag.TYPE_UNDEFINED)) {
- byte[] buf = new byte[(int) numOfComp];
- System.arraycopy(mDataAboveIfd0, (int) offset - DEFAULT_IFD0_OFFSET,
- buf, 0, (int) numOfComp);
- tag.setValue(buf);
- } else {
- tag.setOffset((int) offset);
- }
- } else {
- boolean defCount = tag.hasDefinedCount();
- // Set defined count to 0 so we can add \0 to non-terminated strings
- tag.setHasDefinedCount(false);
- // Read value
- readFullTagValue(tag);
- tag.setHasDefinedCount(defCount);
- mTiffStream.skip(4 - dataSize);
- // Set the offset to the position of value.
- tag.setOffset(mTiffStream.getReadByteCount() - 4);
- }
- return tag;
- }
-
- /**
- * Check the tag, if the tag is one of the offset tag that points to the IFD
- * or image the caller is interested in, register the IFD or image.
- */
- private void checkOffsetOrImageTag(ExifTag tag) {
- // Some invalid formattd image contains tag with 0 size.
- if (tag.getComponentCount() == 0) {
- return;
- }
- short tid = tag.getTagId();
- int ifd = tag.getIfd();
- if (tid == TAG_EXIF_IFD && checkAllowed(ifd, ExifInterface.TAG_EXIF_IFD)) {
- if (isIfdRequested(IfdId.TYPE_IFD_EXIF)
- || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
- registerIfd(IfdId.TYPE_IFD_EXIF, tag.getValueAt(0));
- }
- } else if (tid == TAG_GPS_IFD && checkAllowed(ifd, ExifInterface.TAG_GPS_IFD)) {
- if (isIfdRequested(IfdId.TYPE_IFD_GPS)) {
- registerIfd(IfdId.TYPE_IFD_GPS, tag.getValueAt(0));
- }
- } else if (tid == TAG_INTEROPERABILITY_IFD
- && checkAllowed(ifd, ExifInterface.TAG_INTEROPERABILITY_IFD)) {
- if (isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
- registerIfd(IfdId.TYPE_IFD_INTEROPERABILITY, tag.getValueAt(0));
- }
- } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT
- && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)) {
- if (isThumbnailRequested()) {
- registerCompressedImage(tag.getValueAt(0));
- }
- } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT_LENGTH
- && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)) {
- if (isThumbnailRequested()) {
- mJpegSizeTag = tag;
- }
- } else if (tid == TAG_STRIP_OFFSETS && checkAllowed(ifd, ExifInterface.TAG_STRIP_OFFSETS)) {
- if (isThumbnailRequested()) {
- if (tag.hasValue()) {
- for (int i = 0; i < tag.getComponentCount(); i++) {
- if (tag.getDataType() == ExifTag.TYPE_UNSIGNED_SHORT) {
- registerUncompressedStrip(i, tag.getValueAt(i));
- } else {
- registerUncompressedStrip(i, tag.getValueAt(i));
- }
- }
- } else {
- mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, false));
- }
- }
- } else if (tid == TAG_STRIP_BYTE_COUNTS
- && checkAllowed(ifd, ExifInterface.TAG_STRIP_BYTE_COUNTS)
- &&isThumbnailRequested() && tag.hasValue()) {
- mStripSizeTag = tag;
- }
- }
-
- private boolean checkAllowed(int ifd, int tagId) {
- int info = mInterface.getTagInfo().get(tagId);
- if (info == ExifInterface.DEFINITION_NULL) {
- return false;
- }
- return ExifInterface.isIfdAllowed(info, ifd);
- }
-
- protected void readFullTagValue(ExifTag tag) throws IOException {
- // Some invalid images contains tags with wrong size, check it here
- short type = tag.getDataType();
- if (type == ExifTag.TYPE_ASCII || type == ExifTag.TYPE_UNDEFINED ||
- type == ExifTag.TYPE_UNSIGNED_BYTE) {
- int size = tag.getComponentCount();
- if (mCorrespondingEvent.size() > 0) {
- if (mCorrespondingEvent.firstEntry().getKey() < mTiffStream.getReadByteCount()
- + size) {
- Object event = mCorrespondingEvent.firstEntry().getValue();
- if (event instanceof ImageEvent) {
- // Tag value overlaps thumbnail, ignore thumbnail.
- Log.w(TAG, "Thumbnail overlaps value for tag: \n" + tag.toString());
- Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry();
- Log.w(TAG, "Invalid thumbnail offset: " + entry.getKey());
- } else {
- // Tag value overlaps another tag, shorten count
- if (event instanceof IfdEvent) {
- Log.w(TAG, "Ifd " + ((IfdEvent) event).ifd
- + " overlaps value for tag: \n" + tag.toString());
- } else if (event instanceof ExifTagEvent) {
- Log.w(TAG, "Tag value for tag: \n"
- + ((ExifTagEvent) event).tag.toString()
- + " overlaps value for tag: \n" + tag.toString());
- }
- size = mCorrespondingEvent.firstEntry().getKey()
- - mTiffStream.getReadByteCount();
- Log.w(TAG, "Invalid size of tag: \n" + tag.toString()
- + " setting count to: " + size);
- tag.forceSetComponentCount(size);
- }
- }
- }
- }
- switch (tag.getDataType()) {
- case ExifTag.TYPE_UNSIGNED_BYTE:
- case ExifTag.TYPE_UNDEFINED: {
- byte buf[] = new byte[tag.getComponentCount()];
- read(buf);
- tag.setValue(buf);
- }
- break;
- case ExifTag.TYPE_ASCII:
- tag.setValue(readString(tag.getComponentCount()));
- break;
- case ExifTag.TYPE_UNSIGNED_LONG: {
- long value[] = new long[tag.getComponentCount()];
- for (int i = 0, n = value.length; i < n; i++) {
- value[i] = readUnsignedLong();
- }
- tag.setValue(value);
- }
- break;
- case ExifTag.TYPE_UNSIGNED_RATIONAL: {
- Rational value[] = new Rational[tag.getComponentCount()];
- for (int i = 0, n = value.length; i < n; i++) {
- value[i] = readUnsignedRational();
- }
- tag.setValue(value);
- }
- break;
- case ExifTag.TYPE_UNSIGNED_SHORT: {
- int value[] = new int[tag.getComponentCount()];
- for (int i = 0, n = value.length; i < n; i++) {
- value[i] = readUnsignedShort();
- }
- tag.setValue(value);
- }
- break;
- case ExifTag.TYPE_LONG: {
- int value[] = new int[tag.getComponentCount()];
- for (int i = 0, n = value.length; i < n; i++) {
- value[i] = readLong();
- }
- tag.setValue(value);
- }
- break;
- case ExifTag.TYPE_RATIONAL: {
- Rational value[] = new Rational[tag.getComponentCount()];
- for (int i = 0, n = value.length; i < n; i++) {
- value[i] = readRational();
- }
- tag.setValue(value);
- }
- break;
- }
- if (LOGV) {
- Log.v(TAG, "\n" + tag.toString());
- }
- }
-
- private void parseTiffHeader() throws IOException,
- ExifInvalidFormatException {
- short byteOrder = mTiffStream.readShort();
- if (LITTLE_ENDIAN_TAG == byteOrder) {
- mTiffStream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
- } else if (BIG_ENDIAN_TAG == byteOrder) {
- mTiffStream.setByteOrder(ByteOrder.BIG_ENDIAN);
- } else {
- throw new ExifInvalidFormatException("Invalid TIFF header");
- }
-
- if (mTiffStream.readShort() != TIFF_HEADER_TAIL) {
- throw new ExifInvalidFormatException("Invalid TIFF header");
- }
- }
-
- private boolean seekTiffData(InputStream inputStream) throws IOException,
- ExifInvalidFormatException {
- CountedDataInputStream dataStream = new CountedDataInputStream(inputStream);
- if (dataStream.readShort() != JpegHeader.SOI) {
- throw new ExifInvalidFormatException("Invalid JPEG format");
- }
-
- short marker = dataStream.readShort();
- while (marker != JpegHeader.EOI
- && !JpegHeader.isSofMarker(marker)) {
- int length = dataStream.readUnsignedShort();
- // Some invalid formatted image contains multiple APP1,
- // try to find the one with Exif data.
- if (marker == JpegHeader.APP1) {
- int header = 0;
- short headerTail = 0;
- if (length >= 8) {
- header = dataStream.readInt();
- headerTail = dataStream.readShort();
- length -= 6;
- if (header == EXIF_HEADER && headerTail == EXIF_HEADER_TAIL) {
- mTiffStartPosition = dataStream.getReadByteCount();
- mApp1End = length;
- mOffsetToApp1EndFromSOF = mTiffStartPosition + mApp1End;
- return true;
- }
- }
- }
- if (length < 2 || (length - 2) != dataStream.skip(length - 2)) {
- Log.w(TAG, "Invalid JPEG format.");
- return false;
- }
- marker = dataStream.readShort();
- }
- return false;
- }
-
- protected int getOffsetToExifEndFromSOF() {
- return mOffsetToApp1EndFromSOF;
- }
-
- protected int getTiffStartPosition() {
- return mTiffStartPosition;
- }
-
- /**
- * Reads bytes from the InputStream.
- */
- protected int read(byte[] buffer, int offset, int length) throws IOException {
- return mTiffStream.read(buffer, offset, length);
- }
-
- /**
- * Equivalent to read(buffer, 0, buffer.length).
- */
- protected int read(byte[] buffer) throws IOException {
- return mTiffStream.read(buffer);
- }
-
- /**
- * Reads a String from the InputStream with US-ASCII charset. The parser
- * will read n bytes and convert it to ascii string. This is used for
- * reading values of type {@link ExifTag#TYPE_ASCII}.
- */
- protected String readString(int n) throws IOException {
- return readString(n, US_ASCII);
- }
-
- /**
- * Reads a String from the InputStream with the given charset. The parser
- * will read n bytes and convert it to string. This is used for reading
- * values of type {@link ExifTag#TYPE_ASCII}.
- */
- protected String readString(int n, Charset charset) throws IOException {
- if (n > 0) {
- return mTiffStream.readString(n, charset);
- } else {
- return "";
- }
- }
-
- /**
- * Reads value of type {@link ExifTag#TYPE_UNSIGNED_SHORT} from the
- * InputStream.
- */
- protected int readUnsignedShort() throws IOException {
- return mTiffStream.readShort() & 0xffff;
- }
-
- /**
- * Reads value of type {@link ExifTag#TYPE_UNSIGNED_LONG} from the
- * InputStream.
- */
- protected long readUnsignedLong() throws IOException {
- return readLong() & 0xffffffffL;
- }
-
- /**
- * Reads value of type {@link ExifTag#TYPE_UNSIGNED_RATIONAL} from the
- * InputStream.
- */
- protected Rational readUnsignedRational() throws IOException {
- long nomi = readUnsignedLong();
- long denomi = readUnsignedLong();
- return new Rational(nomi, denomi);
- }
-
- /**
- * Reads value of type {@link ExifTag#TYPE_LONG} from the InputStream.
- */
- protected int readLong() throws IOException {
- return mTiffStream.readInt();
- }
-
- /**
- * Reads value of type {@link ExifTag#TYPE_RATIONAL} from the InputStream.
- */
- protected Rational readRational() throws IOException {
- int nomi = readLong();
- int denomi = readLong();
- return new Rational(nomi, denomi);
- }
-
- private static class ImageEvent {
- int stripIndex;
- int type;
-
- ImageEvent(int type) {
- this.stripIndex = 0;
- this.type = type;
- }
-
- ImageEvent(int type, int stripIndex) {
- this.type = type;
- this.stripIndex = stripIndex;
- }
- }
-
- private static class IfdEvent {
- int ifd;
- boolean isRequested;
-
- IfdEvent(int ifd, boolean isInterestedIfd) {
- this.ifd = ifd;
- this.isRequested = isInterestedIfd;
- }
- }
-
- private static class ExifTagEvent {
- ExifTag tag;
- boolean isRequested;
-
- ExifTagEvent(ExifTag tag, boolean isRequireByUser) {
- this.tag = tag;
- this.isRequested = isRequireByUser;
- }
- }
-
- /**
- * Gets the byte order of the current InputStream.
- */
- protected ByteOrder getByteOrder() {
- return mTiffStream.getByteOrder();
- }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ExifReader.java b/WallpaperPicker/src/com/android/gallery3d/exif/ExifReader.java
deleted file mode 100644
index 68e972fb7..000000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ExifReader.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2012 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.gallery3d.exif;
-
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * This class reads the EXIF header of a JPEG file and stores it in
- * {@link ExifData}.
- */
-class ExifReader {
- private static final String TAG = "ExifReader";
-
- private final ExifInterface mInterface;
-
- ExifReader(ExifInterface iRef) {
- mInterface = iRef;
- }
-
- /**
- * Parses the inputStream and and returns the EXIF data in an
- * {@link ExifData}.
- *
- * @throws ExifInvalidFormatException
- * @throws IOException
- */
- protected ExifData read(InputStream inputStream) throws ExifInvalidFormatException,
- IOException {
- ExifParser parser = ExifParser.parse(inputStream, mInterface);
- ExifData exifData = new ExifData(parser.getByteOrder());
- ExifTag tag = null;
-
- int event = parser.next();
- while (event != ExifParser.EVENT_END) {
- switch (event) {
- case ExifParser.EVENT_START_OF_IFD:
- exifData.addIfdData(new IfdData(parser.getCurrentIfd()));
- break;
- case ExifParser.EVENT_NEW_TAG:
- tag = parser.getTag();
- if (!tag.hasValue()) {
- parser.registerForTagValue(tag);
- } else {
- exifData.getIfdData(tag.getIfd()).setTag(tag);
- }
- break;
- case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
- tag = parser.getTag();
- if (tag.getDataType() == ExifTag.TYPE_UNDEFINED) {
- parser.readFullTagValue(tag);
- }
- exifData.getIfdData(tag.getIfd()).setTag(tag);
- break;
- case ExifParser.EVENT_COMPRESSED_IMAGE:
- byte buf[] = new byte[parser.getCompressedImageSize()];
- if (buf.length == parser.read(buf)) {
- exifData.setCompressedThumbnail(buf);
- } else {
- Log.w(TAG, "Failed to read the compressed thumbnail");
- }
- break;
- case ExifParser.EVENT_UNCOMPRESSED_STRIP:
- buf = new byte[parser.getStripSize()];
- if (buf.length == parser.read(buf)) {
- exifData.setStripBytes(parser.getStripIndex(), buf);
- } else {
- Log.w(TAG, "Failed to read the strip bytes");
- }
- break;
- }
- event = parser.next();
- }
- return exifData;
- }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ExifTag.java b/WallpaperPicker/src/com/android/gallery3d/exif/ExifTag.java
deleted file mode 100644
index b8b387201..000000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ExifTag.java
+++ /dev/null
@@ -1,1008 +0,0 @@
-/*
- * Copyright (C) 2012 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.gallery3d.exif;
-
-import java.nio.charset.Charset;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Date;
-
-/**
- * This class stores information of an EXIF tag. For more information about
- * defined EXIF tags, please read the Jeita EXIF 2.2 standard. Tags should be
- * instantiated using {@link ExifInterface#buildTag}.
- *
- * @see ExifInterface
- */
-public class ExifTag {
- /**
- * The BYTE type in the EXIF standard. An 8-bit unsigned integer.
- */
- public static final short TYPE_UNSIGNED_BYTE = 1;
- /**
- * The ASCII type in the EXIF standard. An 8-bit byte containing one 7-bit
- * ASCII code. The final byte is terminated with NULL.
- */
- public static final short TYPE_ASCII = 2;
- /**
- * The SHORT type in the EXIF standard. A 16-bit (2-byte) unsigned integer
- */
- public static final short TYPE_UNSIGNED_SHORT = 3;
- /**
- * The LONG type in the EXIF standard. A 32-bit (4-byte) unsigned integer
- */
- public static final short TYPE_UNSIGNED_LONG = 4;
- /**
- * The RATIONAL type of EXIF standard. It consists of two LONGs. The first
- * one is the numerator and the second one expresses the denominator.
- */
- public static final short TYPE_UNSIGNED_RATIONAL = 5;
- /**
- * The UNDEFINED type in the EXIF standard. An 8-bit byte that can take any
- * value depending on the field definition.
- */
- public static final short TYPE_UNDEFINED = 7;
- /**
- * The SLONG type in the EXIF standard. A 32-bit (4-byte) signed integer
- * (2's complement notation).
- */
- public static final short TYPE_LONG = 9;
- /**
- * The SRATIONAL type of EXIF standard. It consists of two SLONGs. The first
- * one is the numerator and the second one is the denominator.
- */
- public static final short TYPE_RATIONAL = 10;
-
- private static Charset US_ASCII = Charset.forName("US-ASCII");
- private static final int TYPE_TO_SIZE_MAP[] = new int[11];
- private static final int UNSIGNED_SHORT_MAX = 65535;
- private static final long UNSIGNED_LONG_MAX = 4294967295L;
- private static final long LONG_MAX = Integer.MAX_VALUE;
- private static final long LONG_MIN = Integer.MIN_VALUE;
-
- static {
- TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_BYTE] = 1;
- TYPE_TO_SIZE_MAP[TYPE_ASCII] = 1;
- TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_SHORT] = 2;
- TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_LONG] = 4;
- TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_RATIONAL] = 8;
- TYPE_TO_SIZE_MAP[TYPE_UNDEFINED] = 1;
- TYPE_TO_SIZE_MAP[TYPE_LONG] = 4;
- TYPE_TO_SIZE_MAP[TYPE_RATIONAL] = 8;
- }
-
- static final int SIZE_UNDEFINED = 0;
-
- // Exif TagId
- private final short mTagId;
- // Exif Tag Type
- private final short mDataType;
- // If tag has defined count
- private boolean mHasDefinedDefaultComponentCount;
- // Actual data count in tag (should be number of elements in value array)
- private int mComponentCountActual;
- // The ifd that this tag should be put in
- private int mIfd;
- // The value (array of elements of type Tag Type)
- private Object mValue;
- // Value offset in exif header.
- private int mOffset;
-
- private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("yyyy:MM:dd kk:mm:ss");
-
- /**
- * Returns true if the given IFD is a valid IFD.
- */
- public static boolean isValidIfd(int ifdId) {
- return ifdId == IfdId.TYPE_IFD_0 || ifdId == IfdId.TYPE_IFD_1
- || ifdId == IfdId.TYPE_IFD_EXIF || ifdId == IfdId.TYPE_IFD_INTEROPERABILITY
- || ifdId == IfdId.TYPE_IFD_GPS;
- }
-
- /**
- * Returns true if a given type is a valid tag type.
- */
- public static boolean isValidType(short type) {
- return type == TYPE_UNSIGNED_BYTE || type == TYPE_ASCII ||
- type == TYPE_UNSIGNED_SHORT || type == TYPE_UNSIGNED_LONG ||
- type == TYPE_UNSIGNED_RATIONAL || type == TYPE_UNDEFINED ||
- type == TYPE_LONG || type == TYPE_RATIONAL;
- }
-
- // Use builtTag in ExifInterface instead of constructor.
- ExifTag(short tagId, short type, int componentCount, int ifd,
- boolean hasDefinedComponentCount) {
- mTagId = tagId;
- mDataType = type;
- mComponentCountActual = componentCount;
- mHasDefinedDefaultComponentCount = hasDefinedComponentCount;
- mIfd = ifd;
- mValue = null;
- }
-
- /**
- * Gets the element size of the given data type in bytes.
- *
- * @see #TYPE_ASCII
- * @see #TYPE_LONG
- * @see #TYPE_RATIONAL
- * @see #TYPE_UNDEFINED
- * @see #TYPE_UNSIGNED_BYTE
- * @see #TYPE_UNSIGNED_LONG
- * @see #TYPE_UNSIGNED_RATIONAL
- * @see #TYPE_UNSIGNED_SHORT
- */
- public static int getElementSize(short type) {
- return TYPE_TO_SIZE_MAP[type];
- }
-
- /**
- * Returns the ID of the IFD this tag belongs to.
- *
- * @see IfdId#TYPE_IFD_0
- * @see IfdId#TYPE_IFD_1
- * @see IfdId#TYPE_IFD_EXIF
- * @see IfdId#TYPE_IFD_GPS
- * @see IfdId#TYPE_IFD_INTEROPERABILITY
- */
- public int getIfd() {
- return mIfd;
- }
-
- protected void setIfd(int ifdId) {
- mIfd = ifdId;
- }
-
- /**
- * Gets the TID of this tag.
- */
- public short getTagId() {
- return mTagId;
- }
-
- /**
- * Gets the data type of this tag
- *
- * @see #TYPE_ASCII
- * @see #TYPE_LONG
- * @see #TYPE_RATIONAL
- * @see #TYPE_UNDEFINED
- * @see #TYPE_UNSIGNED_BYTE
- * @see #TYPE_UNSIGNED_LONG
- * @see #TYPE_UNSIGNED_RATIONAL
- * @see #TYPE_UNSIGNED_SHORT
- */
- public short getDataType() {
- return mDataType;
- }
-
- /**
- * Gets the total data size in bytes of the value of this tag.
- */
- public int getDataSize() {
- return getComponentCount() * getElementSize(getDataType());
- }
-
- /**
- * Gets the component count of this tag.
- */
-
- // TODO: fix integer overflows with this
- public int getComponentCount() {
- return mComponentCountActual;
- }
-
- /**
- * Sets the component count of this tag. Call this function before
- * setValue() if the length of value does not match the component count.
- */
- protected void forceSetComponentCount(int count) {
- mComponentCountActual = count;
- }
-
- /**
- * Returns true if this ExifTag contains value; otherwise, this tag will
- * contain an offset value that is determined when the tag is written.
- */
- public boolean hasValue() {
- return mValue != null;
- }
-
- /**
- * Sets integer values into this tag. This method should be used for tags of
- * type {@link #TYPE_UNSIGNED_SHORT}. This method will fail if:
- * <ul>
- * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_SHORT},
- * {@link #TYPE_UNSIGNED_LONG}, or {@link #TYPE_LONG}.</li>
- * <li>The value overflows.</li>
- * <li>The value.length does NOT match the component count in the definition
- * for this tag.</li>
- * </ul>
- */
- public boolean setValue(int[] value) {
- if (checkBadComponentCount(value.length)) {
- return false;
- }
- if (mDataType != TYPE_UNSIGNED_SHORT && mDataType != TYPE_LONG &&
- mDataType != TYPE_UNSIGNED_LONG) {
- return false;
- }
- if (mDataType == TYPE_UNSIGNED_SHORT && checkOverflowForUnsignedShort(value)) {
- return false;
- } else if (mDataType == TYPE_UNSIGNED_LONG && checkOverflowForUnsignedLong(value)) {
- return false;
- }
-
- long[] data = new long[value.length];
- for (int i = 0; i < value.length; i++) {
- data[i] = value[i];
- }
- mValue = data;
- mComponentCountActual = value.length;
- return true;
- }
-
- /**
- * Sets integer value into this tag. This method should be used for tags of
- * type {@link #TYPE_UNSIGNED_SHORT}, or {@link #TYPE_LONG}. This method
- * will fail if:
- * <ul>
- * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_SHORT},
- * {@link #TYPE_UNSIGNED_LONG}, or {@link #TYPE_LONG}.</li>
- * <li>The value overflows.</li>
- * <li>The component count in the definition of this tag is not 1.</li>
- * </ul>
- */
- public boolean setValue(int value) {
- return setValue(new int[] {
- value
- });
- }
-
- /**
- * Sets long values into this tag. This method should be used for tags of
- * type {@link #TYPE_UNSIGNED_LONG}. This method will fail if:
- * <ul>
- * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_LONG}.</li>
- * <li>The value overflows.</li>
- * <li>The value.length does NOT match the component count in the definition
- * for this tag.</li>
- * </ul>
- */
- public boolean setValue(long[] value) {
- if (checkBadComponentCount(value.length) || mDataType != TYPE_UNSIGNED_LONG) {
- return false;
- }
- if (checkOverflowForUnsignedLong(value)) {
- return false;
- }
- mValue = value;
- mComponentCountActual = value.length;
- return true;
- }
-
- /**
- * Sets long values into this tag. This method should be used for tags of
- * type {@link #TYPE_UNSIGNED_LONG}. This method will fail if:
- * <ul>
- * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_LONG}.</li>
- * <li>The value overflows.</li>
- * <li>The component count in the definition for this tag is not 1.</li>
- * </ul>
- */
- public boolean setValue(long value) {
- return setValue(new long[] {
- value
- });
- }
-
- /**
- * Sets a string value into this tag. This method should be used for tags of
- * type {@link #TYPE_ASCII}. The string is converted to an ASCII string.
- * Characters that cannot be converted are replaced with '?'. The length of
- * the string must be equal to either (component count -1) or (component
- * count). The final byte will be set to the string null terminator '\0',
- * overwriting the last character in the string if the value.length is equal
- * to the component count. This method will fail if:
- * <ul>
- * <li>The data type is not {@link #TYPE_ASCII} or {@link #TYPE_UNDEFINED}.</li>
- * <li>The length of the string is not equal to (component count -1) or
- * (component count) in the definition for this tag.</li>
- * </ul>
- */
- public boolean setValue(String value) {
- if (mDataType != TYPE_ASCII && mDataType != TYPE_UNDEFINED) {
- return false;
- }
-
- byte[] buf = value.getBytes(US_ASCII);
- byte[] finalBuf = buf;
- if (buf.length > 0) {
- finalBuf = (buf[buf.length - 1] == 0 || mDataType == TYPE_UNDEFINED) ? buf : Arrays
- .copyOf(buf, buf.length + 1);
- } else if (mDataType == TYPE_ASCII && mComponentCountActual == 1) {
- finalBuf = new byte[] { 0 };
- }
- int count = finalBuf.length;
- if (checkBadComponentCount(count)) {
- return false;
- }
- mComponentCountActual = count;
- mValue = finalBuf;
- return true;
- }
-
- /**
- * Sets Rational values into this tag. This method should be used for tags
- * of type {@link #TYPE_UNSIGNED_RATIONAL}, or {@link #TYPE_RATIONAL}. This
- * method will fail if:
- * <ul>
- * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_RATIONAL}
- * or {@link #TYPE_RATIONAL}.</li>
- * <li>The value overflows.</li>
- * <li>The value.length does NOT match the component count in the definition
- * for this tag.</li>
- * </ul>
- *
- * @see Rational
- */
- public boolean setValue(Rational[] value) {
- if (checkBadComponentCount(value.length)) {
- return false;
- }
- if (mDataType != TYPE_UNSIGNED_RATIONAL && mDataType != TYPE_RATIONAL) {
- return false;
- }
- if (mDataType == TYPE_UNSIGNED_RATIONAL && checkOverflowForUnsignedRational(value)) {
- return false;
- } else if (mDataType == TYPE_RATIONAL && checkOverflowForRational(value)) {
- return false;
- }
-
- mValue = value;
- mComponentCountActual = value.length;
- return true;
- }
-
- /**
- * Sets a Rational value into this tag. This method should be used for tags
- * of type {@link #TYPE_UNSIGNED_RATIONAL}, or {@link #TYPE_RATIONAL}. This
- * method will fail if:
- * <ul>
- * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_RATIONAL}
- * or {@link #TYPE_RATIONAL}.</li>
- * <li>The value overflows.</li>
- * <li>The component count in the definition for this tag is not 1.</li>
- * </ul>
- *
- * @see Rational
- */
- public boolean setValue(Rational value) {
- return setValue(new Rational[] {
- value
- });
- }
-
- /**
- * Sets byte values into this tag. This method should be used for tags of
- * type {@link #TYPE_UNSIGNED_BYTE} or {@link #TYPE_UNDEFINED}. This method
- * will fail if:
- * <ul>
- * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_BYTE} or
- * {@link #TYPE_UNDEFINED} .</li>
- * <li>The length does NOT match the component count in the definition for
- * this tag.</li>
- * </ul>
- */
- public boolean setValue(byte[] value, int offset, int length) {
- if (checkBadComponentCount(length)) {
- return false;
- }
- if (mDataType != TYPE_UNSIGNED_BYTE && mDataType != TYPE_UNDEFINED) {
- return false;
- }
- mValue = new byte[length];
- System.arraycopy(value, offset, mValue, 0, length);
- mComponentCountActual = length;
- return true;
- }
-
- /**
- * Equivalent to setValue(value, 0, value.length).
- */
- public boolean setValue(byte[] value) {
- return setValue(value, 0, value.length);
- }
-
- /**
- * Sets byte value into this tag. This method should be used for tags of
- * type {@link #TYPE_UNSIGNED_BYTE} or {@link #TYPE_UNDEFINED}. This method
- * will fail if:
- * <ul>
- * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_BYTE} or
- * {@link #TYPE_UNDEFINED} .</li>
- * <li>The component count in the definition for this tag is not 1.</li>
- * </ul>
- */
- public boolean setValue(byte value) {
- return setValue(new byte[] {
- value
- });
- }
-
- /**
- * Sets the value for this tag using an appropriate setValue method for the
- * given object. This method will fail if:
- * <ul>
- * <li>The corresponding setValue method for the class of the object passed
- * in would fail.</li>
- * <li>There is no obvious way to cast the object passed in into an EXIF tag
- * type.</li>
- * </ul>
- */
- public boolean setValue(Object obj) {
- if (obj == null) {
- return false;
- } else if (obj instanceof Short) {
- return setValue(((Short) obj).shortValue() & 0x0ffff);
- } else if (obj instanceof String) {
- return setValue((String) obj);
- } else if (obj instanceof int[]) {
- return setValue((int[]) obj);
- } else if (obj instanceof long[]) {
- return setValue((long[]) obj);
- } else if (obj instanceof Rational) {
- return setValue((Rational) obj);
- } else if (obj instanceof Rational[]) {
- return setValue((Rational[]) obj);
- } else if (obj instanceof byte[]) {
- return setValue((byte[]) obj);
- } else if (obj instanceof Integer) {
- return setValue(((Integer) obj).intValue());
- } else if (obj instanceof Long) {
- return setValue(((Long) obj).longValue());
- } else if (obj instanceof Byte) {
- return setValue(((Byte) obj).byteValue());
- } else if (obj instanceof Short[]) {
- // Nulls in this array are treated as zeroes.
- Short[] arr = (Short[]) obj;
- int[] fin = new int[arr.length];
- for (int i = 0; i < arr.length; i++) {
- fin[i] = (arr[i] == null) ? 0 : arr[i].shortValue() & 0x0ffff;
- }
- return setValue(fin);
- } else if (obj instanceof Integer[]) {
- // Nulls in this array are treated as zeroes.
- Integer[] arr = (Integer[]) obj;
- int[] fin = new int[arr.length];
- for (int i = 0; i < arr.length; i++) {
- fin[i] = (arr[i] == null) ? 0 : arr[i].intValue();
- }
- return setValue(fin);
- } else if (obj instanceof Long[]) {
- // Nulls in this array are treated as zeroes.
- Long[] arr = (Long[]) obj;
- long[] fin = new long[arr.length];
- for (int i = 0; i < arr.length; i++) {
- fin[i] = (arr[i] == null) ? 0 : arr[i].longValue();
- }
- return setValue(fin);
- } else if (obj instanceof Byte[]) {
- // Nulls in this array are treated as zeroes.
- Byte[] arr = (Byte[]) obj;
- byte[] fin = new byte[arr.length];
- for (int i = 0; i < arr.length; i++) {
- fin[i] = (arr[i] == null) ? 0 : arr[i].byteValue();
- }
- return setValue(fin);
- } else {
- return false;
- }
- }
-
- /**
- * Sets a timestamp to this tag. The method converts the timestamp with the
- * format of "yyyy:MM:dd kk:mm:ss" and calls {@link #setValue(String)}. This
- * method will fail if the data type is not {@link #TYPE_ASCII} or the
- * component count of this tag is not 20 or undefined.
- *
- * @param time the number of milliseconds since Jan. 1, 1970 GMT
- * @return true on success
- */
- public boolean setTimeValue(long time) {
- // synchronized on TIME_FORMAT as SimpleDateFormat is not thread safe
- synchronized (TIME_FORMAT) {
- return setValue(TIME_FORMAT.format(new Date(time)));
- }
- }
-
- /**
- * Gets the value as a String. This method should be used for tags of type
- * {@link #TYPE_ASCII}.
- *
- * @return the value as a String, or null if the tag's value does not exist
- * or cannot be converted to a String.
- */
- public String getValueAsString() {
- if (mValue == null) {
- return null;
- } else if (mValue instanceof String) {
- return (String) mValue;
- } else if (mValue instanceof byte[]) {
- return new String((byte[]) mValue, US_ASCII);
- }
- return null;
- }
-
- /**
- * Gets the value as a String. This method should be used for tags of type
- * {@link #TYPE_ASCII}.
- *
- * @param defaultValue the String to return if the tag's value does not
- * exist or cannot be converted to a String.
- * @return the tag's value as a String, or the defaultValue.
- */
- public String getValueAsString(String defaultValue) {
- String s = getValueAsString();
- if (s == null) {
- return defaultValue;
- }
- return s;
- }
-
- /**
- * Gets the value as a byte array. This method should be used for tags of
- * type {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE}.
- *
- * @return the value as a byte array, or null if the tag's value does not
- * exist or cannot be converted to a byte array.
- */
- public byte[] getValueAsBytes() {
- if (mValue instanceof byte[]) {
- return (byte[]) mValue;
- }
- return null;
- }
-
- /**
- * Gets the value as a byte. If there are more than 1 bytes in this value,
- * gets the first byte. This method should be used for tags of type
- * {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE}.
- *
- * @param defaultValue the byte to return if tag's value does not exist or
- * cannot be converted to a byte.
- * @return the tag's value as a byte, or the defaultValue.
- */
- public byte getValueAsByte(byte defaultValue) {
- byte[] b = getValueAsBytes();
- if (b == null || b.length < 1) {
- return defaultValue;
- }
- return b[0];
- }
-
- /**
- * Gets the value as an array of Rationals. This method should be used for
- * tags of type {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
- *
- * @return the value as as an array of Rationals, or null if the tag's value
- * does not exist or cannot be converted to an array of Rationals.
- */
- public Rational[] getValueAsRationals() {
- if (mValue instanceof Rational[]) {
- return (Rational[]) mValue;
- }
- return null;
- }
-
- /**
- * Gets the value as a Rational. If there are more than 1 Rationals in this
- * value, gets the first one. This method should be used for tags of type
- * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
- *
- * @param defaultValue the Rational to return if tag's value does not exist
- * or cannot be converted to a Rational.
- * @return the tag's value as a Rational, or the defaultValue.
- */
- public Rational getValueAsRational(Rational defaultValue) {
- Rational[] r = getValueAsRationals();
- if (r == null || r.length < 1) {
- return defaultValue;
- }
- return r[0];
- }
-
- /**
- * Gets the value as a Rational. If there are more than 1 Rationals in this
- * value, gets the first one. This method should be used for tags of type
- * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
- *
- * @param defaultValue the numerator of the Rational to return if tag's
- * value does not exist or cannot be converted to a Rational (the
- * denominator will be 1).
- * @return the tag's value as a Rational, or the defaultValue.
- */
- public Rational getValueAsRational(long defaultValue) {
- Rational defaultVal = new Rational(defaultValue, 1);
- return getValueAsRational(defaultVal);
- }
-
- /**
- * Gets the value as an array of ints. This method should be used for tags
- * of type {@link #TYPE_UNSIGNED_SHORT}, {@link #TYPE_UNSIGNED_LONG}.
- *
- * @return the value as as an array of ints, or null if the tag's value does
- * not exist or cannot be converted to an array of ints.
- */
- public int[] getValueAsInts() {
- if (mValue == null) {
- return null;
- } else if (mValue instanceof long[]) {
- long[] val = (long[]) mValue;
- int[] arr = new int[val.length];
- for (int i = 0; i < val.length; i++) {
- arr[i] = (int) val[i]; // Truncates
- }
- return arr;
- }
- return null;
- }
-
- /**
- * Gets the value as an int. If there are more than 1 ints in this value,
- * gets the first one. This method should be used for tags of type
- * {@link #TYPE_UNSIGNED_SHORT}, {@link #TYPE_UNSIGNED_LONG}.
- *
- * @param defaultValue the int to return if tag's value does not exist or
- * cannot be converted to an int.
- * @return the tag's value as a int, or the defaultValue.
- */
- public int getValueAsInt(int defaultValue) {
- int[] i = getValueAsInts();
- if (i == null || i.length < 1) {
- return defaultValue;
- }
- return i[0];
- }
-
- /**
- * Gets the value as an array of longs. This method should be used for tags
- * of type {@link #TYPE_UNSIGNED_LONG}.
- *
- * @return the value as as an array of longs, or null if the tag's value
- * does not exist or cannot be converted to an array of longs.
- */
- public long[] getValueAsLongs() {
- if (mValue instanceof long[]) {
- return (long[]) mValue;
- }
- return null;
- }
-
- /**
- * Gets the value or null if none exists. If there are more than 1 longs in
- * this value, gets the first one. This method should be used for tags of
- * type {@link #TYPE_UNSIGNED_LONG}.
- *
- * @param defaultValue the long to return if tag's value does not exist or
- * cannot be converted to a long.
- * @return the tag's value as a long, or the defaultValue.
- */
- public long getValueAsLong(long defaultValue) {
- long[] l = getValueAsLongs();
- if (l == null || l.length < 1) {
- return defaultValue;
- }
- return l[0];
- }
-
- /**
- * Gets the tag's value or null if none exists.
- */
- public Object getValue() {
- return mValue;
- }
-
- /**
- * Gets a long representation of the value.
- *
- * @param defaultValue value to return if there is no value or value is a
- * rational with a denominator of 0.
- * @return the tag's value as a long, or defaultValue if no representation
- * exists.
- */
- public long forceGetValueAsLong(long defaultValue) {
- long[] l = getValueAsLongs();
- if (l != null && l.length >= 1) {
- return l[0];
- }
- byte[] b = getValueAsBytes();
- if (b != null && b.length >= 1) {
- return b[0];
- }
- Rational[] r = getValueAsRationals();
- if (r != null && r.length >= 1 && r[0].getDenominator() != 0) {
- return (long) r[0].toDouble();
- }
- return defaultValue;
- }
-
- /**
- * Gets a string representation of the value.
- */
- public String forceGetValueAsString() {
- if (mValue == null) {
- return "";
- } else if (mValue instanceof byte[]) {
- if (mDataType == TYPE_ASCII) {
- return new String((byte[]) mValue, US_ASCII);
- } else {
- return Arrays.toString((byte[]) mValue);
- }
- } else if (mValue instanceof long[]) {
- if (((long[]) mValue).length == 1) {
- return String.valueOf(((long[]) mValue)[0]);
- } else {
- return Arrays.toString((long[]) mValue);
- }
- } else if (mValue instanceof Object[]) {
- if (((Object[]) mValue).length == 1) {
- Object val = ((Object[]) mValue)[0];
- if (val == null) {
- return "";
- } else {
- return val.toString();
- }
- } else {
- return Arrays.toString((Object[]) mValue);
- }
- } else {
- return mValue.toString();
- }
- }
-
- /**
- * Gets the value for type {@link #TYPE_ASCII}, {@link #TYPE_LONG},
- * {@link #TYPE_UNDEFINED}, {@link #TYPE_UNSIGNED_BYTE},
- * {@link #TYPE_UNSIGNED_LONG}, or {@link #TYPE_UNSIGNED_SHORT}. For
- * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}, call
- * {@link #getRational(int)} instead.
- *
- * @exception IllegalArgumentException if the data type is
- * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
- */
- protected long getValueAt(int index) {
- if (mValue instanceof long[]) {
- return ((long[]) mValue)[index];
- } else if (mValue instanceof byte[]) {
- return ((byte[]) mValue)[index];
- }
- throw new IllegalArgumentException("Cannot get integer value from "
- + convertTypeToString(mDataType));
- }
-
- /**
- * Gets the {@link #TYPE_ASCII} data.
- *
- * @exception IllegalArgumentException If the type is NOT
- * {@link #TYPE_ASCII}.
- */
- protected String getString() {
- if (mDataType != TYPE_ASCII) {
- throw new IllegalArgumentException("Cannot get ASCII value from "
- + convertTypeToString(mDataType));
- }
- return new String((byte[]) mValue, US_ASCII);
- }
-
- /*
- * Get the converted ascii byte. Used by ExifOutputStream.
- */
- protected byte[] getStringByte() {
- return (byte[]) mValue;
- }
-
- /**
- * Gets the {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL} data.
- *
- * @exception IllegalArgumentException If the type is NOT
- * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
- */
- protected Rational getRational(int index) {
- if ((mDataType != TYPE_RATIONAL) && (mDataType != TYPE_UNSIGNED_RATIONAL)) {
- throw new IllegalArgumentException("Cannot get RATIONAL value from "
- + convertTypeToString(mDataType));
- }
- return ((Rational[]) mValue)[index];
- }
-
- /**
- * Equivalent to getBytes(buffer, 0, buffer.length).
- */
- protected void getBytes(byte[] buf) {
- getBytes(buf, 0, buf.length);
- }
-
- /**
- * Gets the {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE} data.
- *
- * @param buf the byte array in which to store the bytes read.
- * @param offset the initial position in buffer to store the bytes.
- * @param length the maximum number of bytes to store in buffer. If length >
- * component count, only the valid bytes will be stored.
- * @exception IllegalArgumentException If the type is NOT
- * {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE}.
- */
- protected void getBytes(byte[] buf, int offset, int length) {
- if ((mDataType != TYPE_UNDEFINED) && (mDataType != TYPE_UNSIGNED_BYTE)) {
- throw new IllegalArgumentException("Cannot get BYTE value from "
- + convertTypeToString(mDataType));
- }
- System.arraycopy(mValue, 0, buf, offset,
- (length > mComponentCountActual) ? mComponentCountActual : length);
- }
-
- /**
- * Gets the offset of this tag. This is only valid if this data size > 4 and
- * contains an offset to the location of the actual value.
- */
- protected int getOffset() {
- return mOffset;
- }
-
- /**
- * Sets the offset of this tag.
- */
- protected void setOffset(int offset) {
- mOffset = offset;
- }
-
- protected void setHasDefinedCount(boolean d) {
- mHasDefinedDefaultComponentCount = d;
- }
-
- protected boolean hasDefinedCount() {
- return mHasDefinedDefaultComponentCount;
- }
-
- private boolean checkBadComponentCount(int count) {
- if (mHasDefinedDefaultComponentCount && (mComponentCountActual != count)) {
- return true;
- }
- return false;
- }
-
- private static String convertTypeToString(short type) {
- switch (type) {
- case TYPE_UNSIGNED_BYTE:
- return "UNSIGNED_BYTE";
- case TYPE_ASCII:
- return "ASCII";
- case TYPE_UNSIGNED_SHORT:
- return "UNSIGNED_SHORT";
- case TYPE_UNSIGNED_LONG:
- return "UNSIGNED_LONG";
- case TYPE_UNSIGNED_RATIONAL:
- return "UNSIGNED_RATIONAL";
- case TYPE_UNDEFINED:
- return "UNDEFINED";
- case TYPE_LONG:
- return "LONG";
- case TYPE_RATIONAL:
- return "RATIONAL";
- default:
- return "";
- }
- }
-
- private boolean checkOverflowForUnsignedShort(int[] value) {
- for (int v : value) {
- if (v > UNSIGNED_SHORT_MAX || v < 0) {
- return true;
- }
- }
- return false;
- }
-
- private boolean checkOverflowForUnsignedLong(long[] value) {
- for (long v : value) {
- if (v < 0 || v > UNSIGNED_LONG_MAX) {
- return true;
- }
- }
- return false;
- }
-
- private boolean checkOverflowForUnsignedLong(int[] value) {
- for (int v : value) {
- if (v < 0) {
- return true;
- }
- }
- return false;
- }
-
- private boolean checkOverflowForUnsignedRational(Rational[] value) {
- for (Rational v : value) {
- if (v.getNumerator() < 0 || v.getDenominator() < 0
- || v.getNumerator() > UNSIGNED_LONG_MAX
- || v.getDenominator() > UNSIGNED_LONG_MAX) {
- return true;
- }
- }
- return false;
- }
-
- private boolean checkOverflowForRational(Rational[] value) {
- for (Rational v : value) {
- if (v.getNumerator() < LONG_MIN || v.getDenominator() < LONG_MIN
- || v.getNumerator() > LONG_MAX
- || v.getDenominator() > LONG_MAX) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (obj instanceof ExifTag) {
- ExifTag tag = (ExifTag) obj;
- if (tag.mTagId != this.mTagId
- || tag.mComponentCountActual != this.mComponentCountActual
- || tag.mDataType != this.mDataType) {
- return false;
- }
- if (mValue != null) {
- if (tag.mValue == null) {
- return false;
- } else if (mValue instanceof long[]) {
- if (!(tag.mValue instanceof long[])) {
- return false;
- }
- return Arrays.equals((long[]) mValue, (long[]) tag.mValue);
- } else if (mValue instanceof Rational[]) {
- if (!(tag.mValue instanceof Rational[])) {
- return false;
- }
- return Arrays.equals((Rational[]) mValue, (Rational[]) tag.mValue);
- } else if (mValue instanceof byte[]) {
- if (!(tag.mValue instanceof byte[])) {
- return false;
- }
- return Arrays.equals((byte[]) mValue, (byte[]) tag.mValue);
- } else {
- return mValue.equals(tag.mValue);
- }
- } else {
- return tag.mValue == null;
- }
- }
- return false;
- }
-
- @Override
- public String toString() {
- return String.format("tag id: %04X\n", mTagId) + "ifd id: " + mIfd + "\ntype: "
- + convertTypeToString(mDataType) + "\ncount: " + mComponentCountActual
- + "\noffset: " + mOffset + "\nvalue: " + forceGetValueAsString() + "\n";
- }
-
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/IfdData.java b/WallpaperPicker/src/com/android/gallery3d/exif/IfdData.java
deleted file mode 100644
index 093944aec..000000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/IfdData.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2012 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.gallery3d.exif;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * This class stores all the tags in an IFD.
- *
- * @see ExifData
- * @see ExifTag
- */
-class IfdData {
-
- private final int mIfdId;
- private final Map<Short, ExifTag> mExifTags = new HashMap<Short, ExifTag>();
- private int mOffsetToNextIfd = 0;
- private static final int[] sIfds = {
- IfdId.TYPE_IFD_0, IfdId.TYPE_IFD_1, IfdId.TYPE_IFD_EXIF,
- IfdId.TYPE_IFD_INTEROPERABILITY, IfdId.TYPE_IFD_GPS
- };
- /**
- * Creates an IfdData with given IFD ID.
- *
- * @see IfdId#TYPE_IFD_0
- * @see IfdId#TYPE_IFD_1
- * @see IfdId#TYPE_IFD_EXIF
- * @see IfdId#TYPE_IFD_GPS
- * @see IfdId#TYPE_IFD_INTEROPERABILITY
- */
- IfdData(int ifdId) {
- mIfdId = ifdId;
- }
-
- static protected int[] getIfds() {
- return sIfds;
- }
-
- /**
- * Get a array the contains all {@link ExifTag} in this IFD.
- */
- protected ExifTag[] getAllTags() {
- return mExifTags.values().toArray(new ExifTag[mExifTags.size()]);
- }
-
- /**
- * Gets the ID of this IFD.
- *
- * @see IfdId#TYPE_IFD_0
- * @see IfdId#TYPE_IFD_1
- * @see IfdId#TYPE_IFD_EXIF
- * @see IfdId#TYPE_IFD_GPS
- * @see IfdId#TYPE_IFD_INTEROPERABILITY
- */
- protected int getId() {
- return mIfdId;
- }
-
- /**
- * Gets the {@link ExifTag} with given tag id. Return null if there is no
- * such tag.
- */
- protected ExifTag getTag(short tagId) {
- return mExifTags.get(tagId);
- }
-
- /**
- * Adds or replaces a {@link ExifTag}.
- */
- protected ExifTag setTag(ExifTag tag) {
- tag.setIfd(mIfdId);
- return mExifTags.put(tag.getTagId(), tag);
- }
-
- protected boolean checkCollision(short tagId) {
- return mExifTags.get(tagId) != null;
- }
-
- /**
- * Removes the tag of the given ID
- */
- protected void removeTag(short tagId) {
- mExifTags.remove(tagId);
- }
-
- /**
- * Gets the tags count in the IFD.
- */
- protected int getTagCount() {
- return mExifTags.size();
- }
-
- /**
- * Sets the offset of next IFD.
- */
- protected void setOffsetToNextIfd(int offset) {
- mOffsetToNextIfd = offset;
- }
-
- /**
- * Gets the offset of next IFD.
- */
- protected int getOffsetToNextIfd() {
- return mOffsetToNextIfd;
- }
-
- /**
- * Returns true if all tags in this two IFDs are equal. Note that tags of
- * IFDs offset or thumbnail offset will be ignored.
- */
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (obj instanceof IfdData) {
- IfdData data = (IfdData) obj;
- if (data.getId() == mIfdId && data.getTagCount() == getTagCount()) {
- ExifTag[] tags = data.getAllTags();
- for (ExifTag tag : tags) {
- if (ExifInterface.isOffsetTag(tag.getTagId())) {
- continue;
- }
- ExifTag tag2 = mExifTags.get(tag.getTagId());
- if (!tag.equals(tag2)) {
- return false;
- }
- }
- return true;
- }
- }
- return false;
- }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/IfdId.java b/WallpaperPicker/src/com/android/gallery3d/exif/IfdId.java
deleted file mode 100644
index 7842edbd4..000000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/IfdId.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2012 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.gallery3d.exif;
-
-/**
- * The constants of the IFD ID defined in EXIF spec.
- */
-public interface IfdId {
- public static final int TYPE_IFD_0 = 0;
- public static final int TYPE_IFD_1 = 1;
- public static final int TYPE_IFD_EXIF = 2;
- public static final int TYPE_IFD_INTEROPERABILITY = 3;
- public static final int TYPE_IFD_GPS = 4;
- /* This is used in ExifData to allocate enough IfdData */
- static final int TYPE_IFD_COUNT = 5;
-
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/JpegHeader.java b/WallpaperPicker/src/com/android/gallery3d/exif/JpegHeader.java
deleted file mode 100644
index e3e787eff..000000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/JpegHeader.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2012 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.gallery3d.exif;
-
-class JpegHeader {
- public static final short SOI = (short) 0xFFD8;
- public static final short APP1 = (short) 0xFFE1;
- public static final short APP0 = (short) 0xFFE0;
- public static final short EOI = (short) 0xFFD9;
-
- /**
- * SOF (start of frame). All value between SOF0 and SOF15 is SOF marker except for DHT, JPG,
- * and DAC marker.
- */
- public static final short SOF0 = (short) 0xFFC0;
- public static final short SOF15 = (short) 0xFFCF;
- public static final short DHT = (short) 0xFFC4;
- public static final short JPG = (short) 0xFFC8;
- public static final short DAC = (short) 0xFFCC;
-
- public static final boolean isSofMarker(short marker) {
- return marker >= SOF0 && marker <= SOF15 && marker != DHT && marker != JPG
- && marker != DAC;
- }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/OrderedDataOutputStream.java b/WallpaperPicker/src/com/android/gallery3d/exif/OrderedDataOutputStream.java
deleted file mode 100644
index 428e6b9fc..000000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/OrderedDataOutputStream.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2012 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.gallery3d.exif;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-class OrderedDataOutputStream extends FilterOutputStream {
- private final ByteBuffer mByteBuffer = ByteBuffer.allocate(4);
-
- public OrderedDataOutputStream(OutputStream out) {
- super(out);
- }
-
- public OrderedDataOutputStream setByteOrder(ByteOrder order) {
- mByteBuffer.order(order);
- return this;
- }
-
- public OrderedDataOutputStream writeShort(short value) throws IOException {
- mByteBuffer.rewind();
- mByteBuffer.putShort(value);
- out.write(mByteBuffer.array(), 0, 2);
- return this;
- }
-
- public OrderedDataOutputStream writeInt(int value) throws IOException {
- mByteBuffer.rewind();
- mByteBuffer.putInt(value);
- out.write(mByteBuffer.array());
- return this;
- }
-
- public OrderedDataOutputStream writeRational(Rational rational) throws IOException {
- writeInt((int) rational.getNumerator());
- writeInt((int) rational.getDenominator());
- return this;
- }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/Rational.java b/WallpaperPicker/src/com/android/gallery3d/exif/Rational.java
deleted file mode 100644
index 591d63faf..000000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/Rational.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2012 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.gallery3d.exif;
-
-/**
- * The rational data type of EXIF tag. Contains a pair of longs representing the
- * numerator and denominator of a Rational number.
- */
-public class Rational {
-
- private final long mNumerator;
- private final long mDenominator;
-
- /**
- * Create a Rational with a given numerator and denominator.
- *
- * @param nominator
- * @param denominator
- */
- public Rational(long nominator, long denominator) {
- mNumerator = nominator;
- mDenominator = denominator;
- }
-
- /**
- * Create a copy of a Rational.
- */
- public Rational(Rational r) {
- mNumerator = r.mNumerator;
- mDenominator = r.mDenominator;
- }
-
- /**
- * Gets the numerator of the rational.
- */
- public long getNumerator() {
- return mNumerator;
- }
-
- /**
- * Gets the denominator of the rational
- */
- public long getDenominator() {
- return mDenominator;
- }
-
- /**
- * Gets the rational value as type double. Will cause a divide-by-zero error
- * if the denominator is 0.
- */
- public double toDouble() {
- return mNumerator / (double) mDenominator;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (this == obj) {
- return true;
- }
- if (obj instanceof Rational) {
- Rational data = (Rational) obj;
- return mNumerator == data.mNumerator && mDenominator == data.mDenominator;
- }
- return false;
- }
-
- @Override
- public String toString() {
- return mNumerator + "/" + mDenominator;
- }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/BasicTexture.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/BasicTexture.java
index 0f3efb727..7270e88c0 100644
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/BasicTexture.java
+++ b/WallpaperPicker/src/com/android/gallery3d/glrenderer/BasicTexture.java
@@ -46,8 +46,6 @@ public abstract class BasicTexture implements Texture {
protected int mTextureWidth;
protected int mTextureHeight;
- private boolean mHasBorder;
-
protected GLCanvas mCanvasRef = null;
private static WeakHashMap<BasicTexture, Object> sAllTextures
= new WeakHashMap<BasicTexture, Object>();
@@ -85,10 +83,6 @@ public abstract class BasicTexture implements Texture {
}
}
- public boolean isFlippedVertically() {
- return false;
- }
-
public int getId() {
return mId;
}
@@ -113,25 +107,6 @@ public abstract class BasicTexture implements Texture {
return mTextureHeight;
}
- // Returns true if the texture has one pixel transparent border around the
- // actual content. This is used to avoid jigged edges.
- //
- // The jigged edges appear because we use GL_CLAMP_TO_EDGE for texture wrap
- // mode (GL_CLAMP is not available in OpenGL ES), so a pixel partially
- // covered by the texture will use the color of the edge texel. If we add
- // the transparent border, the color of the edge texel will be mixed with
- // appropriate amount of transparent.
- //
- // Currently our background is black, so we can draw the thumbnails without
- // enabling blending.
- public boolean hasBorder() {
- return mHasBorder;
- }
-
- protected void setBorder(boolean hasBorder) {
- mHasBorder = hasBorder;
- }
-
@Override
public void draw(GLCanvas canvas, int x, int y) {
canvas.drawTexture(this, x, y, getWidth(), getHeight());
@@ -146,9 +121,6 @@ public abstract class BasicTexture implements Texture {
// It should make sure the data is uploaded to GL memory.
abstract protected boolean onBind(GLCanvas canvas);
- // Returns the GL texture target for this texture (e.g. GL_TEXTURE_2D).
- abstract protected int getTarget();
-
public boolean isLoaded() {
return mState == STATE_LOADED;
}
@@ -185,13 +157,6 @@ public abstract class BasicTexture implements Texture {
sInFinalizer.set(null);
}
- // This is for deciding if we can call Bitmap's recycle().
- // We cannot call Bitmap's recycle() in finalizer because at that point
- // the finalizer of Bitmap may already be called so recycle() will crash.
- public static boolean inFinalizer() {
- return sInFinalizer.get() != null;
- }
-
public static void yieldAllTextures() {
synchronized (sAllTextures) {
for (BasicTexture t : sAllTextures.keySet()) {
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/BitmapTexture.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/BitmapTexture.java
index f8b01cb42..bb69b6858 100644
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/BitmapTexture.java
+++ b/WallpaperPicker/src/com/android/gallery3d/glrenderer/BitmapTexture.java
@@ -29,11 +29,7 @@ public class BitmapTexture extends UploadedTexture {
protected Bitmap mContentBitmap;
public BitmapTexture(Bitmap bitmap) {
- this(bitmap, false);
- }
-
- public BitmapTexture(Bitmap bitmap, boolean hasBorder) {
- super(hasBorder);
+ super();
Utils.assertTrue(bitmap != null && !bitmap.isRecycled());
mContentBitmap = bitmap;
}
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLCanvas.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLCanvas.java
index 5b0747704..2bda8d21a 100644
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLCanvas.java
+++ b/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLCanvas.java
@@ -17,7 +17,6 @@
package com.android.gallery3d.glrenderer;
import android.graphics.Bitmap;
-import android.graphics.Rect;
import android.graphics.RectF;
//
@@ -40,36 +39,14 @@ public interface GLCanvas {
// Clear the drawing buffers. This should only be used by GLRoot.
public abstract void clearBuffer();
- public abstract void clearBuffer(float[] argb);
-
- // Sets and gets the current alpha, alpha must be in [0, 1].
- public abstract void setAlpha(float alpha);
-
- public abstract float getAlpha();
-
- // (current alpha) = (current alpha) * alpha
- public abstract void multiplyAlpha(float alpha);
-
- // Change the current transform matrix.
- public abstract void translate(float x, float y, float z);
-
public abstract void translate(float x, float y);
- public abstract void scale(float sx, float sy, float sz);
-
public abstract void rotate(float angle, float x, float y, float z);
- public abstract void multiplyMatrix(float[] mMatrix, int offset);
-
- // Pushes the configuration state (matrix, and alpha) onto
- // a private stack.
- public abstract void save();
-
// Same as save(), but only save those specified in saveFlags.
public abstract void save(int saveFlags);
public static final int SAVE_FLAG_ALL = 0xFFFFFFFF;
- public static final int SAVE_FLAG_ALPHA = 0x01;
public static final int SAVE_FLAG_MATRIX = 0x02;
// Pops from the top of the stack as current configuration state (matrix,
@@ -78,64 +55,22 @@ public interface GLCanvas {
// last save call.
public abstract void restore();
- // Draws a line using the specified paint from (x1, y1) to (x2, y2).
- // (Both end points are included).
- public abstract void drawLine(float x1, float y1, float x2, float y2, GLPaint paint);
-
- // Draws a rectangle using the specified paint from (x1, y1) to (x2, y2).
- // (Both end points are included).
- public abstract void drawRect(float x1, float y1, float x2, float y2, GLPaint paint);
-
- // Fills the specified rectangle with the specified color.
- public abstract void fillRect(float x, float y, float width, float height, int color);
-
// Draws a texture to the specified rectangle.
- public abstract void drawTexture(
- BasicTexture texture, int x, int y, int width, int height);
-
- public abstract void drawMesh(BasicTexture tex, int x, int y, int xyBuffer,
- int uvBuffer, int indexBuffer, int indexCount);
+ public abstract void drawTexture(BasicTexture texture, int x, int y, int width, int height);
// Draws the source rectangle part of the texture to the target rectangle.
public abstract void drawTexture(BasicTexture texture, RectF source, RectF target);
- // Draw a texture with a specified texture transform.
- public abstract void drawTexture(BasicTexture texture, float[] mTextureTransform,
- int x, int y, int w, int h);
-
- // Draw two textures to the specified rectangle. The actual texture used is
- // from * (1 - ratio) + to * ratio
- // The two textures must have the same size.
- public abstract void drawMixed(BasicTexture from, int toColor,
- float ratio, int x, int y, int w, int h);
-
- // Draw a region of a texture and a specified color to the specified
- // rectangle. The actual color used is from * (1 - ratio) + to * ratio.
- // The region of the texture is defined by parameter "src". The target
- // rectangle is specified by parameter "target".
- public abstract void drawMixed(BasicTexture from, int toColor,
- float ratio, RectF src, RectF target);
-
// Unloads the specified texture from the canvas. The resource allocated
// to draw the texture will be released. The specified texture will return
// to the unloaded state. This function should be called only from
// BasicTexture or its descendant
public abstract boolean unloadTexture(BasicTexture texture);
- // Delete the specified buffer object, similar to unloadTexture.
- public abstract void deleteBuffer(int bufferId);
-
// Delete the textures and buffers in GL side. This function should only be
// called in the GL thread.
public abstract void deleteRecycledResources();
- // Dump statistics information and clear the counters. For debug only.
- public abstract void dumpStatisticsAndClear();
-
- public abstract void beginRenderTarget(RawTexture texture);
-
- public abstract void endRenderTarget();
-
/**
* Sets texture parameters to use GL_CLAMP_TO_EDGE for both
* GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T. Sets texture parameters to be
@@ -185,31 +120,4 @@ public interface GLCanvas {
* @return The buffer ID that was generated.
*/
public abstract int uploadBuffer(java.nio.FloatBuffer buffer);
-
- /**
- * Generates buffers and uploads the element array buffer data.
- *
- * @param buffer The buffer to upload
- * @return The buffer ID that was generated.
- */
- public abstract int uploadBuffer(java.nio.ByteBuffer buffer);
-
- /**
- * After LightCycle makes GL calls, this method is called to restore the GL
- * configuration to the one expected by GLCanvas.
- */
- public abstract void recoverFromLightCycle();
-
- /**
- * Gets the bounds given by x, y, width, and height as well as the internal
- * matrix state. There is no special handling for non-90-degree rotations.
- * It only considers the lower-left and upper-right corners as the bounds.
- *
- * @param bounds The output bounds to write to.
- * @param x The left side of the input rectangle.
- * @param y The bottom of the input rectangle.
- * @param width The width of the input rectangle.
- * @param height The height of the input rectangle.
- */
- public abstract void getBounds(Rect bounds, int x, int y, int width, int height);
}
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20Canvas.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20Canvas.java
index 933260b48..0da3bae96 100644
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20Canvas.java
+++ b/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20Canvas.java
@@ -16,7 +16,6 @@
package com.android.gallery3d.glrenderer;
import android.graphics.Bitmap;
-import android.graphics.Rect;
import android.graphics.RectF;
import android.opengl.GLES20;
import android.opengl.GLUtils;
@@ -27,24 +26,22 @@ import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
-import java.util.ArrayList;
import java.util.Arrays;
+import javax.microedition.khronos.opengles.GL11;
+
public class GLES20Canvas implements GLCanvas {
// ************** Constants **********************
private static final String TAG = GLES20Canvas.class.getSimpleName();
private static final int FLOAT_SIZE = Float.SIZE / Byte.SIZE;
- private static final float OPAQUE_ALPHA = 0.95f;
private static final int COORDS_PER_VERTEX = 2;
private static final int VERTEX_STRIDE = COORDS_PER_VERTEX * FLOAT_SIZE;
private static final int COUNT_FILL_VERTEX = 4;
- private static final int COUNT_LINE_VERTEX = 2;
- private static final int COUNT_RECT_VERTEX = 4;
private static final int OFFSET_FILL_RECT = 0;
- private static final int OFFSET_DRAW_LINE = OFFSET_FILL_RECT + COUNT_FILL_VERTEX;
- private static final int OFFSET_DRAW_RECT = OFFSET_DRAW_LINE + COUNT_LINE_VERTEX;
+
+ private static final int GL_TARGET = GL11.GL_TEXTURE_2D;
private static final float[] BOX_COORDINATES = {
0, 0, // Fill rectangle
@@ -59,33 +56,11 @@ public class GLES20Canvas implements GLCanvas {
1, 0,
};
- private static final float[] BOUNDS_COORDINATES = {
- 0, 0, 0, 1,
- 1, 1, 0, 1,
- };
-
private static final String POSITION_ATTRIBUTE = "aPosition";
- private static final String COLOR_UNIFORM = "uColor";
private static final String MATRIX_UNIFORM = "uMatrix";
private static final String TEXTURE_MATRIX_UNIFORM = "uTextureMatrix";
private static final String TEXTURE_SAMPLER_UNIFORM = "uTextureSampler";
private static final String ALPHA_UNIFORM = "uAlpha";
- private static final String TEXTURE_COORD_ATTRIBUTE = "aTextureCoordinate";
-
- private static final String DRAW_VERTEX_SHADER = ""
- + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
- + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
- + "void main() {\n"
- + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
- + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
- + "}\n";
-
- private static final String DRAW_FRAGMENT_SHADER = ""
- + "precision mediump float;\n"
- + "uniform vec4 " + COLOR_UNIFORM + ";\n"
- + "void main() {\n"
- + " gl_FragColor = " + COLOR_UNIFORM + ";\n"
- + "}\n";
private static final String TEXTURE_VERTEX_SHADER = ""
+ "uniform mat4 " + MATRIX_UNIFORM + ";\n"
@@ -98,17 +73,6 @@ public class GLES20Canvas implements GLCanvas {
+ " vTextureCoord = (" + TEXTURE_MATRIX_UNIFORM + " * pos).xy;\n"
+ "}\n";
- private static final String MESH_VERTEX_SHADER = ""
- + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
- + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
- + "attribute vec2 " + TEXTURE_COORD_ATTRIBUTE + ";\n"
- + "varying vec2 vTextureCoord;\n"
- + "void main() {\n"
- + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
- + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
- + " vTextureCoord = " + TEXTURE_COORD_ATTRIBUTE + ";\n"
- + "}\n";
-
private static final String TEXTURE_FRAGMENT_SHADER = ""
+ "precision mediump float;\n"
+ "varying vec2 vTextureCoord;\n"
@@ -119,26 +83,13 @@ public class GLES20Canvas implements GLCanvas {
+ " gl_FragColor *= " + ALPHA_UNIFORM + ";\n"
+ "}\n";
- private static final String OES_TEXTURE_FRAGMENT_SHADER = ""
- + "#extension GL_OES_EGL_image_external : require\n"
- + "precision mediump float;\n"
- + "varying vec2 vTextureCoord;\n"
- + "uniform float " + ALPHA_UNIFORM + ";\n"
- + "uniform samplerExternalOES " + TEXTURE_SAMPLER_UNIFORM + ";\n"
- + "void main() {\n"
- + " gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n"
- + " gl_FragColor *= " + ALPHA_UNIFORM + ";\n"
- + "}\n";
-
private static final int INITIAL_RESTORE_STATE_SIZE = 8;
private static final int MATRIX_SIZE = 16;
// Keep track of restore state
private float[] mMatrices = new float[INITIAL_RESTORE_STATE_SIZE * MATRIX_SIZE];
- private float[] mAlphas = new float[INITIAL_RESTORE_STATE_SIZE];
private IntArray mSaveFlags = new IntArray();
- private int mCurrentAlphaIndex = 0;
private int mCurrentMatrixIndex = 0;
// Viewport size
@@ -148,15 +99,8 @@ public class GLES20Canvas implements GLCanvas {
// Projection matrix
private float[] mProjectionMatrix = new float[MATRIX_SIZE];
- // Screen size for when we aren't bound to a texture
- private int mScreenWidth;
- private int mScreenHeight;
-
// GL programs
- private int mDrawProgram;
private int mTextureProgram;
- private int mOesTextureProgram;
- private int mMeshProgram;
// GL buffer containing BOX_COORDINATES
private int mBoxCoordinates;
@@ -165,17 +109,11 @@ public class GLES20Canvas implements GLCanvas {
private static final int INDEX_POSITION = 0;
private static final int INDEX_MATRIX = 1;
- // Handle indices -- draw
- private static final int INDEX_COLOR = 2;
-
// Handle indices -- texture
private static final int INDEX_TEXTURE_MATRIX = 2;
private static final int INDEX_TEXTURE_SAMPLER = 3;
private static final int INDEX_ALPHA = 4;
- // Handle indices -- mesh
- private static final int INDEX_TEXTURE_COORD = 2;
-
private abstract static class ShaderParameter {
public int handle;
protected final String mName;
@@ -211,52 +149,18 @@ public class GLES20Canvas implements GLCanvas {
}
}
- ShaderParameter[] mDrawParameters = {
- new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
- new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
- new UniformShaderParameter(COLOR_UNIFORM), // INDEX_COLOR
- };
- ShaderParameter[] mTextureParameters = {
+ private ShaderParameter[] mTextureParameters = {
new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX
new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
};
- ShaderParameter[] mOesTextureParameters = {
- new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
- new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
- new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX
- new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
- new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
- };
- ShaderParameter[] mMeshParameters = {
- new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
- new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
- new AttributeShaderParameter(TEXTURE_COORD_ATTRIBUTE), // INDEX_TEXTURE_COORD
- new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
- new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
- };
private final IntArray mUnboundTextures = new IntArray();
- private final IntArray mDeleteBuffers = new IntArray();
-
- // Keep track of statistics for debugging
- private int mCountDrawMesh = 0;
- private int mCountTextureRect = 0;
- private int mCountFillRect = 0;
- private int mCountDrawLine = 0;
-
- // Buffer for framebuffer IDs -- we keep track so we can switch the attached
- // texture.
- private int[] mFrameBuffer = new int[1];
-
- // Bound textures.
- private ArrayList<RawTexture> mTargetTextures = new ArrayList<RawTexture>();
// Temporary variables used within calculations
private final float[] mTempMatrix = new float[32];
- private final float[] mTempColor = new float[4];
private final RectF mTempSourceRect = new RectF();
private final RectF mTempTargetRect = new RectF();
private final float[] mTempTextureMatrix = new float[MATRIX_SIZE];
@@ -267,26 +171,15 @@ public class GLES20Canvas implements GLCanvas {
public GLES20Canvas() {
Matrix.setIdentityM(mTempTextureMatrix, 0);
Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
- mAlphas[mCurrentAlphaIndex] = 1f;
- mTargetTextures.add(null);
FloatBuffer boxBuffer = createBuffer(BOX_COORDINATES);
mBoxCoordinates = uploadBuffer(boxBuffer);
- int drawVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DRAW_VERTEX_SHADER);
int textureVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, TEXTURE_VERTEX_SHADER);
- int meshVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, MESH_VERTEX_SHADER);
- int drawFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DRAW_FRAGMENT_SHADER);
int textureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, TEXTURE_FRAGMENT_SHADER);
- int oesTextureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
- OES_TEXTURE_FRAGMENT_SHADER);
- mDrawProgram = assembleProgram(drawVertexShader, drawFragmentShader, mDrawParameters);
mTextureProgram = assembleProgram(textureVertexShader, textureFragmentShader,
mTextureParameters);
- mOesTextureProgram = assembleProgram(textureVertexShader, oesTextureFragmentShader,
- mOesTextureParameters);
- mMeshProgram = assembleProgram(meshVertexShader, textureFragmentShader, mMeshParameters);
GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
checkError();
}
@@ -348,12 +241,8 @@ public class GLES20Canvas implements GLCanvas {
checkError();
Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
Matrix.orthoM(mProjectionMatrix, 0, 0, width, 0, height, -1, 1);
- if (getTargetTexture() == null) {
- mScreenWidth = width;
- mScreenHeight = height;
- Matrix.translateM(mMatrices, mCurrentMatrixIndex, 0, height, 0);
- Matrix.scaleM(mMatrices, mCurrentMatrixIndex, 1, -1, 1);
- }
+ Matrix.translateM(mMatrices, mCurrentMatrixIndex, 0, height, 0);
+ Matrix.scaleM(mMatrices, mCurrentMatrixIndex, 1, -1, 1);
}
@Override
@@ -364,34 +253,6 @@ public class GLES20Canvas implements GLCanvas {
checkError();
}
- @Override
- public void clearBuffer(float[] argb) {
- GLES20.glClearColor(argb[1], argb[2], argb[3], argb[0]);
- checkError();
- GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
- checkError();
- }
-
- @Override
- public float getAlpha() {
- return mAlphas[mCurrentAlphaIndex];
- }
-
- @Override
- public void setAlpha(float alpha) {
- mAlphas[mCurrentAlphaIndex] = alpha;
- }
-
- @Override
- public void multiplyAlpha(float alpha) {
- setAlpha(getAlpha() * alpha);
- }
-
- @Override
- public void translate(float x, float y, float z) {
- Matrix.translateM(mMatrices, mCurrentMatrixIndex, x, y, z);
- }
-
// This is a faster version of translate(x, y, z) because
// (1) we knows z = 0, (2) we inline the Matrix.translateM call,
// (3) we unroll the loop
@@ -406,11 +267,6 @@ public class GLES20Canvas implements GLCanvas {
}
@Override
- public void scale(float sx, float sy, float sz) {
- Matrix.scaleM(mMatrices, mCurrentMatrixIndex, sx, sy, sz);
- }
-
- @Override
public void rotate(float angle, float x, float y, float z) {
if (angle == 0f) {
return;
@@ -424,30 +280,7 @@ public class GLES20Canvas implements GLCanvas {
}
@Override
- public void multiplyMatrix(float[] matrix, int offset) {
- float[] temp = mTempMatrix;
- float[] currentMatrix = mMatrices;
- int index = mCurrentMatrixIndex;
- Matrix.multiplyMM(temp, 0, currentMatrix, index, matrix, offset);
- System.arraycopy(temp, 0, currentMatrix, index, 16);
- }
-
- @Override
- public void save() {
- save(SAVE_FLAG_ALL);
- }
-
- @Override
public void save(int saveFlags) {
- boolean saveAlpha = (saveFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA;
- if (saveAlpha) {
- float currentAlpha = getAlpha();
- mCurrentAlphaIndex++;
- if (mAlphas.length <= mCurrentAlphaIndex) {
- mAlphas = Arrays.copyOf(mAlphas, mAlphas.length * 2);
- }
- mAlphas[mCurrentAlphaIndex] = currentAlpha;
- }
boolean saveMatrix = (saveFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX;
if (saveMatrix) {
int currentIndex = mCurrentMatrixIndex;
@@ -463,82 +296,12 @@ public class GLES20Canvas implements GLCanvas {
@Override
public void restore() {
int restoreFlags = mSaveFlags.removeLast();
- boolean restoreAlpha = (restoreFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA;
- if (restoreAlpha) {
- mCurrentAlphaIndex--;
- }
boolean restoreMatrix = (restoreFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX;
if (restoreMatrix) {
mCurrentMatrixIndex -= MATRIX_SIZE;
}
}
- @Override
- public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) {
- draw(GLES20.GL_LINE_STRIP, OFFSET_DRAW_LINE, COUNT_LINE_VERTEX, x1, y1, x2 - x1, y2 - y1,
- paint);
- mCountDrawLine++;
- }
-
- @Override
- public void drawRect(float x, float y, float width, float height, GLPaint paint) {
- draw(GLES20.GL_LINE_LOOP, OFFSET_DRAW_RECT, COUNT_RECT_VERTEX, x, y, width, height, paint);
- mCountDrawLine++;
- }
-
- private void draw(int type, int offset, int count, float x, float y, float width, float height,
- GLPaint paint) {
- draw(type, offset, count, x, y, width, height, paint.getColor(), paint.getLineWidth());
- }
-
- private void draw(int type, int offset, int count, float x, float y, float width, float height,
- int color, float lineWidth) {
- prepareDraw(offset, color, lineWidth);
- draw(mDrawParameters, type, count, x, y, width, height);
- }
-
- private void prepareDraw(int offset, int color, float lineWidth) {
- GLES20.glUseProgram(mDrawProgram);
- checkError();
- if (lineWidth > 0) {
- GLES20.glLineWidth(lineWidth);
- checkError();
- }
- float[] colorArray = getColor(color);
- boolean blendingEnabled = (colorArray[3] < 1f);
- enableBlending(blendingEnabled);
- if (blendingEnabled) {
- GLES20.glBlendColor(colorArray[0], colorArray[1], colorArray[2], colorArray[3]);
- checkError();
- }
-
- GLES20.glUniform4fv(mDrawParameters[INDEX_COLOR].handle, 1, colorArray, 0);
- setPosition(mDrawParameters, offset);
- checkError();
- }
-
- private float[] getColor(int color) {
- float alpha = ((color >>> 24) & 0xFF) / 255f * getAlpha();
- float red = ((color >>> 16) & 0xFF) / 255f * alpha;
- float green = ((color >>> 8) & 0xFF) / 255f * alpha;
- float blue = (color & 0xFF) / 255f * alpha;
- mTempColor[0] = red;
- mTempColor[1] = green;
- mTempColor[2] = blue;
- mTempColor[3] = alpha;
- return mTempColor;
- }
-
- private void enableBlending(boolean enableBlending) {
- if (enableBlending) {
- GLES20.glEnable(GLES20.GL_BLEND);
- checkError();
- } else {
- GLES20.glDisable(GLES20.GL_BLEND);
- checkError();
- }
- }
-
private void setPosition(ShaderParameter[] params, int offset) {
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBoxCoordinates);
checkError();
@@ -570,13 +333,6 @@ public class GLES20Canvas implements GLCanvas {
}
@Override
- public void fillRect(float x, float y, float width, float height, int color) {
- draw(GLES20.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, COUNT_FILL_VERTEX, x, y, width, height,
- color, 0f);
- mCountFillRect++;
- }
-
- @Override
public void drawTexture(BasicTexture texture, int x, int y, int width, int height) {
if (width <= 0 || height <= 0) {
return;
@@ -588,17 +344,7 @@ public class GLES20Canvas implements GLCanvas {
}
private static void copyTextureCoordinates(BasicTexture texture, RectF outRect) {
- int left = 0;
- int top = 0;
- int right = texture.getWidth();
- int bottom = texture.getHeight();
- if (texture.hasBorder()) {
- left = 1;
- top = 1;
- right -= 1;
- bottom -= 1;
- }
- outRect.set(left, top, right, bottom);
+ outRect.set(0, 0, texture.getWidth(), texture.getHeight());
}
@Override
@@ -613,16 +359,6 @@ public class GLES20Canvas implements GLCanvas {
drawTextureRect(texture, mTempSourceRect, mTempTargetRect);
}
- @Override
- public void drawTexture(BasicTexture texture, float[] textureTransform, int x, int y, int w,
- int h) {
- if (w <= 0 || h <= 0) {
- return;
- }
- mTempTargetRect.set(x, y, x + w, y + h);
- drawTextureRect(texture, textureTransform, mTempTargetRect);
- }
-
private void drawTextureRect(BasicTexture texture, RectF source, RectF target) {
setTextureMatrix(source);
drawTextureRect(texture, mTempTextureMatrix, target);
@@ -667,30 +403,15 @@ public class GLES20Canvas implements GLCanvas {
setPosition(params, OFFSET_FILL_RECT);
GLES20.glUniformMatrix4fv(params[INDEX_TEXTURE_MATRIX].handle, 1, false, textureMatrix, 0);
checkError();
- if (texture.isFlippedVertically()) {
- save(SAVE_FLAG_MATRIX);
- translate(0, target.centerY());
- scale(1, -1, 1);
- translate(0, -target.centerY());
- }
draw(params, GLES20.GL_TRIANGLE_STRIP, COUNT_FILL_VERTEX, target.left, target.top,
target.width(), target.height());
- if (texture.isFlippedVertically()) {
- restore();
- }
- mCountTextureRect++;
}
private ShaderParameter[] prepareTexture(BasicTexture texture) {
ShaderParameter[] params;
int program;
- if (texture.getTarget() == GLES20.GL_TEXTURE_2D) {
- params = mTextureParameters;
- program = mTextureProgram;
- } else {
- params = mOesTextureParameters;
- program = mOesTextureProgram;
- }
+ params = mTextureParameters;
+ program = mTextureProgram;
prepareTexture(texture, program, params);
return params;
}
@@ -699,86 +420,17 @@ public class GLES20Canvas implements GLCanvas {
deleteRecycledResources();
GLES20.glUseProgram(program);
checkError();
- enableBlending(!texture.isOpaque() || getAlpha() < OPAQUE_ALPHA);
+ GLES20.glDisable(GLES20.GL_BLEND);
+ checkError();
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
checkError();
texture.onBind(this);
- GLES20.glBindTexture(texture.getTarget(), texture.getId());
+ GLES20.glBindTexture(GL_TARGET, texture.getId());
checkError();
GLES20.glUniform1i(params[INDEX_TEXTURE_SAMPLER].handle, 0);
checkError();
- GLES20.glUniform1f(params[INDEX_ALPHA].handle, getAlpha());
- checkError();
- }
-
- @Override
- public void drawMesh(BasicTexture texture, int x, int y, int xyBuffer, int uvBuffer,
- int indexBuffer, int indexCount) {
- prepareTexture(texture, mMeshProgram, mMeshParameters);
-
- GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
- checkError();
-
- GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, xyBuffer);
- checkError();
- int positionHandle = mMeshParameters[INDEX_POSITION].handle;
- GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false,
- VERTEX_STRIDE, 0);
- checkError();
-
- GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, uvBuffer);
- checkError();
- int texCoordHandle = mMeshParameters[INDEX_TEXTURE_COORD].handle;
- GLES20.glVertexAttribPointer(texCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
- false, VERTEX_STRIDE, 0);
- checkError();
- GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
- checkError();
-
- GLES20.glEnableVertexAttribArray(positionHandle);
- checkError();
- GLES20.glEnableVertexAttribArray(texCoordHandle);
- checkError();
-
- setMatrix(mMeshParameters, x, y, 1, 1);
- GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, indexCount, GLES20.GL_UNSIGNED_BYTE, 0);
+ GLES20.glUniform1f(params[INDEX_ALPHA].handle, 1);
checkError();
-
- GLES20.glDisableVertexAttribArray(positionHandle);
- checkError();
- GLES20.glDisableVertexAttribArray(texCoordHandle);
- checkError();
- GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
- checkError();
- mCountDrawMesh++;
- }
-
- @Override
- public void drawMixed(BasicTexture texture, int toColor, float ratio, int x, int y, int w, int h) {
- copyTextureCoordinates(texture, mTempSourceRect);
- mTempTargetRect.set(x, y, x + w, y + h);
- drawMixed(texture, toColor, ratio, mTempSourceRect, mTempTargetRect);
- }
-
- @Override
- public void drawMixed(BasicTexture texture, int toColor, float ratio, RectF source, RectF target) {
- if (target.width() <= 0 || target.height() <= 0) {
- return;
- }
- save(SAVE_FLAG_ALPHA);
-
- float currentAlpha = getAlpha();
- float cappedRatio = Math.min(1f, Math.max(0f, ratio));
-
- float textureAlpha = (1f - cappedRatio) * currentAlpha;
- setAlpha(textureAlpha);
- drawTexture(texture, source, target);
-
- float colorAlpha = cappedRatio * currentAlpha;
- setAlpha(colorAlpha);
- fillRect(target.left, target.top, target.width(), target.height(), toColor);
-
- restore();
}
@Override
@@ -793,13 +445,6 @@ public class GLES20Canvas implements GLCanvas {
}
@Override
- public void deleteBuffer(int bufferId) {
- synchronized (mUnboundTextures) {
- mDeleteBuffers.add(bufferId);
- }
- }
-
- @Override
public void deleteRecycledResources() {
synchronized (mUnboundTextures) {
IntArray ids = mUnboundTextures;
@@ -807,134 +452,41 @@ public class GLES20Canvas implements GLCanvas {
mGLId.glDeleteTextures(null, ids.size(), ids.getInternalArray(), 0);
ids.clear();
}
-
- ids = mDeleteBuffers;
- if (ids.size() > 0) {
- mGLId.glDeleteBuffers(null, ids.size(), ids.getInternalArray(), 0);
- ids.clear();
- }
- }
- }
-
- @Override
- public void dumpStatisticsAndClear() {
- String line = String.format("MESH:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d", mCountDrawMesh,
- mCountTextureRect, mCountFillRect, mCountDrawLine);
- mCountDrawMesh = 0;
- mCountTextureRect = 0;
- mCountFillRect = 0;
- mCountDrawLine = 0;
- Log.d(TAG, line);
- }
-
- @Override
- public void endRenderTarget() {
- RawTexture oldTexture = mTargetTextures.remove(mTargetTextures.size() - 1);
- RawTexture texture = getTargetTexture();
- setRenderTarget(oldTexture, texture);
- restore(); // restore matrix and alpha
- }
-
- @Override
- public void beginRenderTarget(RawTexture texture) {
- save(); // save matrix and alpha and blending
- RawTexture oldTexture = getTargetTexture();
- mTargetTextures.add(texture);
- setRenderTarget(oldTexture, texture);
- }
-
- private RawTexture getTargetTexture() {
- return mTargetTextures.get(mTargetTextures.size() - 1);
- }
-
- private void setRenderTarget(BasicTexture oldTexture, RawTexture texture) {
- if (oldTexture == null && texture != null) {
- GLES20.glGenFramebuffers(1, mFrameBuffer, 0);
- checkError();
- GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffer[0]);
- checkError();
- } else if (oldTexture != null && texture == null) {
- GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
- checkError();
- GLES20.glDeleteFramebuffers(1, mFrameBuffer, 0);
- checkError();
- }
-
- if (texture == null) {
- setSize(mScreenWidth, mScreenHeight);
- } else {
- setSize(texture.getWidth(), texture.getHeight());
-
- if (!texture.isLoaded()) {
- texture.prepare(this);
- }
-
- GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
- texture.getTarget(), texture.getId(), 0);
- checkError();
-
- checkFramebufferStatus();
- }
- }
-
- private static void checkFramebufferStatus() {
- int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
- if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
- String msg = "";
- switch (status) {
- case GLES20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
- msg = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
- break;
- case GLES20.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
- msg = "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
- break;
- case GLES20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
- msg = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
- break;
- case GLES20.GL_FRAMEBUFFER_UNSUPPORTED:
- msg = "GL_FRAMEBUFFER_UNSUPPORTED";
- break;
- }
- throw new RuntimeException(msg + ":" + Integer.toHexString(status));
}
}
@Override
public void setTextureParameters(BasicTexture texture) {
- int target = texture.getTarget();
- GLES20.glBindTexture(target, texture.getId());
+ GLES20.glBindTexture(GL_TARGET, texture.getId());
checkError();
- GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
- GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
- GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
- GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
+ GLES20.glTexParameteri(GL_TARGET, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(GL_TARGET, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameterf(GL_TARGET, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
+ GLES20.glTexParameterf(GL_TARGET, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
}
@Override
public void initializeTextureSize(BasicTexture texture, int format, int type) {
- int target = texture.getTarget();
- GLES20.glBindTexture(target, texture.getId());
+ GLES20.glBindTexture(GL_TARGET, texture.getId());
checkError();
int width = texture.getTextureWidth();
int height = texture.getTextureHeight();
- GLES20.glTexImage2D(target, 0, format, width, height, 0, format, type, null);
+ GLES20.glTexImage2D(GL_TARGET, 0, format, width, height, 0, format, type, null);
}
@Override
public void initializeTexture(BasicTexture texture, Bitmap bitmap) {
- int target = texture.getTarget();
- GLES20.glBindTexture(target, texture.getId());
+ GLES20.glBindTexture(GL_TARGET, texture.getId());
checkError();
- GLUtils.texImage2D(target, 0, bitmap, 0);
+ GLUtils.texImage2D(GL_TARGET, 0, bitmap, 0);
}
@Override
public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap,
int format, int type) {
- int target = texture.getTarget();
- GLES20.glBindTexture(target, texture.getId());
+ GLES20.glBindTexture(GL_TARGET, texture.getId());
checkError();
- GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
+ GLUtils.texSubImage2D(GL_TARGET, 0, xOffset, yOffset, bitmap, format, type);
}
@Override
@@ -942,11 +494,6 @@ public class GLES20Canvas implements GLCanvas {
return uploadBuffer(buf, FLOAT_SIZE);
}
- @Override
- public int uploadBuffer(ByteBuffer buf) {
- return uploadBuffer(buf, 1);
- }
-
private int uploadBuffer(Buffer buffer, int elementSize) {
mGLId.glGenBuffers(1, mTempIntArray, 0);
checkError();
@@ -967,40 +514,6 @@ public class GLES20Canvas implements GLCanvas {
}
}
- @SuppressWarnings("unused")
- private static void printMatrix(String message, float[] m, int offset) {
- StringBuilder b = new StringBuilder(message);
- for (int i = 0; i < MATRIX_SIZE; i++) {
- b.append(' ');
- if (i % 4 == 0) {
- b.append('\n');
- }
- b.append(m[offset + i]);
- }
- Log.v(TAG, b.toString());
- }
-
- @Override
- public void recoverFromLightCycle() {
- GLES20.glViewport(0, 0, mWidth, mHeight);
- GLES20.glDisable(GLES20.GL_DEPTH_TEST);
- GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
- checkError();
- }
-
- @Override
- public void getBounds(Rect bounds, int x, int y, int width, int height) {
- Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f);
- Matrix.scaleM(mTempMatrix, 0, width, height, 1f);
- Matrix.multiplyMV(mTempMatrix, MATRIX_SIZE, mTempMatrix, 0, BOUNDS_COORDINATES, 0);
- Matrix.multiplyMV(mTempMatrix, MATRIX_SIZE + 4, mTempMatrix, 0, BOUNDS_COORDINATES, 4);
- bounds.left = Math.round(mTempMatrix[MATRIX_SIZE]);
- bounds.right = Math.round(mTempMatrix[MATRIX_SIZE + 4]);
- bounds.top = Math.round(mTempMatrix[MATRIX_SIZE + 1]);
- bounds.bottom = Math.round(mTempMatrix[MATRIX_SIZE + 5]);
- bounds.sort();
- }
-
@Override
public GLId getGLId() {
return mGLId;
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLPaint.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLPaint.java
deleted file mode 100644
index b26e9ab29..000000000
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLPaint.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.glrenderer;
-
-import com.android.gallery3d.common.Utils;
-
-public class GLPaint {
- private float mLineWidth = 1f;
- private int mColor = 0;
-
- public void setColor(int color) {
- mColor = color;
- }
-
- public int getColor() {
- return mColor;
- }
-
- public void setLineWidth(float width) {
- Utils.assertTrue(width >= 0);
- mLineWidth = width;
- }
-
- public float getLineWidth() {
- return mLineWidth;
- }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/RawTexture.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/RawTexture.java
deleted file mode 100644
index 93f0fdff9..000000000
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/RawTexture.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.glrenderer;
-
-import android.util.Log;
-
-import javax.microedition.khronos.opengles.GL11;
-
-public class RawTexture extends BasicTexture {
- private static final String TAG = "RawTexture";
-
- private final boolean mOpaque;
- private boolean mIsFlipped;
-
- public RawTexture(int width, int height, boolean opaque) {
- mOpaque = opaque;
- setSize(width, height);
- }
-
- @Override
- public boolean isOpaque() {
- return mOpaque;
- }
-
- @Override
- public boolean isFlippedVertically() {
- return mIsFlipped;
- }
-
- public void setIsFlippedVertically(boolean isFlipped) {
- mIsFlipped = isFlipped;
- }
-
- protected void prepare(GLCanvas canvas) {
- GLId glId = canvas.getGLId();
- mId = glId.generateTexture();
- canvas.initializeTextureSize(this, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE);
- canvas.setTextureParameters(this);
- mState = STATE_LOADED;
- setAssociatedCanvas(canvas);
- }
-
- @Override
- protected boolean onBind(GLCanvas canvas) {
- if (isLoaded()) return true;
- Log.w(TAG, "lost the content due to context change");
- return false;
- }
-
- @Override
- public void yield() {
- // we cannot free the texture because we have no backup.
- }
-
- @Override
- protected int getTarget() {
- return GL11.GL_TEXTURE_2D;
- }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/Texture.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/Texture.java
index 3dcae4aec..e71a379bc 100644
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/Texture.java
+++ b/WallpaperPicker/src/com/android/gallery3d/glrenderer/Texture.java
@@ -24,21 +24,14 @@ package com.android.gallery3d.glrenderer;
// This is the current texture hierarchy:
//
// Texture
-// -- ColorTexture
-// -- FadeInTexture
// -- BasicTexture
// -- UploadedTexture
// -- BitmapTexture
// -- Tile
-// -- ResourceTexture
-// -- NinePatchTexture
-// -- CanvasTexture
-// -- StringTexture
//
public interface Texture {
public int getWidth();
public int getHeight();
public void draw(GLCanvas canvas, int x, int y);
public void draw(GLCanvas canvas, int x, int y, int w, int h);
- public boolean isOpaque();
}
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/UploadedTexture.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/UploadedTexture.java
index 8075bf868..607e2a943 100644
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/UploadedTexture.java
+++ b/WallpaperPicker/src/com/android/gallery3d/glrenderer/UploadedTexture.java
@@ -19,14 +19,12 @@ package com.android.gallery3d.glrenderer;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.opengl.GLUtils;
+import android.util.Pair;
import com.android.gallery3d.common.Utils;
-import com.android.launcher3.util.Thunk;
import java.util.HashMap;
-import javax.microedition.khronos.opengles.GL11;
-
// UploadedTextures use a Bitmap for the content of the texture.
//
// Subclasses should implement onGetBitmap() to provide the Bitmap and
@@ -45,89 +43,29 @@ public abstract class UploadedTexture extends BasicTexture {
// To prevent keeping allocation the borders, we store those used borders here.
// Since the length will be power of two, it won't use too much memory.
- private static HashMap<BorderKey, Bitmap> sBorderLines =
- new HashMap<BorderKey, Bitmap>();
- private static BorderKey sBorderKey = new BorderKey();
-
- @SuppressWarnings("unused")
- private static final String TAG = "Texture";
- private boolean mContentValid = true;
+ private static HashMap<BorderKey, Bitmap> sBorderLines = new HashMap<BorderKey, Bitmap>();
- // indicate this textures is being uploaded in background
- private boolean mIsUploading = false;
- private boolean mOpaque = true;
- private boolean mThrottled = false;
- private static int sUploadedCount;
- private static final int UPLOAD_LIMIT = 100;
+ private static class BorderKey extends Pair<Config, Integer> {
+ public BorderKey(Config config, boolean vertical, int length) {
+ super(config, vertical ? length : -length);
+ }
+ }
+ private boolean mContentValid = true;
protected Bitmap mBitmap;
- private int mBorder;
protected UploadedTexture() {
- this(false);
- }
-
- protected UploadedTexture(boolean hasBorder) {
super(null, 0, STATE_UNLOADED);
- if (hasBorder) {
- setBorder(true);
- mBorder = 1;
- }
- }
-
- protected void setIsUploading(boolean uploading) {
- mIsUploading = uploading;
}
- public boolean isUploading() {
- return mIsUploading;
- }
-
- @Thunk static class BorderKey implements Cloneable {
- public boolean vertical;
- public Config config;
- public int length;
-
- @Override
- public int hashCode() {
- int x = config.hashCode() ^ length;
- return vertical ? x : -x;
- }
-
- @Override
- public boolean equals(Object object) {
- if (!(object instanceof BorderKey)) return false;
- BorderKey o = (BorderKey) object;
- return vertical == o.vertical
- && config == o.config && length == o.length;
- }
-
- @Override
- public BorderKey clone() {
- try {
- return (BorderKey) super.clone();
- } catch (CloneNotSupportedException e) {
- throw new AssertionError(e);
- }
- }
- }
-
- protected void setThrottled(boolean throttled) {
- mThrottled = throttled;
- }
-
- private static Bitmap getBorderLine(
- boolean vertical, Config config, int length) {
- BorderKey key = sBorderKey;
- key.vertical = vertical;
- key.config = config;
- key.length = length;
+ private static Bitmap getBorderLine(boolean vertical, Config config, int length) {
+ BorderKey key = new BorderKey(config, vertical, length);
Bitmap bitmap = sBorderLines.get(key);
if (bitmap == null) {
bitmap = vertical
? Bitmap.createBitmap(1, length, config)
: Bitmap.createBitmap(length, 1, config);
- sBorderLines.put(key.clone(), bitmap);
+ sBorderLines.put(key, bitmap);
}
return bitmap;
}
@@ -135,8 +73,8 @@ public abstract class UploadedTexture extends BasicTexture {
private Bitmap getBitmap() {
if (mBitmap == null) {
mBitmap = onGetBitmap();
- int w = mBitmap.getWidth() + mBorder * 2;
- int h = mBitmap.getHeight() + mBorder * 2;
+ int w = mBitmap.getWidth();
+ int h = mBitmap.getHeight();
if (mWidth == UNSPECIFIED) {
setSize(w, h);
}
@@ -186,37 +124,23 @@ public abstract class UploadedTexture extends BasicTexture {
*/
public void updateContent(GLCanvas canvas) {
if (!isLoaded()) {
- if (mThrottled && ++sUploadedCount > UPLOAD_LIMIT) {
- return;
- }
uploadToCanvas(canvas);
} else if (!mContentValid) {
Bitmap bitmap = getBitmap();
int format = GLUtils.getInternalFormat(bitmap);
int type = GLUtils.getType(bitmap);
- canvas.texSubImage2D(this, mBorder, mBorder, bitmap, format, type);
+ canvas.texSubImage2D(this, 0, 0, bitmap, format, type);
freeBitmap();
mContentValid = true;
}
}
- public static void resetUploadLimit() {
- sUploadedCount = 0;
- }
-
- public static boolean uploadLimitReached() {
- return sUploadedCount > UPLOAD_LIMIT;
- }
-
private void uploadToCanvas(GLCanvas canvas) {
-
Bitmap bitmap = getBitmap();
if (bitmap != null) {
try {
int bWidth = bitmap.getWidth();
int bHeight = bitmap.getHeight();
- int width = bWidth + mBorder * 2;
- int height = bHeight + mBorder * 2;
int texWidth = getTextureWidth();
int texHeight = getTextureHeight();
@@ -234,28 +158,18 @@ public abstract class UploadedTexture extends BasicTexture {
Config config = bitmap.getConfig();
canvas.initializeTextureSize(this, format, type);
- canvas.texSubImage2D(this, mBorder, mBorder, bitmap, format, type);
-
- if (mBorder > 0) {
- // Left border
- Bitmap line = getBorderLine(true, config, texHeight);
- canvas.texSubImage2D(this, 0, 0, line, format, type);
-
- // Top border
- line = getBorderLine(false, config, texWidth);
- canvas.texSubImage2D(this, 0, 0, line, format, type);
- }
+ canvas.texSubImage2D(this, 0, 0, bitmap, format, type);
// Right border
- if (mBorder + bWidth < texWidth) {
+ if (bWidth < texWidth) {
Bitmap line = getBorderLine(true, config, texHeight);
- canvas.texSubImage2D(this, mBorder + bWidth, 0, line, format, type);
+ canvas.texSubImage2D(this, bWidth, 0, line, format, type);
}
// Bottom border
- if (mBorder + bHeight < texHeight) {
+ if (bHeight < texHeight) {
Bitmap line = getBorderLine(false, config, texWidth);
- canvas.texSubImage2D(this, 0, mBorder + bHeight, line, format, type);
+ canvas.texSubImage2D(this, 0, bHeight, line, format, type);
}
}
} finally {
@@ -278,20 +192,6 @@ public abstract class UploadedTexture extends BasicTexture {
}
@Override
- protected int getTarget() {
- return GL11.GL_TEXTURE_2D;
- }
-
- public void setOpaque(boolean isOpaque) {
- mOpaque = isOpaque;
- }
-
- @Override
- public boolean isOpaque() {
- return mOpaque;
- }
-
- @Override
public void recycle() {
super.recycle();
if (mBitmap != null) freeBitmap();
diff --git a/WallpaperPicker/src/com/android/launcher3/CropView.java b/WallpaperPicker/src/com/android/launcher3/CropView.java
index e98e23e98..4770a7177 100644
--- a/WallpaperPicker/src/com/android/launcher3/CropView.java
+++ b/WallpaperPicker/src/com/android/launcher3/CropView.java
@@ -31,7 +31,7 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import com.android.photos.views.TiledImageRenderer.TileSource;
import com.android.photos.views.TiledImageView;
-public class CropView extends TiledImageView implements OnScaleGestureListener {
+public class CropView extends TiledImageView implements OnScaleGestureListener {
private ScaleGestureDetector mScaleGestureDetector;
private long mTouchDownTime;
diff --git a/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java b/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java
deleted file mode 100644
index b53fce119..000000000
--- a/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.app.WallpaperInfo;
-import android.app.WallpaperManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
-import android.service.wallpaper.WallpaperService;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.ListAdapter;
-import android.widget.TextView;
-
-import com.android.launcher3.util.Thunk;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-public class LiveWallpaperListAdapter extends BaseAdapter implements ListAdapter {
- private static final String LOG_TAG = "LiveWallpaperListAdapter";
-
- private final LayoutInflater mInflater;
- private final PackageManager mPackageManager;
-
- @Thunk List<LiveWallpaperTile> mWallpapers;
-
- @SuppressWarnings("unchecked")
- public LiveWallpaperListAdapter(Context context) {
- mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mPackageManager = context.getPackageManager();
-
- List<ResolveInfo> list = mPackageManager.queryIntentServices(
- new Intent(WallpaperService.SERVICE_INTERFACE),
- PackageManager.GET_META_DATA);
-
- mWallpapers = new ArrayList<LiveWallpaperTile>();
-
- new LiveWallpaperEnumerator(context).execute(list);
- }
-
- public int getCount() {
- if (mWallpapers == null) {
- return 0;
- }
- return mWallpapers.size();
- }
-
- public LiveWallpaperTile getItem(int position) {
- return mWallpapers.get(position);
- }
-
- public long getItemId(int position) {
- return position;
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- View view;
-
- if (convertView == null) {
- view = mInflater.inflate(R.layout.wallpaper_picker_live_wallpaper_item, parent, false);
- } else {
- view = convertView;
- }
-
- LiveWallpaperTile wallpaperInfo = mWallpapers.get(position);
- wallpaperInfo.setView(view);
- ImageView image = (ImageView) view.findViewById(R.id.wallpaper_image);
- ImageView icon = (ImageView) view.findViewById(R.id.wallpaper_icon);
- if (wallpaperInfo.mThumbnail != null) {
- image.setImageDrawable(wallpaperInfo.mThumbnail);
- icon.setVisibility(View.GONE);
- } else {
- icon.setImageDrawable(wallpaperInfo.mInfo.loadIcon(mPackageManager));
- icon.setVisibility(View.VISIBLE);
- }
-
- TextView label = (TextView) view.findViewById(R.id.wallpaper_item_label);
- label.setText(wallpaperInfo.mInfo.loadLabel(mPackageManager));
-
- return view;
- }
-
- public static class LiveWallpaperTile extends WallpaperPickerActivity.WallpaperTileInfo {
- @Thunk Drawable mThumbnail;
- @Thunk WallpaperInfo mInfo;
- public LiveWallpaperTile(Drawable thumbnail, WallpaperInfo info, Intent intent) {
- mThumbnail = thumbnail;
- mInfo = info;
- }
- @Override
- public void onClick(WallpaperPickerActivity a) {
- Intent preview = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
- preview.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT,
- mInfo.getComponent());
- a.startActivityForResultSafely(preview,
- WallpaperPickerActivity.PICK_WALLPAPER_THIRD_PARTY_ACTIVITY);
- }
- }
-
- private class LiveWallpaperEnumerator extends
- AsyncTask<List<ResolveInfo>, LiveWallpaperTile, Void> {
- private Context mContext;
- private int mWallpaperPosition;
-
- public LiveWallpaperEnumerator(Context context) {
- super();
- mContext = context;
- mWallpaperPosition = 0;
- }
-
- @Override
- protected Void doInBackground(List<ResolveInfo>... params) {
- final PackageManager packageManager = mContext.getPackageManager();
-
- List<ResolveInfo> list = params[0];
-
- Collections.sort(list, new Comparator<ResolveInfo>() {
- final Collator mCollator;
-
- {
- mCollator = Collator.getInstance();
- }
-
- public int compare(ResolveInfo info1, ResolveInfo info2) {
- return mCollator.compare(info1.loadLabel(packageManager),
- info2.loadLabel(packageManager));
- }
- });
-
- for (ResolveInfo resolveInfo : list) {
- WallpaperInfo info = null;
- try {
- info = new WallpaperInfo(mContext, resolveInfo);
- } catch (XmlPullParserException e) {
- Log.w(LOG_TAG, "Skipping wallpaper " + resolveInfo.serviceInfo, e);
- continue;
- } catch (IOException e) {
- Log.w(LOG_TAG, "Skipping wallpaper " + resolveInfo.serviceInfo, e);
- continue;
- }
-
-
- Drawable thumb = info.loadThumbnail(packageManager);
- Intent launchIntent = new Intent(WallpaperService.SERVICE_INTERFACE);
- launchIntent.setClassName(info.getPackageName(), info.getServiceName());
- LiveWallpaperTile wallpaper = new LiveWallpaperTile(thumb, info, launchIntent);
- publishProgress(wallpaper);
- }
- // Send a null object to show loading is finished
- publishProgress((LiveWallpaperTile) null);
-
- return null;
- }
-
- @Override
- protected void onProgressUpdate(LiveWallpaperTile...infos) {
- for (LiveWallpaperTile info : infos) {
- if (info == null) {
- LiveWallpaperListAdapter.this.notifyDataSetChanged();
- break;
- }
- if (info.mThumbnail != null) {
- info.mThumbnail.setDither(true);
- }
- if (mWallpaperPosition < mWallpapers.size()) {
- mWallpapers.set(mWallpaperPosition, info);
- } else {
- mWallpapers.add(info);
- }
- mWallpaperPosition++;
- }
- }
- }
-}
diff --git a/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java b/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java
index 64b0ac466..9124e414a 100644
--- a/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java
+++ b/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java
@@ -26,29 +26,24 @@ import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
-import android.util.Pair;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ListAdapter;
+
+import com.android.launcher3.wallpapertileinfo.FileWallpaperInfo;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.List;
+public class SavedWallpaperImages {
-public class SavedWallpaperImages extends BaseAdapter implements ListAdapter {
private static String TAG = "Launcher3.SavedWallpaperImages";
- private ImageDb mDb;
- ArrayList<SavedWallpaperTile> mImages;
- Context mContext;
- LayoutInflater mLayoutInflater;
- public static class SavedWallpaperTile extends WallpaperPickerActivity.FileWallpaperInfo {
+ public static class SavedWallpaperInfo extends FileWallpaperInfo {
+
private int mDbId;
- public SavedWallpaperTile(int dbId, File target, Drawable thumb) {
+
+ public SavedWallpaperInfo(int dbId, File target, Drawable thumb) {
super(target, thumb);
mDbId = dbId;
}
@@ -59,19 +54,22 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter {
}
}
+ private final ImageDb mDb;
+ private final Context mContext;
+
public SavedWallpaperImages(Context context) {
// We used to store the saved images in the cache directory, but that meant they'd get
// deleted sometimes-- move them to the data directory
ImageDb.moveFromCacheDirectoryIfNecessary(context);
mDb = new ImageDb(context);
mContext = context;
- mLayoutInflater = LayoutInflater.from(context);
}
- public void loadThumbnailsAndImageIdList() {
- mImages = new ArrayList<SavedWallpaperTile>();
+ public List<SavedWallpaperInfo> loadThumbnailsAndImageIdList() {
+ List<SavedWallpaperInfo> result = new ArrayList<SavedWallpaperInfo>();
+
SQLiteDatabase db = mDb.getReadableDatabase();
- Cursor result = db.query(ImageDb.TABLE_NAME,
+ Cursor c = db.query(ImageDb.TABLE_NAME,
new String[] { ImageDb.COLUMN_ID,
ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME,
ImageDb.COLUMN_IMAGE_FILENAME}, // cols to return
@@ -82,43 +80,24 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter {
ImageDb.COLUMN_ID + " DESC",
null);
- while (result.moveToNext()) {
- String filename = result.getString(1);
+ while (c.moveToNext()) {
+ String filename = c.getString(1);
File file = new File(mContext.getFilesDir(), filename);
Bitmap thumb = BitmapFactory.decodeFile(file.getAbsolutePath());
if (thumb != null) {
- mImages.add(new SavedWallpaperTile(result.getInt(0),
- new File(mContext.getFilesDir(), result.getString(2)),
- new BitmapDrawable(thumb)));
+ result.add(new SavedWallpaperInfo(c.getInt(0),
+ new File(mContext.getFilesDir(), c.getString(2)),
+ new BitmapDrawable(mContext.getResources(), thumb)));
}
}
- result.close();
- }
-
- public int getCount() {
- return mImages.size();
+ c.close();
+ return result;
}
- public SavedWallpaperTile getItem(int position) {
- return mImages.get(position);
- }
-
- public long getItemId(int position) {
- return position;
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- Drawable thumbDrawable = mImages.get(position).mThumb;
- if (thumbDrawable == null) {
- Log.e(TAG, "Error decoding thumbnail for wallpaper #" + position);
- }
- return WallpaperPickerActivity.createImageTileView(
- mLayoutInflater, convertView, parent, thumbDrawable);
- }
+ public void deleteImage(int id) {
+ SQLiteDatabase db = mDb.getWritableDatabase();
- private Pair<String, String> getImageFilenames(int id) {
- SQLiteDatabase db = mDb.getReadableDatabase();
Cursor result = db.query(ImageDb.TABLE_NAME,
new String[] { ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME,
ImageDb.COLUMN_IMAGE_FILENAME }, // cols to return
@@ -128,24 +107,12 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter {
null,
null,
null);
- if (result.getCount() > 0) {
- result.moveToFirst();
- String thumbFilename = result.getString(0);
- String imageFilename = result.getString(1);
- result.close();
- return new Pair<String, String>(thumbFilename, imageFilename);
- } else {
- return null;
+ if (result.moveToFirst()) {
+ new File(mContext.getFilesDir(), result.getString(0)).delete();
+ new File(mContext.getFilesDir(), result.getString(1)).delete();
}
- }
+ result.close();
- public void deleteImage(int id) {
- Pair<String, String> filenames = getImageFilenames(id);
- File imageFile = new File(mContext.getFilesDir(), filenames.first);
- imageFile.delete();
- File thumbFile = new File(mContext.getFilesDir(), filenames.second);
- thumbFile.delete();
- SQLiteDatabase db = mDb.getWritableDatabase();
db.delete(ImageDb.TABLE_NAME,
ImageDb.COLUMN_ID + " = ?", // SELECT query
new String[] {
@@ -177,20 +144,16 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter {
}
}
- static class ImageDb extends SQLiteOpenHelper {
+ private static class ImageDb extends SQLiteOpenHelper {
final static int DB_VERSION = 1;
final static String TABLE_NAME = "saved_wallpaper_images";
final static String COLUMN_ID = "id";
final static String COLUMN_IMAGE_THUMBNAIL_FILENAME = "image_thumbnail";
final static String COLUMN_IMAGE_FILENAME = "image";
- Context mContext;
-
public ImageDb(Context context) {
super(context, context.getDatabasePath(LauncherFiles.WALLPAPER_IMAGES_DB).getPath(),
null, DB_VERSION);
- // Store the context for later use
- mContext = context;
}
public static void moveFromCacheDirectoryIfNecessary(Context context) {
diff --git a/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java b/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java
deleted file mode 100644
index 099bbda7b..000000000
--- a/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ListAdapter;
-import android.widget.TextView;
-
-import com.android.launcher3.util.Thunk;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class ThirdPartyWallpaperPickerListAdapter extends BaseAdapter implements ListAdapter {
- private final LayoutInflater mInflater;
- private final PackageManager mPackageManager;
- private final int mIconSize;
-
- private List<ThirdPartyWallpaperTile> mThirdPartyWallpaperPickers =
- new ArrayList<ThirdPartyWallpaperTile>();
-
- public static class ThirdPartyWallpaperTile extends WallpaperPickerActivity.WallpaperTileInfo {
- @Thunk ResolveInfo mResolveInfo;
- public ThirdPartyWallpaperTile(ResolveInfo resolveInfo) {
- mResolveInfo = resolveInfo;
- }
- @Override
- public void onClick(WallpaperPickerActivity a) {
- final ComponentName itemComponentName = new ComponentName(
- mResolveInfo.activityInfo.packageName, mResolveInfo.activityInfo.name);
- Intent launchIntent = new Intent(Intent.ACTION_SET_WALLPAPER);
- launchIntent.setComponent(itemComponentName)
- .putExtra(WallpaperPickerActivity.EXTRA_WALLPAPER_OFFSET,
- a.getWallpaperParallaxOffset());
- a.startActivityForResultSafely(
- launchIntent, WallpaperPickerActivity.PICK_WALLPAPER_THIRD_PARTY_ACTIVITY);
- }
- }
-
- public ThirdPartyWallpaperPickerListAdapter(Context context) {
- mInflater = LayoutInflater.from(context);
- mPackageManager = context.getPackageManager();
- mIconSize = context.getResources().getDimensionPixelSize(R.dimen.wallpaperItemIconSize);
- final PackageManager pm = mPackageManager;
-
- final Intent pickWallpaperIntent = new Intent(Intent.ACTION_SET_WALLPAPER);
- final List<ResolveInfo> apps =
- pm.queryIntentActivities(pickWallpaperIntent, 0);
-
- // Get list of image picker intents
- Intent pickImageIntent = new Intent(Intent.ACTION_GET_CONTENT);
- pickImageIntent.setType("image/*");
- final List<ResolveInfo> imagePickerActivities =
- pm.queryIntentActivities(pickImageIntent, 0);
- final ComponentName[] imageActivities = new ComponentName[imagePickerActivities.size()];
- for (int i = 0; i < imagePickerActivities.size(); i++) {
- ActivityInfo activityInfo = imagePickerActivities.get(i).activityInfo;
- imageActivities[i] = new ComponentName(activityInfo.packageName, activityInfo.name);
- }
-
- outerLoop:
- for (ResolveInfo info : apps) {
- final ComponentName itemComponentName =
- new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
- final String itemPackageName = itemComponentName.getPackageName();
- // Exclude anything from our own package, and the old Launcher,
- // and live wallpaper picker
- if (itemPackageName.equals(context.getPackageName()) ||
- itemPackageName.equals("com.android.launcher") ||
- itemPackageName.equals("com.android.wallpaper.livepicker")) {
- continue;
- }
- // Exclude any package that already responds to the image picker intent
- for (ResolveInfo imagePickerActivityInfo : imagePickerActivities) {
- if (itemPackageName.equals(
- imagePickerActivityInfo.activityInfo.packageName)) {
- continue outerLoop;
- }
- }
- mThirdPartyWallpaperPickers.add(new ThirdPartyWallpaperTile(info));
- }
- }
-
- public int getCount() {
- return mThirdPartyWallpaperPickers.size();
- }
-
- public ThirdPartyWallpaperTile getItem(int position) {
- return mThirdPartyWallpaperPickers.get(position);
- }
-
- public long getItemId(int position) {
- return position;
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- View view;
-
- if (convertView == null) {
- view = mInflater.inflate(R.layout.wallpaper_picker_third_party_item, parent, false);
- } else {
- view = convertView;
- }
-
- ResolveInfo info = mThirdPartyWallpaperPickers.get(position).mResolveInfo;
- TextView label = (TextView) view.findViewById(R.id.wallpaper_item_label);
- label.setText(info.loadLabel(mPackageManager));
- Drawable icon = info.loadIcon(mPackageManager);
- icon.setBounds(new Rect(0, 0, mIconSize, mIconSize));
- label.setCompoundDrawables(null, icon, null, null);
- return view;
- }
-}
diff --git a/WallpaperPicker/src/com/android/launcher3/ToggleOnTapCallback.java b/WallpaperPicker/src/com/android/launcher3/ToggleOnTapCallback.java
new file mode 100644
index 000000000..2bc48ee17
--- /dev/null
+++ b/WallpaperPicker/src/com/android/launcher3/ToggleOnTapCallback.java
@@ -0,0 +1,67 @@
+package com.android.launcher3;
+
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+
+import com.android.launcher3.util.Thunk;
+
+/**
+ * Callback that toggles the visibility of the target view when crop view is tapped.
+ */
+public class ToggleOnTapCallback implements CropView.TouchCallback {
+
+ @Thunk final View mViewtoToggle;
+
+ private ViewPropertyAnimator mAnim;
+ private boolean mIgnoreNextTap;
+
+ public ToggleOnTapCallback(View viewtoHide) {
+ mViewtoToggle = viewtoHide;
+ }
+
+ @Override
+ public void onTouchDown() {
+ if (mAnim != null) {
+ mAnim.cancel();
+ }
+ if (mViewtoToggle.getAlpha() == 1f) {
+ mIgnoreNextTap = true;
+ }
+
+ mAnim = mViewtoToggle.animate();
+ mAnim.alpha(0f)
+ .setDuration(150)
+ .withEndAction(new Runnable() {
+ public void run() {
+ mViewtoToggle.setVisibility(View.INVISIBLE);
+ }
+ });
+
+ mAnim.setInterpolator(new AccelerateInterpolator(0.75f));
+ mAnim.start();
+ }
+
+ @Override
+ public void onTouchUp() {
+ mIgnoreNextTap = false;
+ }
+
+ @Override
+ public void onTap() {
+ boolean ignoreTap = mIgnoreNextTap;
+ mIgnoreNextTap = false;
+ if (!ignoreTap) {
+ if (mAnim != null) {
+ mAnim.cancel();
+ }
+ mViewtoToggle.setVisibility(View.VISIBLE);
+ mAnim = mViewtoToggle.animate();
+ mAnim.alpha(1f)
+ .setDuration(150)
+ .setInterpolator(new DecelerateInterpolator(0.75f));
+ mAnim.start();
+ }
+ }
+}
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
index 9af5f1653..3554ca79e 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
@@ -20,15 +20,14 @@ import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
import android.app.WallpaperManager;
-import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.Configuration;
+import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -58,24 +57,10 @@ import java.util.WeakHashMap;
public class WallpaperCropActivity extends BaseActivity implements Handler.Callback {
private static final String LOGTAG = "Launcher3.CropActivity";
- protected static final String WALLPAPER_WIDTH_KEY = WallpaperUtils.WALLPAPER_WIDTH_KEY;
- protected static final String WALLPAPER_HEIGHT_KEY = WallpaperUtils.WALLPAPER_HEIGHT_KEY;
-
- /**
- * The maximum bitmap size we allow to be returned through the intent.
- * Intents have a maximum of 1MB in total size. However, the Bitmap seems to
- * have some overhead to hit so that we go way below the limit here to make
- * sure the intent stays below 1MB.We should consider just returning a byte
- * array instead of a Bitmap instance to avoid overhead.
- */
- public static final int MAX_BMAP_IN_INTENT = 750000;
- public static final float WALLPAPER_SCREENS_SPAN = WallpaperUtils.WALLPAPER_SCREENS_SPAN;
-
private static final int MSG_LOAD_IMAGE = 1;
protected CropView mCropView;
protected View mProgressView;
- protected Uri mUri;
protected View mSetWallpaperButton;
private HandlerThread mLoaderThread;
@@ -96,7 +81,7 @@ public class WallpaperCropActivity extends BaseActivity implements Handler.Callb
init();
if (!enableRotation()) {
- setRequestedOrientation(Configuration.ORIENTATION_PORTRAIT);
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
}
@@ -124,12 +109,9 @@ public class WallpaperCropActivity extends BaseActivity implements Handler.Callb
@Override
public void onClick(View v) {
actionBar.hide();
- boolean finishActivityWhenDone = true;
// Never fade on finish because we return to the app that started us (e.g.
// Photos), not the home screen.
- boolean shouldFadeOutOnFinish = false;
- cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone,
- shouldFadeOutOnFinish);
+ cropImageAndSetWallpaper(imageUri, null, false /* shouldFadeOutOnFinish */);
}
});
mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
@@ -170,52 +152,71 @@ public class WallpaperCropActivity extends BaseActivity implements Handler.Callb
public boolean handleMessage(Message msg) {
if (msg.what == MSG_LOAD_IMAGE) {
final LoadRequest req = (LoadRequest) msg.obj;
- try {
- req.src.loadInBackground(new InBitmapProvider() {
+ final boolean loadSuccess;
- @Override
- public Bitmap forPixelCount(int count) {
- Bitmap bitmapToReuse = null;
- // Find the smallest bitmap that satisfies the pixel count limit
- synchronized (mReusableBitmaps) {
- int currentBitmapSize = Integer.MAX_VALUE;
- for (Bitmap b : mReusableBitmaps) {
- int bitmapSize = b.getWidth() * b.getHeight();
- if ((bitmapSize >= count) && (bitmapSize < currentBitmapSize)) {
- bitmapToReuse = b;
- currentBitmapSize = bitmapSize;
+ if (req.src == null) {
+ Drawable defaultWallpaper = WallpaperManager.getInstance(this)
+ .getBuiltInDrawable(mCropView.getWidth(), mCropView.getHeight(),
+ false, 0.5f, 0.5f);
+
+ if (defaultWallpaper == null) {
+ loadSuccess = false;
+ Log.w(LOGTAG, "Null default wallpaper encountered.");
+ } else {
+ loadSuccess = true;
+ req.result = new DrawableTileSource(this,
+ defaultWallpaper, DrawableTileSource.MAX_PREVIEW_SIZE);
+ }
+ } else {
+ try {
+ req.src.loadInBackground(new InBitmapProvider() {
+
+ @Override
+ public Bitmap forPixelCount(int count) {
+ Bitmap bitmapToReuse = null;
+ // Find the smallest bitmap that satisfies the pixel count limit
+ synchronized (mReusableBitmaps) {
+ int currentBitmapSize = Integer.MAX_VALUE;
+ for (Bitmap b : mReusableBitmaps) {
+ int bitmapSize = b.getWidth() * b.getHeight();
+ if ((bitmapSize >= count) && (bitmapSize < currentBitmapSize)) {
+ bitmapToReuse = b;
+ currentBitmapSize = bitmapSize;
+ }
}
- }
- if (bitmapToReuse != null) {
- mReusableBitmaps.remove(bitmapToReuse);
+ if (bitmapToReuse != null) {
+ mReusableBitmaps.remove(bitmapToReuse);
+ }
}
+ return bitmapToReuse;
}
- return bitmapToReuse;
+ });
+ } catch (SecurityException securityException) {
+ if (isActivityDestroyed()) {
+ // Temporarily granted permissions are revoked when the activity
+ // finishes, potentially resulting in a SecurityException here.
+ // Even though {@link #isDestroyed} might also return true in different
+ // situations where the configuration changes, we are fine with
+ // catching these cases here as well.
+ return true;
+ } else {
+ // otherwise it had a different cause and we throw it further
+ throw securityException;
}
- });
- } catch (SecurityException securityException) {
- if (isActivityDestroyed()) {
- // Temporarily granted permissions are revoked when the activity
- // finishes, potentially resulting in a SecurityException here.
- // Even though {@link #isDestroyed} might also return true in different
- // situations where the configuration changes, we are fine with
- // catching these cases here as well.
- return true;
- } else {
- // otherwise it had a different cause and we throw it further
- throw securityException;
}
+
+ req.result = new BitmapRegionTileSource(getContext(), req.src,
+ mTempStorageForDecoding);
+ loadSuccess = req.src.getLoadingState() == BitmapSource.State.LOADED;
}
- req.result = new BitmapRegionTileSource(getContext(), req.src, mTempStorageForDecoding);
runOnUiThread(new Runnable() {
@Override
public void run() {
if (req == mCurrentLoadRequest) {
- onLoadRequestComplete(req,
- req.src.getLoadingState() == BitmapSource.State.LOADED);
+ onLoadRequestComplete(req, loadSuccess);
} else {
addReusableBitmap(req.result);
}
@@ -227,7 +228,7 @@ public class WallpaperCropActivity extends BaseActivity implements Handler.Callb
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
- protected boolean isActivityDestroyed() {
+ public boolean isActivityDestroyed() {
return Utilities.ATLEAST_JB_MR1 && isDestroyed();
}
@@ -275,14 +276,16 @@ public class WallpaperCropActivity extends BaseActivity implements Handler.Callb
mProgressView.setVisibility(View.GONE);
}
+ @TargetApi(Build.VERSION_CODES.KITKAT)
public final void setCropViewTileSource(BitmapSource bitmapSource, boolean touchEnabled,
- boolean moveToLeft, CropViewScaleAndOffsetProvider scaleProvider, Runnable postExecute) {
+ boolean moveToLeft, CropViewScaleAndOffsetProvider scaleAndOffsetProvider,
+ Runnable postExecute) {
final LoadRequest req = new LoadRequest();
req.moveToLeft = moveToLeft;
req.src = bitmapSource;
req.touchEnabled = touchEnabled;
req.postExecute = postExecute;
- req.scaleAndOffsetProvider = scaleProvider;
+ req.scaleAndOffsetProvider = scaleAndOffsetProvider;
mCurrentLoadRequest = req;
// Remove any pending requests
@@ -306,62 +309,39 @@ public class WallpaperCropActivity extends BaseActivity implements Handler.Callb
return getResources().getBoolean(R.bool.allow_rotation);
}
- protected void setWallpaper(Uri uri, final boolean finishActivityWhenDone,
- final boolean shouldFadeOutOnFinish) {
+ public void setWallpaper(Uri uri, boolean shouldFadeOutOnFinish) {
int rotation = BitmapUtils.getRotationFromExif(getContext(), uri);
BitmapCropTask cropTask = new BitmapCropTask(
getContext(), uri, null, rotation, 0, 0, true, false, null);
- final Point bounds = cropTask.getImageBounds();
- BitmapCropTask.OnEndCropHandler onEndCrop = new BitmapCropTask.OnEndCropHandler() {
- public void run(boolean cropSucceeded) {
- updateWallpaperDimensions(bounds.x, bounds.y);
- if (finishActivityWhenDone) {
- setResult(Activity.RESULT_OK);
- finish();
- if (cropSucceeded && shouldFadeOutOnFinish) {
- overridePendingTransition(0, R.anim.fade_out);
- }
- }
- }
- };
- cropTask.setOnEndRunnable(onEndCrop);
+ BitmapCropTask.OnEndCropHandler onEndCrop = new CropAndFinishHandler(
+ cropTask.getImageBounds(), shouldFadeOutOnFinish);
+ cropTask.setOnEndCropHandler(onEndCrop);
cropTask.setNoCrop(true);
cropTask.execute();
}
- protected void cropImageAndSetWallpaper(Resources res, int resId,
- final boolean finishActivityWhenDone, final boolean shouldFadeOutOnFinish) {
+ public void cropImageAndSetWallpaper(Resources res, int resId, boolean shouldFadeOutOnFinish) {
// crop this image and scale it down to the default wallpaper size for
// this device
- int rotation = BitmapUtils.getRotationFromExif(res, resId);
+ int rotation = BitmapUtils.getRotationFromExif(res, resId, this);
Point inSize = mCropView.getSourceDimensions();
Point outSize = WallpaperUtils.getDefaultWallpaperSize(getResources(),
getWindowManager());
RectF crop = Utils.getMaxCropRect(
inSize.x, inSize.y, outSize.x, outSize.y, false);
- BitmapCropTask.OnEndCropHandler onEndCrop = new BitmapCropTask.OnEndCropHandler() {
- public void run(boolean cropSucceeded) {
- // Passing 0, 0 will cause launcher to revert to using the
- // default wallpaper size
- updateWallpaperDimensions(0, 0);
- if (finishActivityWhenDone) {
- setResult(Activity.RESULT_OK);
- finish();
- if (cropSucceeded && shouldFadeOutOnFinish) {
- overridePendingTransition(0, R.anim.fade_out);
- }
- }
- }
- };
+ // Passing 0, 0 will cause launcher to revert to using the
+ // default wallpaper size
+ CropAndFinishHandler onEndCrop = new CropAndFinishHandler(new Point(0, 0),
+ shouldFadeOutOnFinish);
BitmapCropTask cropTask = new BitmapCropTask(getContext(), res, resId,
crop, rotation, outSize.x, outSize.y, true, false, onEndCrop);
cropTask.execute();
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
- protected void cropImageAndSetWallpaper(Uri uri,
+ public void cropImageAndSetWallpaper(Uri uri,
BitmapCropTask.OnBitmapCroppedHandler onBitmapCroppedHandler,
- final boolean finishActivityWhenDone, final boolean shouldFadeOutOnFinish) {
+ boolean shouldFadeOutOnFinish) {
// Give some feedback so user knows something is happening.
mProgressView.setVisibility(View.VISIBLE);
@@ -437,21 +417,12 @@ public class WallpaperCropActivity extends BaseActivity implements Handler.Callb
cropRect.top -= expandHeight;
cropRect.bottom += expandHeight;
}
+
final int outWidth = (int) Math.round(cropRect.width() * cropScale);
final int outHeight = (int) Math.round(cropRect.height() * cropScale);
+ CropAndFinishHandler onEndCrop = new CropAndFinishHandler(new Point(outWidth, outHeight),
+ shouldFadeOutOnFinish);
- BitmapCropTask.OnEndCropHandler onEndCrop = new BitmapCropTask.OnEndCropHandler() {
- public void run(boolean cropSucceeded) {
- updateWallpaperDimensions(outWidth, outHeight);
- if (finishActivityWhenDone) {
- setResult(Activity.RESULT_OK);
- finish();
- }
- if (cropSucceeded && shouldFadeOutOnFinish) {
- overridePendingTransition(0, R.anim.fade_out);
- }
- }
- };
BitmapCropTask cropTask = new BitmapCropTask(getContext(), uri,
cropRect, cropRotation, outWidth, outHeight, true, false, onEndCrop);
if (onBitmapCroppedHandler != null) {
@@ -460,20 +431,30 @@ public class WallpaperCropActivity extends BaseActivity implements Handler.Callb
cropTask.execute();
}
- protected void updateWallpaperDimensions(int width, int height) {
- String spKey = LauncherFiles.WALLPAPER_CROP_PREFERENCES_KEY;
- SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
- SharedPreferences.Editor editor = sp.edit();
- if (width != 0 && height != 0) {
- editor.putInt(WALLPAPER_WIDTH_KEY, width);
- editor.putInt(WALLPAPER_HEIGHT_KEY, height);
- } else {
- editor.remove(WALLPAPER_WIDTH_KEY);
- editor.remove(WALLPAPER_HEIGHT_KEY);
+ public class CropAndFinishHandler implements BitmapCropTask.OnEndCropHandler {
+ private final Point mBounds;
+ private boolean mShouldFadeOutOnFinish;
+
+ /**
+ * @param shouldFadeOutOnFinish Whether the wallpaper picker should override the default
+ * exit animation to fade out instead. This should only be set to true if the wallpaper
+ * preview will exactly match the actual wallpaper on the page we are returning to.
+ */
+ public CropAndFinishHandler(Point bounds, boolean shouldFadeOutOnFinish) {
+ mBounds = bounds;
+ mShouldFadeOutOnFinish = shouldFadeOutOnFinish;
+ }
+
+ @Override
+ public void run(boolean cropSucceeded) {
+ WallpaperUtils.saveWallpaperDimensions(mBounds.x, mBounds.y,
+ WallpaperCropActivity.this);
+ setResult(Activity.RESULT_OK);
+ finish();
+ if (cropSucceeded && mShouldFadeOutOnFinish) {
+ overridePendingTransition(0, R.anim.fade_out);
+ }
}
- editor.commit();
- WallpaperUtils.suggestWallpaperDimension(getResources(),
- sp, getWindowManager(), WallpaperManager.getInstance(getContext()), true);
}
static class LoadRequest {
@@ -486,7 +467,7 @@ public class WallpaperCropActivity extends BaseActivity implements Handler.Callb
TileSource result;
}
- interface CropViewScaleAndOffsetProvider {
+ public interface CropViewScaleAndOffsetProvider {
float getScale(Point wallpaperSize, RectF crop);
float getParallaxOffset();
}
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
index 5d4123843..27d60f8b8 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -16,38 +16,22 @@
package com.android.launcher3;
-import android.Manifest;
import android.animation.LayoutTransition;
-import android.annotation.TargetApi;
+import android.annotation.SuppressLint;
import android.app.ActionBar;
import android.app.Activity;
-import android.app.WallpaperManager;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.database.Cursor;
-import android.database.DataSetObserver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.graphics.PorterDuff;
-import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Build;
import android.os.Bundle;
-import android.os.Process;
-import android.provider.MediaStore;
import android.util.Log;
import android.util.Pair;
import android.view.ActionMode;
-import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -55,34 +39,30 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLayoutChangeListener;
+import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
-import android.view.ViewPropertyAnimator;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowManager;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.widget.ArrayAdapter;
-import android.widget.BaseAdapter;
-import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
-import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.Toast;
-import com.android.gallery3d.common.BitmapCropTask;
-import com.android.gallery3d.common.BitmapUtils;
-import com.android.gallery3d.common.Utils;
import com.android.launcher3.util.Thunk;
-import com.android.photos.BitmapRegionTileSource;
-import com.android.photos.BitmapRegionTileSource.BitmapSource;
+import com.android.launcher3.wallpapertileinfo.DefaultWallpaperInfo;
+import com.android.launcher3.wallpapertileinfo.FileWallpaperInfo;
+import com.android.launcher3.wallpapertileinfo.LiveWallpaperInfo;
+import com.android.launcher3.wallpapertileinfo.PickImageInfo;
+import com.android.launcher3.wallpapertileinfo.ResourceWallpaperInfo;
+import com.android.launcher3.wallpapertileinfo.ThirdPartyWallpaperInfo;
+import com.android.launcher3.wallpapertileinfo.UriWallpaperInfo;
+import com.android.launcher3.wallpapertileinfo.WallpaperTileInfo;
import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.util.ArrayList;
+import java.util.List;
-public class WallpaperPickerActivity extends WallpaperCropActivity {
+public class WallpaperPickerActivity extends WallpaperCropActivity
+ implements OnClickListener, OnLongClickListener, ActionMode.Callback {
static final String TAG = "Launcher.WallpaperPickerActivity";
public static final int IMAGE_PICK = 5;
@@ -93,252 +73,22 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
private static final String SELECTED_INDEX = "SELECTED_INDEX";
private static final int FLAG_POST_DELAY_MILLIS = 200;
- @Thunk View mSelectedTile;
- @Thunk boolean mIgnoreNextTap;
- @Thunk OnClickListener mThumbnailOnClickListener;
+ @Thunk
+ View mSelectedTile;
@Thunk LinearLayout mWallpapersView;
@Thunk HorizontalScrollView mWallpaperScrollContainer;
@Thunk View mWallpaperStrip;
- @Thunk ActionMode.Callback mActionModeCallback;
@Thunk ActionMode mActionMode;
- @Thunk View.OnLongClickListener mLongClickListener;
-
ArrayList<Uri> mTempWallpaperTiles = new ArrayList<Uri>();
private SavedWallpaperImages mSavedImages;
@Thunk int mSelectedIndex = -1;
private float mWallpaperParallaxOffset;
- public static abstract class WallpaperTileInfo {
- protected View mView;
- public Drawable mThumb;
-
- public void setView(View v) {
- mView = v;
- }
- public void onClick(WallpaperPickerActivity a) {}
- public void onSave(WallpaperPickerActivity a) {}
- public void onDelete(WallpaperPickerActivity a) {}
- public boolean isSelectable() { return false; }
- public boolean isNamelessWallpaper() { return false; }
- public void onIndexUpdated(CharSequence label) {
- if (isNamelessWallpaper()) {
- mView.setContentDescription(label);
- }
- }
- }
-
- public static class PickImageInfo extends WallpaperTileInfo {
- @Override
- public void onClick(WallpaperPickerActivity a) {
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
- intent.setType("image/*");
- a.startActivityForResultSafely(intent, IMAGE_PICK);
- }
- }
-
- public static class UriWallpaperInfo extends WallpaperTileInfo {
- private Uri mUri;
- public UriWallpaperInfo(Uri uri) {
- mUri = uri;
- }
- @Override
- public void onClick(final WallpaperPickerActivity a) {
- a.setWallpaperButtonEnabled(false);
- final BitmapRegionTileSource.UriBitmapSource bitmapSource =
- new BitmapRegionTileSource.UriBitmapSource(a.getContext(), mUri);
- a.setCropViewTileSource(bitmapSource, true, false, null, new Runnable() {
-
- @Override
- public void run() {
- if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
- a.selectTile(mView);
- a.setWallpaperButtonEnabled(true);
- } else {
- ViewGroup parent = (ViewGroup) mView.getParent();
- if (parent != null) {
- parent.removeView(mView);
- Toast.makeText(a.getContext(), R.string.image_load_fail,
- Toast.LENGTH_SHORT).show();
- }
- }
- }
- });
- }
- @Override
- public void onSave(final WallpaperPickerActivity a) {
- boolean finishActivityWhenDone = true;
- BitmapCropTask.OnBitmapCroppedHandler h = new BitmapCropTask.OnBitmapCroppedHandler() {
- public void onBitmapCropped(byte[] imageBytes) {
- Point thumbSize = getDefaultThumbnailSize(a.getResources());
- // rotation is set to 0 since imageBytes has already been correctly rotated
- Bitmap thumb = createThumbnail(
- thumbSize, null, null, imageBytes, null, 0, 0, true);
- a.getSavedImages().writeImage(thumb, imageBytes);
- }
- };
- boolean shouldFadeOutOnFinish = a.getWallpaperParallaxOffset() == 0f;
- a.cropImageAndSetWallpaper(mUri, h, finishActivityWhenDone, shouldFadeOutOnFinish);
- }
- @Override
- public boolean isSelectable() {
- return true;
- }
- @Override
- public boolean isNamelessWallpaper() {
- return true;
- }
- }
-
- public static class FileWallpaperInfo extends WallpaperTileInfo {
- private File mFile;
-
- public FileWallpaperInfo(File target, Drawable thumb) {
- mFile = target;
- mThumb = thumb;
- }
- @Override
- public void onClick(final WallpaperPickerActivity a) {
- a.setWallpaperButtonEnabled(false);
- final BitmapRegionTileSource.UriBitmapSource bitmapSource =
- new BitmapRegionTileSource.UriBitmapSource(a.getContext(), Uri.fromFile(mFile));
- a.setCropViewTileSource(bitmapSource, false, true, null, new Runnable() {
-
- @Override
- public void run() {
- if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
- a.setWallpaperButtonEnabled(true);
- }
- }
- });
- }
- @Override
- public void onSave(WallpaperPickerActivity a) {
- boolean shouldFadeOutOnFinish = a.getWallpaperParallaxOffset() == 0f;
- a.setWallpaper(Uri.fromFile(mFile), true, shouldFadeOutOnFinish);
- }
- @Override
- public boolean isSelectable() {
- return true;
- }
- @Override
- public boolean isNamelessWallpaper() {
- return true;
- }
- }
-
- public static class ResourceWallpaperInfo extends WallpaperTileInfo {
- private Resources mResources;
- private int mResId;
-
- public ResourceWallpaperInfo(Resources res, int resId, Drawable thumb) {
- mResources = res;
- mResId = resId;
- mThumb = thumb;
- }
- @Override
- public void onClick(final WallpaperPickerActivity a) {
- a.setWallpaperButtonEnabled(false);
- final BitmapRegionTileSource.ResourceBitmapSource bitmapSource =
- new BitmapRegionTileSource.ResourceBitmapSource(mResources, mResId);
- a.setCropViewTileSource(bitmapSource, false, false, new CropViewScaleAndOffsetProvider() {
-
- @Override
- public float getScale(Point wallpaperSize, RectF crop) {
- return wallpaperSize.x /crop.width();
- }
-
- @Override
- public float getParallaxOffset() {
- return a.getWallpaperParallaxOffset();
- }
- }, new Runnable() {
-
- @Override
- public void run() {
- if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
- a.setWallpaperButtonEnabled(true);
- }
- }
- });
- }
- @Override
- public void onSave(WallpaperPickerActivity a) {
- boolean finishActivityWhenDone = true;
- boolean shouldFadeOutOnFinish = true;
- a.cropImageAndSetWallpaper(mResources, mResId, finishActivityWhenDone,
- shouldFadeOutOnFinish);
- }
- @Override
- public boolean isSelectable() {
- return true;
- }
- @Override
- public boolean isNamelessWallpaper() {
- return true;
- }
- }
-
- @TargetApi(Build.VERSION_CODES.KITKAT)
- public static class DefaultWallpaperInfo extends WallpaperTileInfo {
- public DefaultWallpaperInfo(Drawable thumb) {
- mThumb = thumb;
- }
- @Override
- public void onClick(WallpaperPickerActivity a) {
- CropView c = a.getCropView();
- Drawable defaultWallpaper = WallpaperManager.getInstance(a.getContext())
- .getBuiltInDrawable(c.getWidth(), c.getHeight(), false, 0.5f, 0.5f);
- if (defaultWallpaper == null) {
- Log.w(TAG, "Null default wallpaper encountered.");
- c.setTileSource(null, null);
- return;
- }
-
- LoadRequest req = new LoadRequest();
- req.moveToLeft = false;
- req.touchEnabled = false;
- req.scaleAndOffsetProvider = new CropViewScaleAndOffsetProvider() {
-
- @Override
- public float getScale(Point wallpaperSize, RectF crop) {
- return 1f;
- }
-
- @Override
- public float getParallaxOffset() {
- return 0.5f;
- }
- };
- req.result = new DrawableTileSource(a.getContext(),
- defaultWallpaper, DrawableTileSource.MAX_PREVIEW_SIZE);
- a.onLoadRequestComplete(req, true);
- }
- @Override
- public void onSave(WallpaperPickerActivity a) {
- try {
- WallpaperManager.getInstance(a.getContext()).clear();
- a.setResult(Activity.RESULT_OK);
- } catch (IOException e) {
- Log.w("Setting wallpaper to default threw exception", e);
- }
- a.finish();
- }
- @Override
- public boolean isSelectable() {
- return true;
- }
- @Override
- public boolean isNamelessWallpaper() {
- return true;
- }
- }
-
/**
- * shows the system wallpaper behind the window and hides the {@link
- * #mCropView} if visible
+ * shows the system wallpaper behind the window and hides the {@link #mCropView} if visible
* @param visible should the system wallpaper be shown
*/
protected void setSystemWallpaperVisiblity(final boolean visible) {
@@ -380,7 +130,9 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
}
}
- // called by onCreate; this is subclassed to overwrite WallpaperCropActivity
+ /**
+ * called by onCreate; this is sub-classed to overwrite WallpaperCropActivity
+ */
protected void init() {
setContentView(R.layout.wallpaper_picker);
@@ -390,139 +142,39 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
mProgressView = findViewById(R.id.loading);
mWallpaperScrollContainer = (HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container);
mWallpaperStrip = findViewById(R.id.wallpaper_strip);
- mCropView.setTouchCallback(new CropView.TouchCallback() {
- ViewPropertyAnimator mAnim;
- @Override
- public void onTouchDown() {
- if (mAnim != null) {
- mAnim.cancel();
- }
- if (mWallpaperStrip.getAlpha() == 1f) {
- mIgnoreNextTap = true;
- }
- mAnim = mWallpaperStrip.animate();
- mAnim.alpha(0f)
- .setDuration(150)
- .withEndAction(new Runnable() {
- public void run() {
- mWallpaperStrip.setVisibility(View.INVISIBLE);
- }
- });
- mAnim.setInterpolator(new AccelerateInterpolator(0.75f));
- mAnim.start();
- }
- @Override
- public void onTouchUp() {
- mIgnoreNextTap = false;
- }
- @Override
- public void onTap() {
- boolean ignoreTap = mIgnoreNextTap;
- mIgnoreNextTap = false;
- if (!ignoreTap) {
- if (mAnim != null) {
- mAnim.cancel();
- }
- mWallpaperStrip.setVisibility(View.VISIBLE);
- mAnim = mWallpaperStrip.animate();
- mAnim.alpha(1f)
- .setDuration(150)
- .setInterpolator(new DecelerateInterpolator(0.75f));
- mAnim.start();
- }
- }
- });
-
- mThumbnailOnClickListener = new OnClickListener() {
- public void onClick(View v) {
- if (mActionMode != null) {
- // When CAB is up, clicking toggles the item instead
- if (v.isLongClickable()) {
- mLongClickListener.onLongClick(v);
- }
- return;
- }
- setWallpaperButtonEnabled(true);
- WallpaperTileInfo info = (WallpaperTileInfo) v.getTag();
- if (info.isSelectable() && v.getVisibility() == View.VISIBLE) {
- selectTile(v);
- }
- info.onClick(WallpaperPickerActivity.this);
- }
- };
- mLongClickListener = new View.OnLongClickListener() {
- // Called when the user long-clicks on someView
- public boolean onLongClick(View view) {
- CheckableFrameLayout c = (CheckableFrameLayout) view;
- c.toggle();
-
- if (mActionMode != null) {
- mActionMode.invalidate();
- } else {
- // Start the CAB using the ActionMode.Callback defined below
- mActionMode = startActionMode(mActionModeCallback);
- int childCount = mWallpapersView.getChildCount();
- for (int i = 0; i < childCount; i++) {
- mWallpapersView.getChildAt(i).setSelected(false);
- }
- }
- return true;
- }
- };
+ mCropView.setTouchCallback(new ToggleOnTapCallback(mWallpaperStrip));
mWallpaperParallaxOffset = getIntent().getFloatExtra(EXTRA_WALLPAPER_OFFSET, 0);
- // Populate the built-in wallpapers
- ArrayList<WallpaperTileInfo> wallpapers = findBundledWallpapers();
mWallpapersView = (LinearLayout) findViewById(R.id.wallpaper_list);
- SimpleWallpapersAdapter ia = new SimpleWallpapersAdapter(getContext(), wallpapers);
- populateWallpapersFromAdapter(mWallpapersView, ia, false);
-
// Populate the saved wallpapers
mSavedImages = new SavedWallpaperImages(getContext());
- mSavedImages.loadThumbnailsAndImageIdList();
- populateWallpapersFromAdapter(mWallpapersView, mSavedImages, true);
-
- // Populate the live wallpapers
- final LinearLayout liveWallpapersView =
- (LinearLayout) findViewById(R.id.live_wallpaper_list);
- final LiveWallpaperListAdapter a = new LiveWallpaperListAdapter(getContext());
- a.registerDataSetObserver(new DataSetObserver() {
- public void onChanged() {
- liveWallpapersView.removeAllViews();
- populateWallpapersFromAdapter(liveWallpapersView, a, false);
+ populateWallpapers(mWallpapersView, mSavedImages.loadThumbnailsAndImageIdList(), true);
+
+ // Populate the built-in wallpapers
+ ArrayList<WallpaperTileInfo> wallpapers = findBundledWallpapers();
+ populateWallpapers(mWallpapersView, wallpapers, false);
+
+ // Load live wallpapers asynchronously
+ new LiveWallpaperInfo.LoaderTask(this) {
+
+ @Override
+ protected void onPostExecute(List<LiveWallpaperInfo> result) {
+ populateWallpapers((LinearLayout) findViewById(R.id.live_wallpaper_list),
+ result, false);
initializeScrollForRtl();
updateTileIndices();
}
- });
+ }.execute();
// Populate the third-party wallpaper pickers
- final LinearLayout thirdPartyWallpapersView =
- (LinearLayout) findViewById(R.id.third_party_wallpaper_list);
- final ThirdPartyWallpaperPickerListAdapter ta =
- new ThirdPartyWallpaperPickerListAdapter(getContext());
- populateWallpapersFromAdapter(thirdPartyWallpapersView, ta, false);
+ populateWallpapers((LinearLayout) findViewById(R.id.third_party_wallpaper_list),
+ ThirdPartyWallpaperInfo.getAll(this), false /* addLongPressHandler */);
// Add a tile for the Gallery
LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list);
- FrameLayout pickImageTile = (FrameLayout) getLayoutInflater().
- inflate(R.layout.wallpaper_picker_image_picker_item, masterWallpaperList, false);
- masterWallpaperList.addView(pickImageTile, 0);
-
- // Make its background the last photo taken on external storage
- Bitmap lastPhoto = getThumbnailOfLastPhoto();
- if (lastPhoto != null) {
- ImageView galleryThumbnailBg =
- (ImageView) pickImageTile.findViewById(R.id.wallpaper_image);
- galleryThumbnailBg.setImageBitmap(lastPhoto);
- int colorOverlay = getResources().getColor(R.color.wallpaper_picker_translucent_gray);
- galleryThumbnailBg.setColorFilter(colorOverlay, PorterDuff.Mode.SRC_ATOP);
- }
-
- PickImageInfo pickImageInfo = new PickImageInfo();
- pickImageTile.setTag(pickImageInfo);
- pickImageInfo.setView(pickImageTile);
- pickImageTile.setOnClickListener(mThumbnailOnClickListener);
+ masterWallpaperList.addView(
+ createTileView(masterWallpaperList, new PickImageInfo(), false), 0);
// Select the first item; wait for a layout pass so that we initialize the dimensions of
// cropView or the defaultWallpaperView first
@@ -532,8 +184,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if ((right - left) > 0 && (bottom - top) > 0) {
if (mSelectedIndex >= 0 && mSelectedIndex < mWallpapersView.getChildCount()) {
- mThumbnailOnClickListener.onClick(
- mWallpapersView.getChildAt(mSelectedIndex));
+ onClick(mWallpapersView.getChildAt(mSelectedIndex));
setSystemWallpaperVisiblity(false);
}
v.removeOnLayoutChangeListener(this);
@@ -577,95 +228,47 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
}
});
mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
+ }
- // CAB for deleting items
- mActionModeCallback = new ActionMode.Callback() {
- // Called when the action mode is created; startActionMode() was called
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- // Inflate a menu resource providing context menu items
- MenuInflater inflater = mode.getMenuInflater();
- inflater.inflate(R.menu.cab_delete_wallpapers, menu);
- return true;
- }
-
- private int numCheckedItems() {
- int childCount = mWallpapersView.getChildCount();
- int numCheckedItems = 0;
- for (int i = 0; i < childCount; i++) {
- CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
- if (c.isChecked()) {
- numCheckedItems++;
- }
- }
- return numCheckedItems;
- }
-
- // Called each time the action mode is shown. Always called after onCreateActionMode,
- // but may be called multiple times if the mode is invalidated.
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- int numCheckedItems = numCheckedItems();
- if (numCheckedItems == 0) {
- mode.finish();
- return true;
- } else {
- mode.setTitle(getResources().getQuantityString(
- R.plurals.number_of_items_selected, numCheckedItems, numCheckedItems));
- return true;
- }
+ /**
+ * Called when a wallpaper tile is clicked
+ */
+ @Override
+ public void onClick(View v) {
+ if (mActionMode != null) {
+ // When CAB is up, clicking toggles the item instead
+ if (v.isLongClickable()) {
+ onLongClick(v);
}
+ return;
+ }
+ setWallpaperButtonEnabled(true);
+ WallpaperTileInfo info = (WallpaperTileInfo) v.getTag();
+ if (info.isSelectable() && v.getVisibility() == View.VISIBLE) {
+ selectTile(v);
+ }
+ info.onClick(this);
+ }
- // Called when the user selects a contextual menu item
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- int itemId = item.getItemId();
- if (itemId == R.id.menu_delete) {
- int childCount = mWallpapersView.getChildCount();
- ArrayList<View> viewsToRemove = new ArrayList<View>();
- boolean selectedTileRemoved = false;
- for (int i = 0; i < childCount; i++) {
- CheckableFrameLayout c =
- (CheckableFrameLayout) mWallpapersView.getChildAt(i);
- if (c.isChecked()) {
- WallpaperTileInfo info = (WallpaperTileInfo) c.getTag();
- info.onDelete(WallpaperPickerActivity.this);
- viewsToRemove.add(c);
- if (i == mSelectedIndex) {
- selectedTileRemoved = true;
- }
- }
- }
- for (View v : viewsToRemove) {
- mWallpapersView.removeView(v);
- }
- if (selectedTileRemoved) {
- mSelectedIndex = -1;
- mSelectedTile = null;
- setSystemWallpaperVisiblity(true);
- }
- updateTileIndices();
- mode.finish(); // Action picked, so close the CAB
- return true;
- } else {
- return false;
- }
- }
+ /**
+ * Called when a view is long clicked
+ */
+ @Override
+ public boolean onLongClick(View v) {
+ CheckableFrameLayout c = (CheckableFrameLayout) v;
+ c.toggle();
- // Called when the user exits the action mode
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- int childCount = mWallpapersView.getChildCount();
- for (int i = 0; i < childCount; i++) {
- CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
- c.setChecked(false);
- }
- if (mSelectedTile != null) {
- mSelectedTile.setSelected(true);
- }
- mActionMode = null;
+ if (mActionMode != null) {
+ mActionMode.invalidate();
+ } else {
+ // Start the CAB using the ActionMode.Callback defined below
+ mActionMode = startActionMode(this);
+ int childCount = mWallpapersView.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ mWallpapersView.getChildAt(i).setSelected(false);
}
- };
+ }
+ return true;
}
public void setWallpaperButtonEnabled(boolean enabled) {
@@ -676,7 +279,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
return mWallpaperParallaxOffset;
}
- @Thunk void selectTile(View v) {
+ public void selectTile(View v) {
if (mSelectedTile != null) {
mSelectedTile.setSelected(false);
mSelectedTile = null;
@@ -704,35 +307,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
}
}
- protected Bitmap getThumbnailOfLastPhoto() {
- boolean canReadExternalStorage = getActivity().checkPermission(
- Manifest.permission.READ_EXTERNAL_STORAGE, Process.myPid(), Process.myUid()) ==
- PackageManager.PERMISSION_GRANTED;
-
- if (!canReadExternalStorage) {
- // MediaStore.Images.Media.EXTERNAL_CONTENT_URI requires
- // the READ_EXTERNAL_STORAGE permission
- return null;
- }
-
- Cursor cursor = MediaStore.Images.Media.query(getContext().getContentResolver(),
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
- new String[] { MediaStore.Images.ImageColumns._ID,
- MediaStore.Images.ImageColumns.DATE_TAKEN},
- null, null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC LIMIT 1");
-
- Bitmap thumb = null;
- if (cursor != null) {
- if (cursor.moveToNext()) {
- int id = cursor.getInt(0);
- thumb = MediaStore.Images.Thumbnails.getThumbnail(getContext().getContentResolver(),
- id, MediaStore.Images.Thumbnails.MINI_KIND, null);
- }
- cursor.close();
- }
- return thumb;
- }
-
public void onStop() {
super.onStop();
mWallpaperStrip = findViewById(R.id.wallpaper_strip);
@@ -755,21 +329,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
mSelectedIndex = savedInstanceState.getInt(SELECTED_INDEX, -1);
}
- @Thunk void populateWallpapersFromAdapter(ViewGroup parent, BaseAdapter adapter,
- boolean addLongPressHandler) {
- for (int i = 0; i < adapter.getCount(); i++) {
- FrameLayout thumbnail = (FrameLayout) adapter.getView(i, null, parent);
- parent.addView(thumbnail, i);
- WallpaperTileInfo info = (WallpaperTileInfo) adapter.getItem(i);
- thumbnail.setTag(info);
- info.setView(thumbnail);
- if (addLongPressHandler) {
- addLongPressHandler(thumbnail);
- }
- thumbnail.setOnClickListener(mThumbnailOnClickListener);
- }
- }
-
@Thunk void updateTileIndices() {
LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list);
final int childCount = masterWallpaperList.getChildCount();
@@ -811,126 +370,68 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
}
}
- @Thunk static Point getDefaultThumbnailSize(Resources res) {
- return new Point(res.getDimensionPixelSize(R.dimen.wallpaperThumbnailWidth),
- res.getDimensionPixelSize(R.dimen.wallpaperThumbnailHeight));
-
- }
-
- @Thunk static Bitmap createThumbnail(Point size, Context context, Uri uri, byte[] imageBytes,
- Resources res, int resId, int rotation, boolean leftAligned) {
- int width = size.x;
- int height = size.y;
-
- BitmapCropTask cropTask;
- if (uri != null) {
- cropTask = new BitmapCropTask(
- context, uri, null, rotation, width, height, false, true, null);
- } else if (imageBytes != null) {
- cropTask = new BitmapCropTask(
- imageBytes, null, rotation, width, height, false, true, null);
- } else {
- cropTask = new BitmapCropTask(
- context, res, resId, null, rotation, width, height, false, true, null);
- }
- Point bounds = cropTask.getImageBounds();
- if (bounds == null || bounds.x == 0 || bounds.y == 0) {
- return null;
- }
-
- Matrix rotateMatrix = new Matrix();
- rotateMatrix.setRotate(rotation);
- float[] rotatedBounds = new float[] { bounds.x, bounds.y };
- rotateMatrix.mapPoints(rotatedBounds);
- rotatedBounds[0] = Math.abs(rotatedBounds[0]);
- rotatedBounds[1] = Math.abs(rotatedBounds[1]);
-
- RectF cropRect = Utils.getMaxCropRect(
- (int) rotatedBounds[0], (int) rotatedBounds[1], width, height, leftAligned);
- cropTask.setCropBounds(cropRect);
-
- if (cropTask.cropBitmap()) {
- return cropTask.getCroppedBitmap();
- } else {
- return null;
- }
- }
-
private void addTemporaryWallpaperTile(final Uri uri, boolean fromRestore) {
+
// Add a tile for the image picked from Gallery, reusing the existing tile if there is one.
- FrameLayout existingImageThumbnail = null;
+ View imageTile = null;
int indexOfExistingTile = 0;
for (; indexOfExistingTile < mWallpapersView.getChildCount(); indexOfExistingTile++) {
- FrameLayout thumbnail = (FrameLayout) mWallpapersView.getChildAt(indexOfExistingTile);
+ View thumbnail = mWallpapersView.getChildAt(indexOfExistingTile);
Object tag = thumbnail.getTag();
if (tag instanceof UriWallpaperInfo && ((UriWallpaperInfo) tag).mUri.equals(uri)) {
- existingImageThumbnail = thumbnail;
+ imageTile = thumbnail;
break;
}
}
- final FrameLayout pickedImageThumbnail;
- if (existingImageThumbnail != null) {
- pickedImageThumbnail = existingImageThumbnail;
+ final UriWallpaperInfo info;
+ if (imageTile != null) {
// Always move the existing wallpaper to the front so user can see it without scrolling.
mWallpapersView.removeViewAt(indexOfExistingTile);
- mWallpapersView.addView(existingImageThumbnail, 0);
+ info = (UriWallpaperInfo) imageTile.getTag();
} else {
// This is the first time this temporary wallpaper has been added
- pickedImageThumbnail = (FrameLayout) getLayoutInflater()
- .inflate(R.layout.wallpaper_picker_item, mWallpapersView, false);
- pickedImageThumbnail.setVisibility(View.GONE);
- mWallpapersView.addView(pickedImageThumbnail, 0);
+ info = new UriWallpaperInfo(uri);
+ imageTile = createTileView(mWallpapersView, info, true);
mTempWallpaperTiles.add(uri);
}
+ mWallpapersView.addView(imageTile, 0);
+ info.loadThumbnaleAsync(this);
- // Load the thumbnail
- final ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
- final Point defaultSize = getDefaultThumbnailSize(this.getResources());
- final Context context = getContext();
- new AsyncTask<Void, Bitmap, Bitmap>() {
- protected Bitmap doInBackground(Void...args) {
- try {
- int rotation = BitmapUtils.getRotationFromExif(context, uri);
- return createThumbnail(defaultSize, context, uri, null, null, 0, rotation,
- false);
- } catch (SecurityException securityException) {
- if (isActivityDestroyed()) {
- // Temporarily granted permissions are revoked when the activity
- // finishes, potentially resulting in a SecurityException here.
- // Even though {@link #isDestroyed} might also return true in different
- // situations where the configuration changes, we are fine with
- // catching these cases here as well.
- cancel(false);
- } else {
- // otherwise it had a different cause and we throw it further
- throw securityException;
- }
- return null;
- }
- }
- protected void onPostExecute(Bitmap thumb) {
- if (!isCancelled() && thumb != null) {
- image.setImageBitmap(thumb);
- Drawable thumbDrawable = image.getDrawable();
- thumbDrawable.setDither(true);
- pickedImageThumbnail.setVisibility(View.VISIBLE);
- } else {
- Log.e(TAG, "Error loading thumbnail for uri=" + uri);
- }
- }
- }.execute();
-
- UriWallpaperInfo info = new UriWallpaperInfo(uri);
- pickedImageThumbnail.setTag(info);
- info.setView(pickedImageThumbnail);
- addLongPressHandler(pickedImageThumbnail);
updateTileIndices();
- pickedImageThumbnail.setOnClickListener(mThumbnailOnClickListener);
if (!fromRestore) {
- mThumbnailOnClickListener.onClick(pickedImageThumbnail);
+ onClick(imageTile);
}
}
+ @Thunk void populateWallpapers(ViewGroup parent, List<? extends WallpaperTileInfo> wallpapers,
+ boolean addLongPressHandler) {
+ for (WallpaperTileInfo info : wallpapers) {
+ parent.addView(createTileView(parent, info, addLongPressHandler));
+ }
+ }
+
+ private View createTileView(ViewGroup parent, WallpaperTileInfo info, boolean addLongPress) {
+ View view = info.createView(this, getLayoutInflater(), parent);
+ view.setTag(info);
+
+ if (addLongPress) {
+ view.setOnLongClickListener(this);
+
+ // Enable stylus button to also trigger long click.
+ final StylusEventHelper stylusEventHelper =
+ new StylusEventHelper(new SimpleOnStylusPressListener(view), view);
+ view.setOnTouchListener(new View.OnTouchListener() {
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ return stylusEventHelper.onMotionEvent(event);
+ }
+ });
+ }
+ view.setOnClickListener(this);
+ return view;
+ }
+
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == IMAGE_PICK && resultCode == Activity.RESULT_OK) {
if (data != null && data.getData() != null) {
@@ -945,19 +446,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
}
}
- private void addLongPressHandler(View v) {
- v.setOnLongClickListener(mLongClickListener);
-
- // Enable stylus button to also trigger long click.
- final StylusEventHelper stylusEventHelper = new StylusEventHelper(v);
- v.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View view, MotionEvent event) {
- return stylusEventHelper.checkAndPerformStylusEvent(event);
- }
- });
- }
-
private ArrayList<WallpaperTileInfo> findBundledWallpapers() {
final PackageManager pm = getContext().getPackageManager();
final ArrayList<WallpaperTileInfo> bundled = new ArrayList<WallpaperTileInfo>(24);
@@ -994,7 +482,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
File thumbnail = new File(systemDir, name + "_small" + extension);
Bitmap thumb = BitmapFactory.decodeFile(thumbnail.getAbsolutePath());
if (thumb != null) {
- bundled.add(new FileWallpaperInfo(file, new BitmapDrawable(thumb)));
+ bundled.add(new FileWallpaperInfo(
+ file, new BitmapDrawable(getResources(), thumb)));
}
}
}
@@ -1012,8 +501,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
if (partner == null || !partner.hideDefaultWallpaper()) {
// Add an entry for the default wallpaper (stored in system resources)
- WallpaperTileInfo defaultWallpaperInfo = Utilities.ATLEAST_KITKAT
- ? getDefaultWallpaper() : getPreKKDefaultWallpaperInfo();
+ WallpaperTileInfo defaultWallpaperInfo = DefaultWallpaperInfo.get(this);
if (defaultWallpaperInfo != null) {
bundled.add(0, defaultWallpaperInfo);
}
@@ -1021,95 +509,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
return bundled;
}
- private boolean writeImageToFileAsJpeg(File f, Bitmap b) {
- try {
- f.createNewFile();
- FileOutputStream thumbFileStream =
- getContext().openFileOutput(f.getName(), Context.MODE_PRIVATE);
- b.compress(Bitmap.CompressFormat.JPEG, 95, thumbFileStream);
- thumbFileStream.close();
- return true;
- } catch (IOException e) {
- Log.e(TAG, "Error while writing bitmap to file " + e);
- f.delete();
- }
- return false;
- }
-
- private File getDefaultThumbFile() {
- return new File(getContext().getFilesDir(), Build.VERSION.SDK_INT
- + "_" + LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL);
- }
-
- private boolean saveDefaultWallpaperThumb(Bitmap b) {
- // Delete old thumbnails.
- new File(getContext().getFilesDir(), LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL_OLD).delete();
- new File(getContext().getFilesDir(), LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL).delete();
-
- for (int i = Build.VERSION_CODES.JELLY_BEAN; i < Build.VERSION.SDK_INT; i++) {
- new File(getContext().getFilesDir(), i + "_"
- + LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL).delete();
- }
- return writeImageToFileAsJpeg(getDefaultThumbFile(), b);
- }
-
- private ResourceWallpaperInfo getPreKKDefaultWallpaperInfo() {
- Resources sysRes = Resources.getSystem();
- int resId = sysRes.getIdentifier("default_wallpaper", "drawable", "android");
-
- File defaultThumbFile = getDefaultThumbFile();
- Bitmap thumb = null;
- boolean defaultWallpaperExists = false;
- if (defaultThumbFile.exists()) {
- thumb = BitmapFactory.decodeFile(defaultThumbFile.getAbsolutePath());
- defaultWallpaperExists = true;
- } else {
- Resources res = getResources();
- Point defaultThumbSize = getDefaultThumbnailSize(res);
- int rotation = BitmapUtils.getRotationFromExif(res, resId);
- thumb = createThumbnail(
- defaultThumbSize, getContext(), null, null, sysRes, resId, rotation, false);
- if (thumb != null) {
- defaultWallpaperExists = saveDefaultWallpaperThumb(thumb);
- }
- }
- if (defaultWallpaperExists) {
- return new ResourceWallpaperInfo(sysRes, resId, new BitmapDrawable(thumb));
- }
- return null;
- }
-
- @TargetApi(Build.VERSION_CODES.KITKAT)
- private DefaultWallpaperInfo getDefaultWallpaper() {
- File defaultThumbFile = getDefaultThumbFile();
- Bitmap thumb = null;
- boolean defaultWallpaperExists = false;
- if (defaultThumbFile.exists()) {
- thumb = BitmapFactory.decodeFile(defaultThumbFile.getAbsolutePath());
- defaultWallpaperExists = true;
- } else {
- Resources res = getResources();
- Point defaultThumbSize = getDefaultThumbnailSize(res);
- Drawable wallpaperDrawable = WallpaperManager.getInstance(getContext()).getBuiltInDrawable(
- defaultThumbSize.x, defaultThumbSize.y, true, 0.5f, 0.5f);
- if (wallpaperDrawable != null) {
- thumb = Bitmap.createBitmap(
- defaultThumbSize.x, defaultThumbSize.y, Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(thumb);
- wallpaperDrawable.setBounds(0, 0, defaultThumbSize.x, defaultThumbSize.y);
- wallpaperDrawable.draw(c);
- c.setBitmap(null);
- }
- if (thumb != null) {
- defaultWallpaperExists = saveDefaultWallpaperThumb(thumb);
- }
- }
- if (defaultWallpaperExists) {
- return new DefaultWallpaperInfo(new BitmapDrawable(thumb));
- }
- return null;
- }
-
public Pair<ApplicationInfo, Integer> getWallpaperArrayResourceId() {
// Context.getPackageName() may return the "original" package name,
// com.android.launcher3; Resources needs the real package name,
@@ -1144,62 +543,111 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
}
}
- public CropView getCropView() {
- return mCropView;
- }
-
public SavedWallpaperImages getSavedImages() {
return mSavedImages;
}
- private static class SimpleWallpapersAdapter extends ArrayAdapter<WallpaperTileInfo> {
- private final LayoutInflater mLayoutInflater;
-
- SimpleWallpapersAdapter(Context context, ArrayList<WallpaperTileInfo> wallpapers) {
- super(context, R.layout.wallpaper_picker_item, wallpapers);
- mLayoutInflater = LayoutInflater.from(context);
- }
+ public void startActivityForResultSafely(Intent intent, int requestCode) {
+ Utilities.startActivityForResultSafely(getActivity(), intent, requestCode);
+ }
- public View getView(int position, View convertView, ViewGroup parent) {
- Drawable thumb = getItem(position).mThumb;
- if (thumb == null) {
- Log.e(TAG, "Error decoding thumbnail for wallpaper #" + position);
- }
- return createImageTileView(mLayoutInflater, convertView, parent, thumb);
- }
+ @Override
+ public boolean enableRotation() {
+ return super.enableRotation() ||
+ getContentResolver().call(LauncherSettings.Settings.CONTENT_URI,
+ LauncherSettings.Settings.METHOD_GET_BOOLEAN,
+ Utilities.ALLOW_ROTATION_PREFERENCE_KEY, new Bundle())
+ .getBoolean(LauncherSettings.Settings.EXTRA_VALUE);
}
- public static View createImageTileView(LayoutInflater layoutInflater,
- View convertView, ViewGroup parent, Drawable thumb) {
- View view;
+ // CAB for deleting items
+ /**
+ * Called when the action mode is created; startActionMode() was called
+ */
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ // Inflate a menu resource providing context menu items
+ MenuInflater inflater = mode.getMenuInflater();
+ inflater.inflate(R.menu.cab_delete_wallpapers, menu);
+ return true;
+ }
- if (convertView == null) {
- view = layoutInflater.inflate(R.layout.wallpaper_picker_item, parent, false);
- } else {
- view = convertView;
+ /**
+ * Called each time the action mode is shown. Always called after onCreateActionMode,
+ * but may be called multiple times if the mode is invalidated.
+ */
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ int childCount = mWallpapersView.getChildCount();
+ int numCheckedItems = 0;
+ for (int i = 0; i < childCount; i++) {
+ CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
+ if (c.isChecked()) {
+ numCheckedItems++;
+ }
}
- ImageView image = (ImageView) view.findViewById(R.id.wallpaper_image);
-
- if (thumb != null) {
- image.setImageDrawable(thumb);
- thumb.setDither(true);
+ if (numCheckedItems == 0) {
+ mode.finish();
+ return true;
+ } else {
+ mode.setTitle(getResources().getQuantityString(
+ R.plurals.number_of_items_selected, numCheckedItems, numCheckedItems));
+ return true;
}
-
- return view;
- }
-
- public void startActivityForResultSafely(Intent intent, int requestCode) {
- Utilities.startActivityForResultSafely(getActivity(), intent, requestCode);
}
+ /**
+ * Called when the user selects a contextual menu item
+ */
@Override
- public boolean enableRotation() {
- // Check if rotation is enabled for this device.
- if (Utilities.isRotationAllowedForDevice(getContext()))
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ int itemId = item.getItemId();
+ if (itemId == R.id.menu_delete) {
+ int childCount = mWallpapersView.getChildCount();
+ ArrayList<View> viewsToRemove = new ArrayList<View>();
+ boolean selectedTileRemoved = false;
+ for (int i = 0; i < childCount; i++) {
+ CheckableFrameLayout c =
+ (CheckableFrameLayout) mWallpapersView.getChildAt(i);
+ if (c.isChecked()) {
+ WallpaperTileInfo info = (WallpaperTileInfo) c.getTag();
+ info.onDelete(WallpaperPickerActivity.this);
+ viewsToRemove.add(c);
+ if (i == mSelectedIndex) {
+ selectedTileRemoved = true;
+ }
+ }
+ }
+ for (View v : viewsToRemove) {
+ mWallpapersView.removeView(v);
+ }
+ if (selectedTileRemoved) {
+ mSelectedIndex = -1;
+ mSelectedTile = null;
+ setSystemWallpaperVisiblity(true);
+ }
+ updateTileIndices();
+ mode.finish(); // Action picked, so close the CAB
return true;
+ } else {
+ return false;
+ }
+ }
- // Check if the user has specifically enabled rotation via preferences.
- return Utilities.isAllowRotationPrefEnabled(getApplicationContext(), true);
+ /**
+ * Called when the user exits the action mode
+ */
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ int childCount = mWallpapersView.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
+ c.setChecked(false);
+ }
+ if (mSelectedTile != null) {
+ mSelectedTile.setSelected(true);
+ }
+ mActionMode = null;
}
}
diff --git a/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/DefaultWallpaperInfo.java b/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/DefaultWallpaperInfo.java
new file mode 100644
index 000000000..cf4c35f2b
--- /dev/null
+++ b/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/DefaultWallpaperInfo.java
@@ -0,0 +1,164 @@
+package com.android.launcher3.wallpapertileinfo;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.graphics.RectF;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.Log;
+
+import com.android.gallery3d.common.BitmapUtils;
+import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.WallpaperCropActivity.CropViewScaleAndOffsetProvider;
+import com.android.launcher3.WallpaperPickerActivity;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class DefaultWallpaperInfo extends DrawableThumbWallpaperInfo {
+
+ private static final String TAG = "DefaultWallpaperInfo";
+
+ public DefaultWallpaperInfo(Drawable thumb) {
+ super(thumb);
+ }
+
+ @Override
+ public void onClick(WallpaperPickerActivity a) {
+ a.setCropViewTileSource(null, false, false, new CropViewScaleAndOffsetProvider() {
+
+ @Override
+ public float getScale(Point wallpaperSize, RectF crop) {
+ return 1f;
+ }
+
+ @Override
+ public float getParallaxOffset() {
+ return 0.5f;
+ }
+ }, null);
+ }
+
+ @Override
+ public void onSave(WallpaperPickerActivity a) {
+ try {
+ WallpaperManager.getInstance(a.getContext()).clear();
+ a.setResult(Activity.RESULT_OK);
+ } catch (IOException e) {
+ Log.w(TAG, "Setting wallpaper to default threw exception", e);
+ }
+ a.finish();
+ }
+
+ @Override
+ public boolean isSelectable() {
+ return true;
+ }
+
+ @Override
+ public boolean isNamelessWallpaper() {
+ return true;
+ }
+
+ /**
+ * @return the system default wallpaper tile or null
+ */
+ public static WallpaperTileInfo get(Context context) {
+ return Utilities.ATLEAST_KITKAT
+ ? getDefaultWallpaper(context) : getPreKKDefaultWallpaperInfo(context);
+ }
+
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ private static DefaultWallpaperInfo getDefaultWallpaper(Context context) {
+ File defaultThumbFile = getDefaultThumbFile(context);
+ Bitmap thumb = null;
+ boolean defaultWallpaperExists = false;
+ Resources res = context.getResources();
+
+ if (defaultThumbFile.exists()) {
+ thumb = BitmapFactory.decodeFile(defaultThumbFile.getAbsolutePath());
+ defaultWallpaperExists = true;
+ } else {
+ Point defaultThumbSize = getDefaultThumbSize(res);
+ Drawable wallpaperDrawable = WallpaperManager.getInstance(context).getBuiltInDrawable(
+ defaultThumbSize.x, defaultThumbSize.y, true, 0.5f, 0.5f);
+ if (wallpaperDrawable != null) {
+ thumb = Bitmap.createBitmap(
+ defaultThumbSize.x, defaultThumbSize.y, Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(thumb);
+ wallpaperDrawable.setBounds(0, 0, defaultThumbSize.x, defaultThumbSize.y);
+ wallpaperDrawable.draw(c);
+ c.setBitmap(null);
+ }
+ if (thumb != null) {
+ defaultWallpaperExists = saveDefaultWallpaperThumb(context, thumb);
+ }
+ }
+ if (defaultWallpaperExists) {
+ return new DefaultWallpaperInfo(new BitmapDrawable(res, thumb));
+ }
+ return null;
+ }
+
+ private static ResourceWallpaperInfo getPreKKDefaultWallpaperInfo(Context context) {
+ Resources sysRes = Resources.getSystem();
+ Resources res = context.getResources();
+
+ int resId = sysRes.getIdentifier("default_wallpaper", "drawable", "android");
+
+ File defaultThumbFile = getDefaultThumbFile(context);
+ Bitmap thumb = null;
+ boolean defaultWallpaperExists = false;
+ if (defaultThumbFile.exists()) {
+ thumb = BitmapFactory.decodeFile(defaultThumbFile.getAbsolutePath());
+ defaultWallpaperExists = true;
+ } else {
+ int rotation = BitmapUtils.getRotationFromExif(res, resId, context);
+ thumb = createThumbnail(context, null, null, sysRes, resId, rotation, false);
+ if (thumb != null) {
+ defaultWallpaperExists = saveDefaultWallpaperThumb(context, thumb);
+ }
+ }
+ if (defaultWallpaperExists) {
+ return new ResourceWallpaperInfo(sysRes, resId, new BitmapDrawable(res, thumb));
+ }
+ return null;
+ }
+
+ private static File getDefaultThumbFile(Context context) {
+ return new File(context.getFilesDir(), Build.VERSION.SDK_INT
+ + "_" + LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL);
+ }
+
+ private static boolean saveDefaultWallpaperThumb(Context c, Bitmap b) {
+ // Delete old thumbnails.
+ new File(c.getFilesDir(), LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL_OLD).delete();
+ new File(c.getFilesDir(), LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL).delete();
+
+ for (int i = Build.VERSION_CODES.JELLY_BEAN; i < Build.VERSION.SDK_INT; i++) {
+ new File(c.getFilesDir(), i + "_" + LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL).delete();
+ }
+ File f = getDefaultThumbFile(c);
+ try {
+ f.createNewFile();
+ FileOutputStream thumbFileStream = c.openFileOutput(f.getName(), Context.MODE_PRIVATE);
+ b.compress(Bitmap.CompressFormat.JPEG, 95, thumbFileStream);
+ thumbFileStream.close();
+ return true;
+ } catch (IOException e) {
+ Log.e(TAG, "Error while writing bitmap to file " + e);
+ f.delete();
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/DrawableThumbWallpaperInfo.java b/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/DrawableThumbWallpaperInfo.java
new file mode 100644
index 000000000..a55375ddf
--- /dev/null
+++ b/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/DrawableThumbWallpaperInfo.java
@@ -0,0 +1,37 @@
+package com.android.launcher3.wallpapertileinfo;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.android.launcher3.R;
+
+/**
+ * WallpaperTileInfo which uses drawable as the thumbnail.
+ */
+public abstract class DrawableThumbWallpaperInfo extends WallpaperTileInfo {
+
+ private final Drawable mThumb;
+
+ DrawableThumbWallpaperInfo(Drawable thumb) {
+ mThumb = thumb;
+ }
+
+ @Override
+ public View createView(Context context, LayoutInflater inflator, ViewGroup parent) {
+ mView = inflator.inflate(R.layout.wallpaper_picker_item, parent, false);
+ setThumb(mThumb);
+ return mView;
+ }
+
+ public void setThumb(Drawable thumb) {
+ if (mView != null && thumb != null) {
+ thumb.setDither(true);
+ ImageView image = (ImageView) mView.findViewById(R.id.wallpaper_image);
+ image.setImageDrawable(thumb);
+ }
+ }
+}
diff --git a/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/FileWallpaperInfo.java b/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/FileWallpaperInfo.java
new file mode 100644
index 000000000..f6a46fc48
--- /dev/null
+++ b/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/FileWallpaperInfo.java
@@ -0,0 +1,52 @@
+package com.android.launcher3.wallpapertileinfo;
+
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+
+import com.android.launcher3.WallpaperPickerActivity;
+import com.android.photos.BitmapRegionTileSource;
+import com.android.photos.BitmapRegionTileSource.BitmapSource;
+
+import java.io.File;
+
+public class FileWallpaperInfo extends DrawableThumbWallpaperInfo {
+
+ private final File mFile;
+
+ public FileWallpaperInfo(File target, Drawable thumb) {
+ super(thumb);
+ mFile = target;
+ }
+
+ @Override
+ public void onClick(final WallpaperPickerActivity a) {
+ a.setWallpaperButtonEnabled(false);
+ final BitmapRegionTileSource.FilePathBitmapSource bitmapSource =
+ new BitmapRegionTileSource.FilePathBitmapSource(mFile.getAbsolutePath());
+ a.setCropViewTileSource(bitmapSource, false, true, null, new Runnable() {
+
+ @Override
+ public void run() {
+ if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
+ a.setWallpaperButtonEnabled(true);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onSave(WallpaperPickerActivity a) {
+ boolean shouldFadeOutOnFinish = a.getWallpaperParallaxOffset() == 0f;
+ a.setWallpaper(Uri.fromFile(mFile), shouldFadeOutOnFinish);
+ }
+
+ @Override
+ public boolean isSelectable() {
+ return true;
+ }
+
+ @Override
+ public boolean isNamelessWallpaper() {
+ return true;
+ }
+} \ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/LiveWallpaperInfo.java b/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/LiveWallpaperInfo.java
new file mode 100644
index 000000000..d800ba6e1
--- /dev/null
+++ b/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/LiveWallpaperInfo.java
@@ -0,0 +1,118 @@
+package com.android.launcher3.wallpapertileinfo;
+
+import android.app.WallpaperInfo;
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.service.wallpaper.WallpaperService;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.launcher3.R;
+import com.android.launcher3.WallpaperPickerActivity;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class LiveWallpaperInfo extends WallpaperTileInfo {
+
+ private static final String TAG = "LiveWallpaperTile";
+
+ private Drawable mThumbnail;
+ private WallpaperInfo mInfo;
+
+ public LiveWallpaperInfo(Drawable thumbnail, WallpaperInfo info, Intent intent) {
+ mThumbnail = thumbnail;
+ mInfo = info;
+ }
+
+ @Override
+ public void onClick(WallpaperPickerActivity a) {
+ Intent preview = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
+ preview.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT,
+ mInfo.getComponent());
+ a.startActivityForResultSafely(preview,
+ WallpaperPickerActivity.PICK_WALLPAPER_THIRD_PARTY_ACTIVITY);
+ }
+
+ @Override
+ public View createView(Context context, LayoutInflater inflator, ViewGroup parent) {
+ mView = inflator.inflate(R.layout.wallpaper_picker_live_wallpaper_item, parent, false);
+
+ ImageView image = (ImageView) mView.findViewById(R.id.wallpaper_image);
+ ImageView icon = (ImageView) mView.findViewById(R.id.wallpaper_icon);
+ if (mThumbnail != null) {
+ image.setImageDrawable(mThumbnail);
+ icon.setVisibility(View.GONE);
+ } else {
+ icon.setImageDrawable(mInfo.loadIcon(context.getPackageManager()));
+ icon.setVisibility(View.VISIBLE);
+ }
+
+ TextView label = (TextView) mView.findViewById(R.id.wallpaper_item_label);
+ label.setText(mInfo.loadLabel(context.getPackageManager()));
+ return mView;
+ }
+
+ /**
+ * An async task to load various live wallpaper tiles.
+ */
+ public static class LoaderTask extends AsyncTask<Void, Void, List<LiveWallpaperInfo>> {
+ private final Context mContext;
+
+ public LoaderTask(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ protected List<LiveWallpaperInfo> doInBackground(Void... params) {
+ final PackageManager pm = mContext.getPackageManager();
+
+ List<ResolveInfo> list = pm.queryIntentServices(
+ new Intent(WallpaperService.SERVICE_INTERFACE),
+ PackageManager.GET_META_DATA);
+
+ Collections.sort(list, new Comparator<ResolveInfo>() {
+ final Collator mCollator = Collator.getInstance();
+
+ public int compare(ResolveInfo info1, ResolveInfo info2) {
+ return mCollator.compare(info1.loadLabel(pm), info2.loadLabel(pm));
+ }
+ });
+
+ List<LiveWallpaperInfo> result = new ArrayList<>();
+
+ for (ResolveInfo resolveInfo : list) {
+ WallpaperInfo info = null;
+ try {
+ info = new WallpaperInfo(mContext, resolveInfo);
+ } catch (XmlPullParserException | IOException e) {
+ Log.w(TAG, "Skipping wallpaper " + resolveInfo.serviceInfo, e);
+ continue;
+ }
+
+
+ Drawable thumb = info.loadThumbnail(pm);
+ Intent launchIntent = new Intent(WallpaperService.SERVICE_INTERFACE);
+ launchIntent.setClassName(info.getPackageName(), info.getServiceName());
+ result.add(new LiveWallpaperInfo(thumb, info, launchIntent));
+ }
+
+ return result;
+ }
+ }
+} \ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/PickImageInfo.java b/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/PickImageInfo.java
new file mode 100644
index 000000000..9d8cc1cd7
--- /dev/null
+++ b/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/PickImageInfo.java
@@ -0,0 +1,74 @@
+package com.android.launcher3.wallpapertileinfo;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.PorterDuff;
+import android.os.Process;
+import android.provider.MediaStore;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.android.launcher3.R;
+import com.android.launcher3.WallpaperPickerActivity;
+
+public class PickImageInfo extends WallpaperTileInfo {
+
+ @Override
+ public void onClick(WallpaperPickerActivity a) {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT).setType("image/*");
+ a.startActivityForResultSafely(intent, WallpaperPickerActivity.IMAGE_PICK);
+ }
+
+ @Override
+ public View createView(Context context, LayoutInflater inflator, ViewGroup parent) {
+ mView = inflator.inflate(R.layout.wallpaper_picker_image_picker_item, parent, false);
+
+ // Make its background the last photo taken on external storage
+ Bitmap lastPhoto = getThumbnailOfLastPhoto(context);
+ if (lastPhoto != null) {
+ ImageView galleryThumbnailBg =
+ (ImageView) mView.findViewById(R.id.wallpaper_image);
+ galleryThumbnailBg.setImageBitmap(lastPhoto);
+ int colorOverlay = context.getResources().getColor(R.color.wallpaper_picker_translucent_gray);
+ galleryThumbnailBg.setColorFilter(colorOverlay, PorterDuff.Mode.SRC_ATOP);
+ }
+
+ mView.setTag(this);
+ return mView;
+ }
+
+ private Bitmap getThumbnailOfLastPhoto(Context context) {
+ boolean canReadExternalStorage = context.checkPermission(
+ Manifest.permission.READ_EXTERNAL_STORAGE, Process.myPid(), Process.myUid()) ==
+ PackageManager.PERMISSION_GRANTED;
+
+ if (!canReadExternalStorage) {
+ // MediaStore.Images.Media.EXTERNAL_CONTENT_URI requires
+ // the READ_EXTERNAL_STORAGE permission
+ return null;
+ }
+
+ Cursor cursor = MediaStore.Images.Media.query(context.getContentResolver(),
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ new String[] { MediaStore.Images.ImageColumns._ID,
+ MediaStore.Images.ImageColumns.DATE_TAKEN},
+ null, null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC LIMIT 1");
+
+ Bitmap thumb = null;
+ if (cursor != null) {
+ if (cursor.moveToNext()) {
+ int id = cursor.getInt(0);
+ thumb = MediaStore.Images.Thumbnails.getThumbnail(context.getContentResolver(),
+ id, MediaStore.Images.Thumbnails.MINI_KIND, null);
+ }
+ cursor.close();
+ }
+ return thumb;
+ }
+} \ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/ResourceWallpaperInfo.java b/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/ResourceWallpaperInfo.java
new file mode 100644
index 000000000..d63714c22
--- /dev/null
+++ b/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/ResourceWallpaperInfo.java
@@ -0,0 +1,65 @@
+package com.android.launcher3.wallpapertileinfo;
+
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+
+import com.android.launcher3.WallpaperCropActivity.CropViewScaleAndOffsetProvider;
+import com.android.launcher3.WallpaperPickerActivity;
+import com.android.photos.BitmapRegionTileSource;
+import com.android.photos.BitmapRegionTileSource.BitmapSource;
+
+public class ResourceWallpaperInfo extends DrawableThumbWallpaperInfo {
+
+ private final Resources mResources;
+ private final int mResId;
+
+ public ResourceWallpaperInfo(Resources res, int resId, Drawable thumb) {
+ super(thumb);
+ mResources = res;
+ mResId = resId;
+ }
+
+ @Override
+ public void onClick(final WallpaperPickerActivity a) {
+ a.setWallpaperButtonEnabled(false);
+ final BitmapRegionTileSource.ResourceBitmapSource bitmapSource =
+ new BitmapRegionTileSource.ResourceBitmapSource(mResources, mResId, a);
+ a.setCropViewTileSource(bitmapSource, false, false, new CropViewScaleAndOffsetProvider() {
+
+ @Override
+ public float getScale(Point wallpaperSize, RectF crop) {
+ return wallpaperSize.x /crop.width();
+ }
+
+ @Override
+ public float getParallaxOffset() {
+ return a.getWallpaperParallaxOffset();
+ }
+ }, new Runnable() {
+
+ @Override
+ public void run() {
+ if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
+ a.setWallpaperButtonEnabled(true);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onSave(WallpaperPickerActivity a) {
+ a.cropImageAndSetWallpaper(mResources, mResId, true /* shouldFadeOutOnFinish */);
+ }
+
+ @Override
+ public boolean isSelectable() {
+ return true;
+ }
+
+ @Override
+ public boolean isNamelessWallpaper() {
+ return true;
+ }
+} \ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/ThirdPartyWallpaperInfo.java b/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/ThirdPartyWallpaperInfo.java
new file mode 100644
index 000000000..5e2538f84
--- /dev/null
+++ b/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/ThirdPartyWallpaperInfo.java
@@ -0,0 +1,78 @@
+package com.android.launcher3.wallpapertileinfo;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.android.launcher3.R;
+import com.android.launcher3.WallpaperPickerActivity;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+public class ThirdPartyWallpaperInfo extends WallpaperTileInfo {
+
+ private final ResolveInfo mResolveInfo;
+ private final int mIconSize;
+
+ public ThirdPartyWallpaperInfo(ResolveInfo resolveInfo, int iconSize) {
+ mResolveInfo = resolveInfo;
+ mIconSize = iconSize;
+ }
+
+ @Override
+ public void onClick(WallpaperPickerActivity a) {
+ final ComponentName itemComponentName = new ComponentName(
+ mResolveInfo.activityInfo.packageName, mResolveInfo.activityInfo.name);
+ Intent launchIntent = new Intent(Intent.ACTION_SET_WALLPAPER)
+ .setComponent(itemComponentName)
+ .putExtra(WallpaperPickerActivity.EXTRA_WALLPAPER_OFFSET,
+ a.getWallpaperParallaxOffset());
+ a.startActivityForResultSafely(
+ launchIntent, WallpaperPickerActivity.PICK_WALLPAPER_THIRD_PARTY_ACTIVITY);
+ }
+
+ @Override
+ public View createView(Context context, LayoutInflater inflator, ViewGroup parent) {
+ mView = inflator.inflate(R.layout.wallpaper_picker_third_party_item, parent, false);
+
+ TextView label = (TextView) mView.findViewById(R.id.wallpaper_item_label);
+ label.setText(mResolveInfo.loadLabel(context.getPackageManager()));
+ Drawable icon = mResolveInfo.loadIcon(context.getPackageManager());
+ icon.setBounds(new Rect(0, 0, mIconSize, mIconSize));
+ label.setCompoundDrawables(null, icon, null, null);
+ return mView;
+ }
+
+ public static List<ThirdPartyWallpaperInfo> getAll(Context context) {
+ ArrayList<ThirdPartyWallpaperInfo> result = new ArrayList<>();
+ int iconSize = context.getResources().getDimensionPixelSize(R.dimen.wallpaperItemIconSize);
+
+ final PackageManager pm = context.getPackageManager();
+ Intent pickImageIntent = new Intent(Intent.ACTION_GET_CONTENT).setType("image/*");
+ HashSet<String> excludePackages = new HashSet<>();
+ // Exclude packages which contain an image picker
+ for (ResolveInfo info : pm.queryIntentActivities(pickImageIntent, 0)) {
+ excludePackages.add(info.activityInfo.packageName);
+ }
+ excludePackages.add(context.getPackageName());
+ excludePackages.add("com.android.wallpaper.livepicker");
+
+ final Intent pickWallpaperIntent = new Intent(Intent.ACTION_SET_WALLPAPER);
+ for (ResolveInfo info : pm.queryIntentActivities(pickWallpaperIntent, 0)) {
+ if (!excludePackages.contains(info.activityInfo.packageName)) {
+ result.add(new ThirdPartyWallpaperInfo(info, iconSize));
+ }
+ }
+ return result;
+ }
+} \ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/UriWallpaperInfo.java b/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/UriWallpaperInfo.java
new file mode 100644
index 000000000..180eb93e0
--- /dev/null
+++ b/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/UriWallpaperInfo.java
@@ -0,0 +1,109 @@
+package com.android.launcher3.wallpapertileinfo;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import com.android.gallery3d.common.BitmapCropTask;
+import com.android.gallery3d.common.BitmapUtils;
+import com.android.launcher3.R;
+import com.android.launcher3.WallpaperPickerActivity;
+import com.android.photos.BitmapRegionTileSource;
+import com.android.photos.BitmapRegionTileSource.BitmapSource;
+
+public class UriWallpaperInfo extends DrawableThumbWallpaperInfo {
+
+ private static final String TAG = "UriWallpaperInfo";
+
+ public final Uri mUri;
+
+ public UriWallpaperInfo(Uri uri) {
+ super(null);
+ mUri = uri;
+ }
+
+ @Override
+ public void onClick(final WallpaperPickerActivity a) {
+ a.setWallpaperButtonEnabled(false);
+ final BitmapRegionTileSource.UriBitmapSource bitmapSource =
+ new BitmapRegionTileSource.UriBitmapSource(a.getContext(), mUri);
+ a.setCropViewTileSource(bitmapSource, true, false, null, new Runnable() {
+
+ @Override
+ public void run() {
+ if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
+ a.selectTile(mView);
+ a.setWallpaperButtonEnabled(true);
+ } else {
+ ViewGroup parent = (ViewGroup) mView.getParent();
+ if (parent != null) {
+ parent.removeView(mView);
+ Toast.makeText(a.getContext(), R.string.image_load_fail,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onSave(final WallpaperPickerActivity a) {
+ BitmapCropTask.OnBitmapCroppedHandler h = new BitmapCropTask.OnBitmapCroppedHandler() {
+ public void onBitmapCropped(byte[] imageBytes) {
+ // rotation is set to 0 since imageBytes has already been correctly rotated
+ Bitmap thumb = createThumbnail(a, null, imageBytes, null, 0, 0, true);
+ a.getSavedImages().writeImage(thumb, imageBytes);
+ }
+ };
+ boolean shouldFadeOutOnFinish = a.getWallpaperParallaxOffset() == 0f;
+ a.cropImageAndSetWallpaper(mUri, h, shouldFadeOutOnFinish);
+ }
+
+ @Override
+ public boolean isSelectable() {
+ return true;
+ }
+
+ @Override
+ public boolean isNamelessWallpaper() {
+ return true;
+ }
+
+ public void loadThumbnaleAsync(final WallpaperPickerActivity activity) {
+ mView.setVisibility(View.GONE);
+ new AsyncTask<Void, Void, Bitmap>() {
+ protected Bitmap doInBackground(Void...args) {
+ try {
+ int rotation = BitmapUtils.getRotationFromExif(activity, mUri);
+ return createThumbnail(activity, mUri, null, null, 0, rotation, false);
+ } catch (SecurityException securityException) {
+ if (activity.isActivityDestroyed()) {
+ // Temporarily granted permissions are revoked when the activity
+ // finishes, potentially resulting in a SecurityException here.
+ // Even though {@link #isDestroyed} might also return true in different
+ // situations where the configuration changes, we are fine with
+ // catching these cases here as well.
+ cancel(false);
+ } else {
+ // otherwise it had a different cause and we throw it further
+ throw securityException;
+ }
+ return null;
+ }
+ }
+ protected void onPostExecute(Bitmap thumb) {
+ if (!isCancelled() && thumb != null) {
+ setThumb(new BitmapDrawable(activity.getResources(), thumb));
+ mView.setVisibility(View.VISIBLE);
+ } else {
+ Log.e(TAG, "Error loading thumbnail for uri=" + mUri);
+ }
+ }
+ }.execute();
+ }
+} \ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/WallpaperTileInfo.java b/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/WallpaperTileInfo.java
new file mode 100644
index 000000000..5fc317c3a
--- /dev/null
+++ b/WallpaperPicker/src/com/android/launcher3/wallpapertileinfo/WallpaperTileInfo.java
@@ -0,0 +1,87 @@
+package com.android.launcher3.wallpapertileinfo;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.graphics.Point;
+import android.graphics.RectF;
+import android.net.Uri;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.gallery3d.common.BitmapCropTask;
+import com.android.gallery3d.common.Utils;
+import com.android.launcher3.R;
+import com.android.launcher3.WallpaperPickerActivity;
+
+public abstract class WallpaperTileInfo {
+
+ protected View mView;
+
+ public void onClick(WallpaperPickerActivity a) {}
+
+ public void onSave(WallpaperPickerActivity a) {}
+
+ public void onDelete(WallpaperPickerActivity a) {}
+
+ public boolean isSelectable() { return false; }
+
+ public boolean isNamelessWallpaper() { return false; }
+
+ public void onIndexUpdated(CharSequence label) {
+ if (isNamelessWallpaper()) {
+ mView.setContentDescription(label);
+ }
+ }
+
+ public abstract View createView(Context context, LayoutInflater inflator, ViewGroup parent);
+
+ protected static Point getDefaultThumbSize(Resources res) {
+ return new Point(res.getDimensionPixelSize(R.dimen.wallpaperThumbnailWidth),
+ res.getDimensionPixelSize(R.dimen.wallpaperThumbnailHeight));
+
+ }
+
+ protected static Bitmap createThumbnail(Context context, Uri uri, byte[] imageBytes,
+ Resources res, int resId, int rotation, boolean leftAligned) {
+ Point size = getDefaultThumbSize(context.getResources());
+ int width = size.x;
+ int height = size.y;
+
+ BitmapCropTask cropTask;
+ if (uri != null) {
+ cropTask = new BitmapCropTask(
+ context, uri, null, rotation, width, height, false, true, null);
+ } else if (imageBytes != null) {
+ cropTask = new BitmapCropTask(
+ imageBytes, null, rotation, width, height, false, true, null);
+ } else {
+ cropTask = new BitmapCropTask(
+ context, res, resId, null, rotation, width, height, false, true, null);
+ }
+ Point bounds = cropTask.getImageBounds();
+ if (bounds == null || bounds.x == 0 || bounds.y == 0) {
+ return null;
+ }
+
+ Matrix rotateMatrix = new Matrix();
+ rotateMatrix.setRotate(rotation);
+ float[] rotatedBounds = new float[] { bounds.x, bounds.y };
+ rotateMatrix.mapPoints(rotatedBounds);
+ rotatedBounds[0] = Math.abs(rotatedBounds[0]);
+ rotatedBounds[1] = Math.abs(rotatedBounds[1]);
+
+ RectF cropRect = Utils.getMaxCropRect(
+ (int) rotatedBounds[0], (int) rotatedBounds[1], width, height, leftAligned);
+ cropTask.setCropBounds(cropRect);
+
+ if (cropTask.cropBitmap()) {
+ return cropTask.getCroppedBitmap();
+ } else {
+ return null;
+ }
+ }
+
+} \ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
index 6baac6a6b..4267c4123 100644
--- a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
+++ b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
@@ -31,8 +31,8 @@ import android.os.Build;
import android.util.Log;
import com.android.gallery3d.common.BitmapUtils;
+import com.android.gallery3d.common.ExifOrientation;
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;
@@ -161,13 +161,7 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
/** Returns whether loading was successful. */
public boolean loadInBackground(InBitmapProvider bitmapProvider) {
- ExifInterface ei = new ExifInterface();
- if (readExif(ei)) {
- Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
- if (ori != null) {
- mRotation = ExifInterface.getRotationForOrientationValue(ori.shortValue());
- }
- }
+ mRotation = getExifRotation();
mDecoder = loadBitmapRegionDecoder();
if (mDecoder == null) {
mState = State.ERROR_LOADING;
@@ -237,7 +231,7 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
return mRotation;
}
- public abstract boolean readExif(ExifInterface ei);
+ public abstract int getExifRotation();
public abstract SimpleBitmapRegionDecoder loadBitmapRegionDecoder();
public abstract Bitmap loadPreviewBitmap(BitmapFactory.Options options);
@@ -264,18 +258,10 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
return BitmapFactory.decodeFile(mPath, options);
}
+
@Override
- public boolean readExif(ExifInterface ei) {
- try {
- ei.readExif(mPath);
- return true;
- } catch (NullPointerException e) {
- Log.w("BitmapRegionTileSource", "reading exif failed", e);
- return false;
- } catch (IOException e) {
- Log.w("BitmapRegionTileSource", "getting decoder failed", e);
- return false;
- }
+ public int getExifRotation() {
+ return ExifOrientation.readRotation(mPath);
}
}
@@ -320,35 +306,22 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
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.d("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
- return false;
- } catch (IOException e) {
- Log.d("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
- return false;
- } catch (NullPointerException e) {
- Log.d("BitmapRegionTileSource", "Failed to read EXIF for URI " + mUri, e);
- return false;
- } finally {
- Utils.closeSilently(is);
- }
+ public int getExifRotation() {
+ return BitmapUtils.getRotationFromExif(mContext, mUri);
}
}
public static class ResourceBitmapSource extends BitmapSource {
private Resources mRes;
private int mResId;
- public ResourceBitmapSource(Resources res, int resId) {
+ private Context mContext;
+
+ public ResourceBitmapSource(Resources res, int resId, Context context) {
mRes = res;
mResId = resId;
+ mContext = context;
}
private InputStream regenerateInputStream() {
InputStream is = mRes.openRawResource(mResId);
@@ -371,17 +344,10 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
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;
- }
+ public int getExifRotation() {
+ return BitmapUtils.getRotationFromExif(mRes, mResId, mContext);
}
}
diff --git a/WallpaperPicker/src/com/android/photos/views/TiledImageView.java b/WallpaperPicker/src/com/android/photos/views/TiledImageView.java
index 7e3e1a936..759613d98 100644
--- a/WallpaperPicker/src/com/android/photos/views/TiledImageView.java
+++ b/WallpaperPicker/src/com/android/photos/views/TiledImageView.java
@@ -17,12 +17,7 @@
package com.android.photos.views;
import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
import android.graphics.RectF;
import android.opengl.GLSurfaceView;
import android.opengl.GLSurfaceView.Renderer;
@@ -82,7 +77,6 @@ public class TiledImageView extends FrameLayout {
mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
addView(mGLSurfaceView, new LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
- //setTileSource(new ColoredTiles());
}
@Override
@@ -170,40 +164,6 @@ public class TiledImageView extends FrameLayout {
}
}
- private RectF mTempRectF = new RectF();
- public void positionFromMatrix(Matrix matrix) {
- if (mRenderer.source != null) {
- final int rotation = mRenderer.source.getRotation();
- final boolean swap = !(rotation % 180 == 0);
- final int width = swap ? mRenderer.source.getImageHeight()
- : mRenderer.source.getImageWidth();
- final int height = swap ? mRenderer.source.getImageWidth()
- : mRenderer.source.getImageHeight();
- mTempRectF.set(0, 0, width, height);
- matrix.mapRect(mTempRectF);
- matrix.getValues(mValues);
- int cx = width / 2;
- int cy = height / 2;
- float scale = mValues[Matrix.MSCALE_X];
- int xoffset = Math.round((getWidth() - mTempRectF.width()) / 2 / scale);
- int yoffset = Math.round((getHeight() - mTempRectF.height()) / 2 / scale);
- if (rotation == 90 || rotation == 180) {
- cx += (mTempRectF.left / scale) - xoffset;
- } else {
- cx -= (mTempRectF.left / scale) - xoffset;
- }
- if (rotation == 180 || rotation == 270) {
- cy += (mTempRectF.top / scale) - yoffset;
- } else {
- cy -= (mTempRectF.top / scale) - yoffset;
- }
- mRenderer.scale = scale;
- mRenderer.centerX = swap ? cy : cx;
- mRenderer.centerY = swap ? cx : cy;
- invalidate();
- }
- }
-
@Thunk class TileRenderer implements Renderer {
private GLES20Canvas mCanvas;
@@ -247,66 +207,4 @@ public class TiledImageView extends FrameLayout {
}
}
-
- @SuppressWarnings("unused")
- private static class ColoredTiles implements TileSource {
- private static final int[] COLORS = new int[] {
- Color.RED,
- Color.BLUE,
- Color.YELLOW,
- Color.GREEN,
- Color.CYAN,
- Color.MAGENTA,
- Color.WHITE,
- };
-
- private Paint mPaint = new Paint();
- private Canvas mCanvas = new Canvas();
-
- @Override
- public int getTileSize() {
- return 256;
- }
-
- @Override
- public int getImageWidth() {
- return 16384;
- }
-
- @Override
- public int getImageHeight() {
- return 8192;
- }
-
- @Override
- public int getRotation() {
- return 0;
- }
-
- @Override
- public Bitmap getTile(int level, int x, int y, Bitmap bitmap) {
- int tileSize = getTileSize();
- if (bitmap == null) {
- bitmap = Bitmap.createBitmap(tileSize, tileSize,
- Bitmap.Config.ARGB_8888);
- }
- mCanvas.setBitmap(bitmap);
- mCanvas.drawColor(COLORS[level]);
- mPaint.setColor(Color.BLACK);
- mPaint.setTextSize(20);
- mPaint.setTextAlign(Align.CENTER);
- mCanvas.drawText(x + "x" + y, 128, 128, mPaint);
- tileSize <<= level;
- x /= tileSize;
- y /= tileSize;
- mCanvas.drawText(x + "x" + y + " @ " + level, 128, 30, mPaint);
- mCanvas.setBitmap(null);
- return bitmap;
- }
-
- @Override
- public BasicTexture getPreview() {
- return null;
- }
- }
}
diff --git a/proguard.flags b/proguard.flags
index 772580020..05963f7bc 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -39,7 +39,7 @@
public int getY();
}
--keep class com.android.launcher3.DragLayer$LayoutParams {
+-keep class com.android.launcher3.dragndrop.DragLayer$LayoutParams {
public void setWidth(int);
public int getWidth();
public void setHeight(int);
diff --git a/res/animator-v21/overview_button_anim.xml b/res/animator-v21/overview_button_anim.xml
new file mode 100644
index 000000000..aac3d2655
--- /dev/null
+++ b/res/animator-v21/overview_button_anim.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2015, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="true">
+ <objectAnimator
+ android:duration="@android:integer/config_shortAnimTime"
+ android:propertyName="alpha"
+ android:valueTo="0.5"
+ android:valueType="floatType" />
+ </item>
+
+ <item android:state_focused="true">
+ <objectAnimator
+ android:duration="@android:integer/config_shortAnimTime"
+ android:propertyName="alpha"
+ android:valueTo="0.5"
+ android:valueType="floatType" />
+ </item>
+ <item>
+ <objectAnimator
+ android:duration="@android:integer/config_shortAnimTime"
+ android:propertyName="alpha"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </item>
+
+</selector> \ No newline at end of file
diff --git a/res/drawable-hdpi/page_hover_left.9.png b/res/drawable-hdpi/page_hover_left.9.png
deleted file mode 100644
index 3f11d0b33..000000000
--- a/res/drawable-hdpi/page_hover_left.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_left_active.9.png b/res/drawable-hdpi/page_hover_left_active.9.png
deleted file mode 100644
index abe4c31a9..000000000
--- a/res/drawable-hdpi/page_hover_left_active.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_right.9.png b/res/drawable-hdpi/page_hover_right.9.png
deleted file mode 100644
index 3bcf19195..000000000
--- a/res/drawable-hdpi/page_hover_right.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_right_active.9.png b/res/drawable-hdpi/page_hover_right_active.9.png
deleted file mode 100644
index 101e4dc0f..000000000
--- a/res/drawable-hdpi/page_hover_right_active.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_left.9.png b/res/drawable-mdpi/page_hover_left.9.png
deleted file mode 100644
index 2b6094cf6..000000000
--- a/res/drawable-mdpi/page_hover_left.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_left_active.9.png b/res/drawable-mdpi/page_hover_left_active.9.png
deleted file mode 100644
index 9eb00a282..000000000
--- a/res/drawable-mdpi/page_hover_left_active.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_right.9.png b/res/drawable-mdpi/page_hover_right.9.png
deleted file mode 100644
index c2e598351..000000000
--- a/res/drawable-mdpi/page_hover_right.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_right_active.9.png b/res/drawable-mdpi/page_hover_right_active.9.png
deleted file mode 100644
index d2771a1a5..000000000
--- a/res/drawable-mdpi/page_hover_right_active.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_left.9.png b/res/drawable-xhdpi/page_hover_left.9.png
deleted file mode 100644
index dbcc0abd2..000000000
--- a/res/drawable-xhdpi/page_hover_left.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_left_active.9.png b/res/drawable-xhdpi/page_hover_left_active.9.png
deleted file mode 100644
index 3233efe28..000000000
--- a/res/drawable-xhdpi/page_hover_left_active.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_right.9.png b/res/drawable-xhdpi/page_hover_right.9.png
deleted file mode 100644
index d82f809bf..000000000
--- a/res/drawable-xhdpi/page_hover_right.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_right_active.9.png b/res/drawable-xhdpi/page_hover_right_active.9.png
deleted file mode 100644
index 819ea195c..000000000
--- a/res/drawable-xhdpi/page_hover_right_active.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_left.9.png b/res/drawable-xxhdpi/page_hover_left.9.png
deleted file mode 100644
index c81f86c02..000000000
--- a/res/drawable-xxhdpi/page_hover_left.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_left_active.9.png b/res/drawable-xxhdpi/page_hover_left_active.9.png
deleted file mode 100644
index 858a3b2ab..000000000
--- a/res/drawable-xxhdpi/page_hover_left_active.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_right.9.png b/res/drawable-xxhdpi/page_hover_right.9.png
deleted file mode 100644
index c5297703e..000000000
--- a/res/drawable-xxhdpi/page_hover_right.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_right_active.9.png b/res/drawable-xxhdpi/page_hover_right_active.9.png
deleted file mode 100644
index 99005536b..000000000
--- a/res/drawable-xxhdpi/page_hover_right_active.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/ic_setting_pressed.xml b/res/drawable/ic_setting_pressed.xml
new file mode 100644
index 000000000..689f833ce
--- /dev/null
+++ b/res/drawable/ic_setting_pressed.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015, 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.
+-->
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:alpha="0.5"
+ android:src="@drawable/ic_setting" />
diff --git a/res/drawable/ic_wallpaper_pressed.xml b/res/drawable/ic_wallpaper_pressed.xml
new file mode 100644
index 000000000..d241c7d76
--- /dev/null
+++ b/res/drawable/ic_wallpaper_pressed.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015, 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.
+-->
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:alpha="0.5"
+ android:src="@drawable/ic_wallpaper" />
diff --git a/res/drawable/ic_widget_pressed.xml b/res/drawable/ic_widget_pressed.xml
new file mode 100644
index 000000000..44ac5b6e4
--- /dev/null
+++ b/res/drawable/ic_widget_pressed.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015, 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.
+-->
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:alpha="0.5"
+ android:src="@drawable/ic_widget" />
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 6500ebcd2..d166df7ac 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -23,7 +23,7 @@
android:layout_height="match_parent"
android:fitsSystemWindows="true">
- <com.android.launcher3.DragLayer
+ <com.android.launcher3.dragndrop.DragLayer
android:id="@+id/drag_layer"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -39,8 +39,7 @@
android:id="@+id/workspace"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_gravity="center"
- launcher:defaultScreen="@integer/config_workspaceDefaultScreen" />
+ android:layout_gravity="center" />
<!-- DO NOT CHANGE THE ID -->
<include layout="@layout/hotseat"
@@ -50,6 +49,10 @@
android:layout_gravity="right" />
<include
+ android:id="@+id/app_info_drop_target_bar"
+ layout="@layout/app_info_drop_target_bar" />
+
+ <include
android:id="@+id/search_drop_target_bar"
layout="@layout/search_drop_target_bar" />
@@ -68,7 +71,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
- </com.android.launcher3.DragLayer>
+ </com.android.launcher3.dragndrop.DragLayer>
<ViewStub
android:id="@+id/launcher_overlay_stub"
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index d0772ee70..b618b534c 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -24,7 +24,7 @@
android:layout_height="match_parent"
android:fitsSystemWindows="true">
- <com.android.launcher3.DragLayer
+ <com.android.launcher3.dragndrop.DragLayer
android:id="@+id/drag_layer"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -40,7 +40,6 @@
android:id="@+id/workspace"
android:layout_width="match_parent"
android:layout_height="match_parent"
- launcher:defaultScreen="@integer/config_workspaceDefaultScreen"
launcher:pageIndicator="@+id/page_indicator">
</com.android.launcher3.Workspace>
@@ -64,6 +63,10 @@
android:layout_gravity="center_horizontal" />
<include
+ android:id="@+id/app_info_drop_target_bar"
+ layout="@layout/app_info_drop_target_bar" />
+
+ <include
android:id="@+id/search_drop_target_bar"
layout="@layout/search_drop_target_bar" />
@@ -78,7 +81,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
- </com.android.launcher3.DragLayer>
+ </com.android.launcher3.dragndrop.DragLayer>
<ViewStub
android:id="@+id/launcher_overlay_stub"
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 802922ec1..f976e3042 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -23,7 +23,7 @@
android:layout_height="match_parent"
android:fitsSystemWindows="true">
- <com.android.launcher3.DragLayer
+ <com.android.launcher3.dragndrop.DragLayer
android:id="@+id/drag_layer"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -39,7 +39,6 @@
android:id="@+id/workspace"
android:layout_width="match_parent"
android:layout_height="match_parent"
- launcher:defaultScreen="@integer/config_workspaceDefaultScreen"
launcher:pageIndicator="@id/page_indicator">
</com.android.launcher3.Workspace>
@@ -50,6 +49,10 @@
android:layout_height="match_parent" />
<include
+ android:id="@+id/app_info_drop_target_bar"
+ layout="@layout/app_info_drop_target_bar" />
+
+ <include
android:id="@+id/search_drop_target_bar"
layout="@layout/search_drop_target_bar" />
@@ -76,7 +79,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
- </com.android.launcher3.DragLayer>
+ </com.android.launcher3.dragndrop.DragLayer>
<ViewStub
android:id="@+id/launcher_overlay_stub"
diff --git a/res/layout-v21/overview_panel.xml b/res/layout-v21/overview_panel.xml
new file mode 100644
index 000000000..fb6b512fc
--- /dev/null
+++ b/res/layout-v21/overview_panel.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 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="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|bottom"
+ android:gravity="top"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/wallpaper_button"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawablePadding="4dp"
+ android:drawableTop="@drawable/ic_wallpaper"
+ android:fontFamily="sans-serif-condensed"
+ android:gravity="center_horizontal"
+ android:stateListAnimator="@animator/overview_button_anim"
+ android:text="@string/wallpaper_button_text"
+ android:textAllCaps="true"
+ android:textColor="@android:color/white"
+ android:textSize="12sp" />
+
+ <TextView
+ android:id="@+id/widget_button"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawablePadding="4dp"
+ android:drawableTop="@drawable/ic_widget"
+ android:fontFamily="sans-serif-condensed"
+ android:gravity="center_horizontal"
+ android:stateListAnimator="@animator/overview_button_anim"
+ android:text="@string/widget_button_text"
+ android:textAllCaps="true"
+ android:textColor="@android:color/white"
+ android:textSize="12sp" />
+
+ <TextView
+ android:id="@+id/settings_button"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawablePadding="4dp"
+ android:drawableTop="@drawable/ic_setting"
+ android:fontFamily="sans-serif-condensed"
+ android:gravity="center_horizontal"
+ android:stateListAnimator="@animator/overview_button_anim"
+ android:text="@string/settings_button_text"
+ android:textAllCaps="true"
+ android:textColor="@android:color/white"
+ android:textSize="12sp" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/layout/app_info_drop_target_bar.xml b/res/layout/app_info_drop_target_bar.xml
new file mode 100644
index 000000000..5f19d4331
--- /dev/null
+++ b/res/layout/app_info_drop_target_bar.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.AppInfoDropTargetBar xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="false" >
+
+ <!-- Drag specific targets container -->
+ <LinearLayout
+ android:id="@+id/drag_target_bar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center|bottom" >
+
+ <FrameLayout
+ style="@style/DropTargetButtonContainer"
+ android:layout_weight="1" >
+
+ <!-- Info target -->
+
+ <com.android.launcher3.InfoDropTarget
+ android:id="@+id/info_target_text"
+ style="@style/DropTargetButton"
+ android:text="@string/app_info_drop_target_label" />
+ </FrameLayout>
+ </LinearLayout>
+
+</com.android.launcher3.AppInfoDropTargetBar> \ No newline at end of file
diff --git a/res/layout/search_drop_target_bar.xml b/res/layout/search_drop_target_bar.xml
index 4737ee1bc..50f96791a 100644
--- a/res/layout/search_drop_target_bar.xml
+++ b/res/layout/search_drop_target_bar.xml
@@ -36,19 +36,7 @@
<com.android.launcher3.DeleteDropTarget
android:id="@+id/delete_target_text"
style="@style/DropTargetButton"
- android:text="@string/delete_target_label" />
- </FrameLayout>
-
- <FrameLayout
- style="@style/DropTargetButtonContainer"
- android:layout_weight="1" >
-
- <!-- Info target -->
-
- <com.android.launcher3.InfoDropTarget
- android:id="@+id/info_target_text"
- style="@style/DropTargetButton"
- android:text="@string/info_target_label" />
+ android:text="@string/remove_drop_target_label" />
</FrameLayout>
<FrameLayout
@@ -60,7 +48,7 @@
<com.android.launcher3.UninstallDropTarget
android:id="@+id/uninstall_target_text"
style="@style/DropTargetButton"
- android:text="@string/delete_target_uninstall_label" />
+ android:text="@string/uninstall_drop_target_label" />
</FrameLayout>
</LinearLayout>
diff --git a/res/layout/workspace_screen.xml b/res/layout/workspace_screen.xml
index 83b319b72..faf688503 100644
--- a/res/layout/workspace_screen.xml
+++ b/res/layout/workspace_screen.xml
@@ -17,9 +17,6 @@
<com.android.launcher3.CellLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
-
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:hapticFeedbackEnabled="false"
-
- launcher:maxGap="@dimen/workspace_max_gap" />
+ android:hapticFeedbackEnabled="false" />
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index e426e50b5..2bedb604c 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Geen plek meer in die Gunstelinge-laai nie"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Programme"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Tuis"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Verwyder"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Deïnstalleer"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Programinligting"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Verwyder"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deïnstalleer"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Programinligting"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installeer kortpaaie"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Laat \'n program toe om kortpaaie by te voeg sonder gebruikerinmenging."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lees Tuis-instellings en -kortpaaie"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 2d1fb3dc3..290099ad3 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"በተወዳጆች መሣቢያ ውስጥ ተጨማሪ ቦታ የለም"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"መተግበሪያዎች"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"መነሻ"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"አስወግድ"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"አራግፍ"</string>
- <string name="info_target_label" msgid="8053346143994679532">"የመተግበሪያ መረጃ"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"አስወግድ"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"አራግፍ"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"የመተግበሪያ መረጃ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"አቋራጮችን ይጭናል"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"መተግበሪያው ያለተጠቃሚ ጣልቃ ገብነት አቋራጭ እንዲያክል ያስችለዋል።"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"የመነሻ ቅንብሮች እና አቋራጮችን ያነባል"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 67400d4fc..a58ed71e1 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"لا يوجد المزيد من الحقول في علبة المفضلة"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"التطبيقات"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"الرئيسية"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"إزالة"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"إلغاء التثبيت"</string>
- <string name="info_target_label" msgid="8053346143994679532">"معلومات عن التطبيق"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"إزالة"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"إلغاء التثبيت"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"معلومات عن التطبيق"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"تثبيت اختصارات"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"للسماح لتطبيق ما بإضافة اختصارات بدون تدخل المستخدم."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"قراءة إعدادات واختصارات الشاشة الرئيسية"</string>
diff --git a/res/values-az-rAZ/strings.xml b/res/values-az-rAZ/strings.xml
index b9ea83f26..29f2cf04b 100644
--- a/res/values-az-rAZ/strings.xml
+++ b/res/values-az-rAZ/strings.xml
@@ -19,15 +19,15 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="application_name" msgid="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"İş"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Tətbiq quraşdırılmayıb."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Tətbiq əlçatmazdır"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Güvənli rejimdə icazə verilməyən tətbiq endirildi"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Vidcetlər Güvənli rejimdə deaktiv edilib"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Suvenirləri göstər"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Vidceti götürmək üçün toxunub saxlayın."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Vidceti götürmək üçün &amp; iki dəfə toxunub saxlayın və ya fərdi fəaliyyətləri istifadə edin."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Tətbiq Axtarın"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Tətbiqlər endirilir..."</string>
@@ -37,21 +37,23 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritlər-də yer yoxdur"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Tətbiqlər"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Əsas səhifə"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Kənarlaşdır"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Sistemdən sil"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Tətbiq məlumatı"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Silin"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Sistemdən sil"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Tətbiq məlumatı"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"qısayolları quraşdır"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Tətbiqə istifadəçi müdaxiləsi olmadan qısayolları əlavə etməyə icazə verir."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Əsas Səhifə ayarlarını və qısayolları oxuyun"</string>
<string name="permdesc_read_settings" msgid="5833423719057558387">"Tətbiqə Əsas Səhifədə parametrləri və qısayolları oxumağa icazə verir."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"Əsas Səhifə ayarlarını və qısayolları yazın"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Tətbiqə Əsas Səhifədə ayarları və qısayolları dəyişməyə icazə verir."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqinə telefon zəngləri etmək üçün icazə verilmir"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Vidcet yükləmə problemi"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Quraşdırma"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu sistem tətbiqi olduğu üçün sistemdən silinə bilməz."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Adsız Qovluq"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Səhifə %1$d of %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Əsas Səhifə ekranı %1$d of %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Yeni əsas ekran səhifəsi"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Xoş gəlmisiniz"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Tətbiq ikonalarınızı kopyalayın"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Köhnə Əsas ekranınızda olan ikonalar və qovluqlar import edilsin?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Axtarış"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Bu tətbiq quraşdırılmayıb"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Bu ikona üçün tətbiq quraşdırılmayıb. Onu silə bilərsiniz, və ya tətbiqi taparaq manual yol ilə quraşdıra bilərsiniz."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> endirilir, <xliff:g id="PROGRESS">%2$s</xliff:g> tamamlandı"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> yüklənmək üçün gözləyir"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Əsas ekrana əlavə edin"</string>
<string name="action_move_here" msgid="2170188780612570250">"Elementi bura köçürün"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Element əsas ekrana əlavə edildi"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 0ba752561..6f1a0bc27 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Няма повече място в областта с любимите"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Приложения"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Начало"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Премахване"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Деинсталиране"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Информация за приложението"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Премахване"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Деинсталиране"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Данни за прилож."</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталиране на преки пътища"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Разрешава на приложението да добавя преки пътища без намеса на потребителя."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"четене на настройките и преките пътища в Начало"</string>
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
index 8f0c92286..a8f6496e9 100644
--- a/res/values-bn-rBD/strings.xml
+++ b/res/values-bn-rBD/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"পছন্দসই ট্রে-তে আর কোনো জায়গা নেই"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"অ্যাপ্লিকেশানগুলি"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"হোম"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"সরান"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"আনইনস্টল করুন"</string>
- <string name="info_target_label" msgid="8053346143994679532">"অ্যাপ্লিকেশানের তথ্য"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"সরান"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"আনইনস্টল করুন"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"অ্যাপ্লিকেশানের তথ্য"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"শর্টকাটগুলি ইনস্টল করে"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"একটি অ্যাপ্লিকেশানকে ব্যবহারকারীর হস্তক্ষেপ ছাড়াই শর্টকাটগুলি যোগ করার অনুমতি দেয়৷"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"হোম সেটিংস এবং শর্টকাটগুলি পড়ে"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 2d3f9ed2b..503716f23 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"No hi ha més espai a la safata Preferits."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplicacions"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Inici"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Suprimeix"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstal·la"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Informació de l\'aplicació"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Suprimeix"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstal·la"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Dades de l\'aplicació"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instal·la dreceres"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permet que una aplicació afegeixi dreceres sense la intervenció de l\'usuari."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"llegeix la configuració i les dreceres de la pantalla d\'inici"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index ce3757a73..4a8daaf6d 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Na panelu Oblíbené položky již není místo."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikace"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Plocha"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Odstranit"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Odinstalovat"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Informace o aplikaci"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Odstranit"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinstalovat"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Informace o aplikaci"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalace zástupce"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Umožňuje aplikaci přidat zástupce bez zásahu uživatele."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"čtení nastavení a odkazů plochy"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index ffa168bdb..a90476547 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Der er ikke mere plads i bakken Foretrukne"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Startskærm"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Fjern"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Afinstaller"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Oplysninger om appen"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Fjern"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Afinstaller"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Oplysninger om appen"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installere genveje"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Tillader, at en app tilføjer genveje uden brugerens indgriben."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"læs indstillinger og genveje for startskærmen"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 118e34aa5..e201d218d 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ablage \"Favoriten\" ist voll."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Startseite"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Entfernen"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Deinstallieren"</string>
- <string name="info_target_label" msgid="8053346143994679532">"App-Info"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Entfernen"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstallieren"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"App-Details"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Verknüpfungen installieren"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ermöglicht einer App das Hinzufügen von Verknüpfungen ohne Eingreifen des Nutzers"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Einstellungen und Verknüpfungen auf dem Startbildschirm lesen"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 38ee326d4..ff6ff1d62 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Δεν υπάρχει επιπλέον χώρος στην περιοχή Αγαπημένα"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Εφαρμογές"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Αρχική οθόνη"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Κατάργηση"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Κατάργηση εγκατάστασης"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Πληροφορίες εφαρμογής"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Κατάργηση"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Απεγκατάσταση"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Πληροφορίες εφαρμογής"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"εγκατάσταση συντομεύσεων"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Επιτρέπει σε μια εφαρμογή την προσθήκη συντομεύσεων χωρίς την παρέμβαση του χρήστη."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ανάγνωση ρυθμίσεων και συντομεύσεων αρχικής οθόνης"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index ee45af040..f4a8639e4 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Home"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Remove"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Uninstall"</string>
- <string name="info_target_label" msgid="8053346143994679532">"App info"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Remove"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"read Home settings and shortcuts"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index ee45af040..f4a8639e4 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Home"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Remove"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Uninstall"</string>
- <string name="info_target_label" msgid="8053346143994679532">"App info"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Remove"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"read Home settings and shortcuts"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index ee45af040..f4a8639e4 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Home"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Remove"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Uninstall"</string>
- <string name="info_target_label" msgid="8053346143994679532">"App info"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Remove"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"read Home settings and shortcuts"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 6f6c9def2..5246945f0 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"La bandeja de favoritos está llena."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplicaciones"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Pantalla principal"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Eliminar"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstalar"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Información de la aplicación"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Quitar"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Información de app"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar accesos directos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que una aplicación agregue accesos directos sin que el usuario intervenga."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"leer configuración y accesos directos de la pantalla principal"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 11ae8cb9b..c160aa3de 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"La bandeja de favoritos está completa"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplicaciones"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Inicio"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Eliminar"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstalar"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Información de la aplicación"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Quitar"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Datos de aplicación"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar accesos directos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que una aplicación añada accesos directos sin intervención del usuario."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"leer información de accesos directos y de ajustes de la pantalla de inicio"</string>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index 6f45387f9..0065b4ea4 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Salves Lemmikud pole rohkem ruumi"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Rakendused"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Avaekraan"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Eemalda"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstalli"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Rakenduse teave"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Eemalda"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalli"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Rakenduse teave"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installi otseteed"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Võimaldab rakendusel lisada otseteid kasutaja sekkumiseta."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"loe avaekraani seadeid ja otseteid"</string>
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml
index 7a465b3ab..84653e3c5 100644
--- a/res/values-eu-rES/strings.xml
+++ b/res/values-eu-rES/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ez dago toki gehiago Gogokoak erretiluan"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikazioak"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Hasiera"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Kendu"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstalatu"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Aplikazioaren informazioa"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Kendu"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalatu"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Aplikazioaren datuak"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Instalatu lasterbideak"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Erabiltzaileak ezer egin gabe lasterbideak gehitzea baimentzen die aplikazioei."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Irakurri hasierako ezarpenak eta lasterbideak"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 9f0bb44f4..91fc0d367 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"فضای بیشتری در سینی موارد دلخواه وجود ندارد"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"برنامه‌ها"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"صفحه اصلی"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"حذف"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"حذف نصب"</string>
- <string name="info_target_label" msgid="8053346143994679532">"اطلاعات برنامه"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"برداشتن"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"حذف نصب"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"اطلاعات برنامه"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"نصب میان‌برها"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"به برنامه اجازه می‌دهد میان‌برها را بدون دخالت کاربر اضافه کند."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"خواندن تنظیمات و میان‌برهای صفحه اصلی"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 2cbf12774..b7578c6d4 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Suosikit-valikossa ei ole enää tilaa"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Sovellukset"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Aloitusruutu"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Poista kuvake"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Poista asennus"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Sovelluksen tiedot"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Poista"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Poista asennus"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Sovelluksen tiedot"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"asenna pikakuvakkeita"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Antaa sovelluksen lisätä pikakuvakkeita itsenäisesti ilman käyttäjän valintaa."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lue aloitusruudun asetuksia ja pikakuvakkeita"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 2b05477ed..ca6eccc1a 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Il n\'y a plus d\'espace dans la zone des favoris"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Applications"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Accueil"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Supprimer"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Désinstaller"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Détails de l\'application"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Supprimer"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Désinstaller"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Détails de l\'appli"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installer des raccourcis"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permet à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lire les paramètres et les raccourcis de la page d\'accueil"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 2d97d230d..58a0fb7e3 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Plus d\'espace disponible dans la zone de favoris."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Applications"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Accueil"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Supprimer"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Désinstaller"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Informations sur l\'application"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Supprimer"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Désinstaller"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Infos sur l\'appli"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installer des raccourcis"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permettre à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lire les paramètres et les raccourcis de l\'écran d\'accueil"</string>
diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml
index 0a1cbe1b9..9e5a60594 100644
--- a/res/values-gl-rES/strings.xml
+++ b/res/values-gl-rES/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Non hai máis espazo na bandexa de favoritos"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplicacións"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Inicio"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Eliminar"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstalar"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Información da aplicación"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Eliminar"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Info. da aplicación"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atallos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a unha aplicación engadir atallos sen intervención do usuario."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ler a configuración e os atallos da pantalla de inicio"</string>
diff --git a/res/values-gu-rIN/strings.xml b/res/values-gu-rIN/strings.xml
index db056a0e8..129d83e13 100644
--- a/res/values-gu-rIN/strings.xml
+++ b/res/values-gu-rIN/strings.xml
@@ -19,15 +19,15 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="application_name" msgid="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"કાર્યાલય"</string>
<string name="activity_not_found" msgid="8071924732094499514">"એપ્લિકેશન ઇન્સ્ટોલ થઈ નથી."</string>
<string name="activity_not_available" msgid="7456344436509528827">"એપ્લિકેશન ઉપલબ્ધ નથી"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"સુરક્ષિત મોડમાં ડાઉનલોડ કરેલ એપ્લિકેશન અક્ષમ કરી"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"સુરક્ષિત મોડમાં વિજેટ્સ અક્ષમ કર્યા"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Mem બતાવો"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"વિજેટ ચૂંટવા માટે ટચ કરો અને પકડી રાખો."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"વિજેટ ચૂંટવા અથવા કસ્ટમ ક્રિયાઓનો ઉપયોગ કરવા માટે બે વાર ટેપ કરો અને પકડી રાખો."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"શોધ એપ્લિકેશનો"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"એપ્લિકેશનો લોડ કરી રહ્યું છે…"</string>
@@ -37,21 +37,23 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"મનપસંદ ટ્રે પર વધુ જગ્યા નથી"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"એપ્લિકેશનો"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"હોમ"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"દૂર કરો"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"અનઇન્સ્ટોલ કરો"</string>
- <string name="info_target_label" msgid="8053346143994679532">"એપ્લિકેશન માહિતી"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"દૂર કરો"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"અનઇન્સ્ટોલ કરો"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"ઍપ્લિકેશન માહિતી"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"શોર્ટકટ્સ ઇન્સ્ટોલ કરો"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"એપ્લિકેશનને વપરાશકર્તા હસ્તક્ષેપ વગર શોર્ટકટ્સ ઉમેરવાની મંજૂરી આપે છે."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"હોમ સેટિંગ્સ અને શોર્ટકટ્સ વાંચો"</string>
<string name="permdesc_read_settings" msgid="5833423719057558387">"એપ્લિકેશનને હોમમાં સેટિંગ્સ અને શોર્ટકટ્સ વાંચવાની મંજૂરી આપે છે."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"હોમ સેટિંગ્સ અને શોર્ટકટ્સ લખો"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"એપ્લિકેશનને હોમમાં સેટિંગ્સ અને શોર્ટકટ્સ બદલવાની મંજૂરી આપે છે."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> ને ફોન કૉલ્સ કરવાની મંજૂરી નથી"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"વિજેટ લોડ કરવામાં સમસ્યા"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"સેટઅપ"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"આ એક સિસ્ટમ એપ્લિકેશન છે અને અનઇન્સ્ટોલ કરી શકાતી નથી."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"અનામી ફોલ્ડર"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"%2$d માંથી %1$d પૃષ્ઠ"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d માંથી %1$d હોમ સ્ક્રીન"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"નવું હોમ સ્ક્રીન પૃષ્ઠ"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"સ્વાગત છે"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"તમારા એપ્લિકેશન આયકન્સને કૉપિ કરો"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"તમારી જૂની હોમ સ્ક્રીન્સથી આયકન્સ અને ફોલ્ડર્સને આયાત કરીએ?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"શોધો"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"આ એપ્લિકેશન ઇન્સ્ટોલ થયેલ નથી"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"આ આયકન માટેની એપ્લિકેશન ઇન્સ્ટોલ થયેલ નથી. તમે તેને દૂર કરી શકો છો અથવા એપ્લિકેશન માટે શોધ કરી અને તેને મેન્યુઅલી ઇન્સ્ટોલ કરી શકો છો."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ડાઉનલોડ કરી રહ્યાં છે, <xliff:g id="PROGRESS">%2$s</xliff:g> પૂર્ણ"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>, ઇન્સ્ટૉલ થવાની રાહ જોઈ રહ્યું છે"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"હોમ સ્ક્રીન પર ઉમેરો"</string>
<string name="action_move_here" msgid="2170188780612570250">"આઇટમ અહીં ખસેડો"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"હોમ સ્ક્રીનમાં આઇટમ ઉમેરી"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 49531af74..b209cee9e 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"पसंदीदा ट्रे में और स्थान नहीं है"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"ऐप्लिकेशन"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"होम"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"निकालें"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"अनइंस्टॉल करें"</string>
- <string name="info_target_label" msgid="8053346143994679532">"ऐप्लिकेशन की जानकारी"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"निकालें"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"अनइंस्टॉल करें"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"ऐप की जानकारी"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट इंस्‍टॉल करें"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ऐप्लिकेशन को उपयोगकर्ता के हस्‍तक्षेप के बिना शॉर्टकट जोड़ने देती है."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"होम सेटिंग और शॉर्टकट पढ़ें"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 0ec3789a0..ec9f892f6 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Nema više prostora na traci Favoriti"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacije"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Početna"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Ukloni"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Deinstaliraj"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Informacije o aplikaciji"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Ukloni"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstaliraj"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Info o aplikaciji"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliranje prečaca"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Aplikaciji omogućuje dodavanje prečaca bez intervencije korisnika."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"čitanje postavki početnog zaslona i prečaca"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 8f93d97cf..cfff315ac 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Nincs több hely a Kedvencek tálcán"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Alkalmazások"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Főoldal"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Eltávolítás"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Törlés"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Alkalmazásinformáció"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Törlés"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Eltávolítás"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Alkalmazásinformáció"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"parancsikonok telepítése"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Lehetővé teszi egy alkalmazás számára, hogy felhasználói beavatkozás nélkül adjon hozzá parancsikonokat."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Főoldal beállításainak és parancsikonjainak beolvasása"</string>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index 3f06f8c87..8beff3a1a 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ընտրյալների ցուցակում այլևս ազատ տեղ չկա"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Ծրագրեր"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Հիմնական"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Հեռացնել"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Ապատեղադրել"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Ծրագրի տեղեկություններ"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Հեռացնել"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Հեռացնել"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Հավելվածի տվյալներ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"տեղադրել դյուրանցումներ"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ծրագրին թույլ է տալիս ավելացնել դյուրանցումներ՝ առանց օգտագործողի միջամտության:"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"կարդալ հիմնաէջի կարգավորումներն ու դյուրանցումները"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 2537255ae..3de54afc7 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Tidak ada ruang tersisa di baki Favorit"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikasi"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Layar Utama"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Hapus"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Copot pemasangan"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Info aplikasi"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Hapus"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Copot pemasangan"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Info aplikasi"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"memasang pintasan"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Mengizinkan aplikasi menambahkan pintasan tanpa campur tangan pengguna."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"membaca setelan dan pintasan layar Utama"</string>
diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml
index 1bf3dbfad..eb900be91 100644
--- a/res/values-is-rIS/strings.xml
+++ b/res/values-is-rIS/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ekki meira pláss í bakka fyrir uppáhald"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Forrit"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Heim"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Fjarlægja"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Eyða"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Upplýsingar um forrit"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Fjarlægja"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Fjarlægja"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Forritsupplýsingar"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"setja upp flýtileiðir"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Leyfir forriti að bæta við flýtileiðum án íhlutunar notanda."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lesa stillingar og flýtileiðir heimaskjás"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index e1631a4d4..7a1585d7c 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Spazio esaurito nella barra dei Preferiti"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"App"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Home page"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Rimuovi"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Disinstalla"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Informazioni app"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Rimuovi"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Disinstalla"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Informazioni app"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"aggiunta di scorciatoie"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Consente a un\'app di aggiungere scorciatoie automaticamente."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lettura di impostazioni e scorciatoie in Home"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index db88fe21e..5b978af12 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"אין עוד מקום במגש המועדפים"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"אפליקציות"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"דף הבית"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"הסר"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"הסר התקנה"</string>
- <string name="info_target_label" msgid="8053346143994679532">"פרטי אפליקציה"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"הסר"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"הסר התקנה"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"פרטי אפליקציה"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"התקן קיצורי דרך"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"מאפשר לאפליקציה להוסיף קיצורי דרך ללא התערבות המשתמש."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"קרא הגדרות וקיצורי דרך של דף הבית"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index b4873a0e3..662e2918e 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"お気に入りトレイに空きスペースがありません"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"アプリ"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"ホーム"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"削除"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"アンインストール"</string>
- <string name="info_target_label" msgid="8053346143994679532">"アプリ情報"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"削除"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"アンインストール"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"アプリ情報"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ショートカットのインストール"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ユーザー操作なしでショートカットを追加することをアプリに許可します。"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ホームの設定とショートカットの読み取り"</string>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index 44c17a2b9..bf5724f6b 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"რჩეულების თაროზე ადგილი არ არის"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"აპები"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"მთავარი"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"წაშლა"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"დეინსტალაცია"</string>
- <string name="info_target_label" msgid="8053346143994679532">"აპის შესახებ"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"ამოშლა"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"დეინსტალაცია"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"აპის შესახებ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"მალსახმობების დაყენება"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"აპისთვის მალსახმობების დამოუკიდებლად დამატების უფლების მიცემა."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"მთავარი ეკრანის პარამეტრებისა და მალსახმობების წაკითხვა"</string>
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml
index e30d9fe09..61a423a39 100644
--- a/res/values-kk-rKZ/strings.xml
+++ b/res/values-kk-rKZ/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Қалаулылар науасында орын қалмады"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Қолданбалар"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Негізгі"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Алып тастау"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Алмау"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Қолданба ақпары"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Жою"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Жою"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Қолданба ақпараты"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"төте пернелерді орнату"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Қолданбаға пайдаланушының қатысуынсыз төте пернелерді қосу мүмкіндігін береді."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Негізгі экрандағы параметрлер мен төте пернелерді оқу"</string>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index abe8a1c59..1a4469d1b 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"គ្មាន​បន្ទប់​​ក្នុង​ថាស​និយម​ប្រើ"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"កម្មវិធី"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"ដើម"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"លុប​ចេញ"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"លុប"</string>
- <string name="info_target_label" msgid="8053346143994679532">"ព័ត៌មាន​កម្មវិធី"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"យកចេញ"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"លុបការដំឡើង"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"ព័ត៌មាន​កម្មវិធី"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ដំឡើង​ផ្លូវកាត់"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"អនុញ្ញាត​ឲ្យ​កម្មវិធី​បន្ថែម​ផ្លូវកាត់​ ដោយ​មិន​ចាំបាច់​​អំពើ​ពី​អ្នក​ប្រើ។"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"អាន​ការ​កំណត់​ និង​ផ្លូវកាត់​​អេក្រង់​ដើម"</string>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
index 9086d9bf6..e712ba884 100644
--- a/res/values-kn-rIN/strings.xml
+++ b/res/values-kn-rIN/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ಮೆಚ್ಚಿನವುಗಳ ಟ್ರೇನಲ್ಲಿ ಹೆಚ್ಚಿನ ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳು"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"ಮುಖಪುಟ"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"ತೆಗೆದುಹಾಕು"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"ಅಸ್ಥಾಪಿಸು"</string>
- <string name="info_target_label" msgid="8053346143994679532">"ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"ತೆಗೆದುಹಾಕಿ"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ಅಸ್ಥಾಪಿಸು"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸ್ಥಾಪಿಸಿ"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ಬಳಕೆದಾರರ ಹಸ್ತಕ್ಷೇಪವಿಲ್ಲದೆ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸೇರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ಮುಖಪುಟದ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಓದಿ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index cebf762b1..b7c1a50db 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"즐겨찾기 트레이에 더 이상 공간이 없습니다."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"앱"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"홈"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"삭제"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"제거"</string>
- <string name="info_target_label" msgid="8053346143994679532">"앱 정보"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"삭제"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"제거"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"앱 정보"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"바로가기 설치"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"앱이 사용자의 작업 없이 바로가기를 추가할 수 있도록 합니다."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"홈 설정 및 바로가기 읽기"</string>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
index 89f97a4c3..53c3f8876 100644
--- a/res/values-ky-rKG/strings.xml
+++ b/res/values-ky-rKG/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Тандамалдар тайпасында орун калган жок"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Колдонмолор"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Үйгө"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Алып салуу"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Чечип салуу"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Колдонмо тууралуу"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Алып салуу"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Чыгарып салуу"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Колдонмо тууралуу"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"тез чакырмаларды орнотуу"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Колдонмого колдонуучуга кайрылбастан тез чакырма кошууга уруксат берет."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Үйдүн тууралоолорун жана тез чакырмаларын окуу"</string>
diff --git a/res/values-land/styles.xml b/res/values-land/styles.xml
index c5a76d595..40926799f 100644
--- a/res/values-land/styles.xml
+++ b/res/values-land/styles.xml
@@ -19,9 +19,6 @@
<resources>
- <!-- Search Bar -->
- <style name="SearchButton"></style>
-
<style name="DropTargetButtonContainer">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index 04fdd0294..6d19f312d 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ບໍ່ມີບ່ອນຫວ່າງໃນຖາດສຳລັບເກັບສິ່ງທີ່ໃຊ້ເປັນປະຈຳ"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"ແອັບຯ"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"ໜ້າຫຼັກ"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"ລຶບ"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"ຖອນ​ການ​ຕິດ​ຕັ້ງ"</string>
- <string name="info_target_label" msgid="8053346143994679532">"ຂໍ້ມູນແອັບຯ"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"ເອົາ​ອອກ"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ຖອນ​ການ​ຕິດ​ຕັ້ງ"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"ຂໍ້ມູນແອັບ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ຕິດຕັ້ງທາງລັດ"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ອະນຸຍາດໃຫ້ແອັບຯ ເພີ່ມທາງລັດໂດຍບໍ່ຕ້ອງຮັບການຢືນຢັນຈາກຜູ່ໃຊ້."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ອ່ານການຕັ້ງຄ່າໜ້າຫຼັກ ແລະທາງລັດ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 34d1bbada..1336a4d06 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Mėgstamiausių dėkle nebėra vietos"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Programos"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Pagrindinis"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Ištrinti"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Pašalinti"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Programos informacija"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Ištrinti"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Pašalinti"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Programos inform."</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"įdiegti sparčiuosius klavišus"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Programai leidžiama pridėti sparčiuosius klavišus be naudotojo įsikišimo."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"skaityti pagrindinio puslapio nustatymus ir sparčiuosius klavišus"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index cc157c53a..4e2f98e0e 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Izlases joslā vairs nav vietas."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Lietotnes"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Sākums"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Noņemt"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Atinstalēt"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Lietotnes informācija"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Noņemt"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Atinstalēt"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Lietotnes informācija"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalēt saīsnes"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ļauj lietotnei pievienot saīsnes, nejautājot lietotājam."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lasīt sākuma ekrāna iestatījumus un saīsnes"</string>
diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml
index b6f89d1b7..834598af3 100644
--- a/res/values-mk-rMK/strings.xml
+++ b/res/values-mk-rMK/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Нема повеќе простор на лентата „Омилени“"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Апликации"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Почетна страница"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Отстрани"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Деинсталирај"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Информации за апликацијата"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Отстрани"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Деинсталирај"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Инф. за апликација"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталирај кратенки"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Овозможува апликацијата да додава кратенки без интервенција на корисникот."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"прочитај подесувања и кратенки на почетна страница"</string>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index 4a4795618..eb48c7c67 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"പ്രിയപ്പെട്ടവയുടെ ട്രേയിൽ ഒഴിവൊന്നുമില്ല"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"അപ്ലിക്കേഷനുകൾ"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"ഹോം"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"നീക്കംചെയ്യുക"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"അണ്‍ഇസ്റ്റാളുചെയ്യുക"</string>
- <string name="info_target_label" msgid="8053346143994679532">"ആപ്പ് വിവരം"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"നീക്കംചെയ്യുക"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"അൺഇൻസ്റ്റാളുചെയ്യുക"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"ആപ്പ് വിവരം"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"കുറുക്കുവഴികൾ ഇൻസ്റ്റാളുചെയ്യുക"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ഉപയോക്തൃ ഇടപെടൽ ഇല്ലാതെ കുറുക്കുവഴികൾ ചേർക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ഹോം ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും റീഡുചെയ്യുക"</string>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index c59a206fb..07eaf1505 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"\"Дуртай\" трей дээр өөр зай байхгүй байна"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Апп"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Нүүр"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Устгах"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Устгах"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Апп мэдээлэл"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Арилгах"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Устгах"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Апп-н мэдээлэл"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"товчлол суулгах"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Апп нь хэрэглэгчийн оролцоогүйгээр товчлолыг нэмэж чадна"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Нүүрний тохиргоо болон товчлолыг унших"</string>
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml
index 373a7cdef..c9fa78dec 100644
--- a/res/values-mr-rIN/strings.xml
+++ b/res/values-mr-rIN/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"आवडीच्या ट्रे मध्ये आणखी जागा नाही"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"अॅप्स"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"मुख्‍यपृष्‍ठ"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"काढा"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"विस्थापित करा"</string>
- <string name="info_target_label" msgid="8053346143994679532">"अॅप माहिती"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"काढा"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"विस्थापित करा"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"अॅप माहिती"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट स्‍थापित करा"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"वापरकर्ता हस्तक्षेपाशिवाय शॉर्टकट जोडण्यास अॅप ला अनुमती देते."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"मुख्यपृष्ठ सेटिंग्ज आणि शॉर्टकट वाचा"</string>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index 0ff456ca5..55de17288 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Tiada ruang dalam dulang Kegemaran lagi"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apl"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Laman Utama"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Alih keluar"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Nyahpasang"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Maklumat apl"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Alih keluar"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Nyahpasang"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Maklumat apl"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"pasang pintasan"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Membenarkan apl menambah pintasan tanpa campur tangan pengguna."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"baca tetapan dan pintasan Laman Utama"</string>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index ceef91c6c..c47f2e654 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"အနှစ်သက်ဆုံးများ ထားရာတွင် နေရာလွတ် မကျန်တော့ပါ"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"အပ်ပလီကေးရှင်းများ"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"ပင်မစာမျက်နှာ"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"ဖယ်ရှာခြင်း"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"ဖယ်ရှားခြင်း"</string>
- <string name="info_target_label" msgid="8053346143994679532">"အပ်ပလီကေးရှင်း အချက်အလက်များ"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"ဖယ်ရှားမည်"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ဖယ်ထုတ်မည်"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"အက်ပ် အချက်အလက်များ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"အတိုကောက်မှတ်သားမှုများအား ထည့်သွင်းခြင်း"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"အသုံးပြုသူ လုပ်ဆောင်မှုမရှိပဲ အပ်ပလီကေးရှင်းကို အတိုကောက်မှတ်သားမှုများ ပြုလုပ်ခွင့် ပေးခြင်း"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ဖတ်ခြင်း"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 981b21e5a..575d59141 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritter-skuffen er full"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apper"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Startside"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Fjern"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Avinstaller"</string>
- <string name="info_target_label" msgid="8053346143994679532">"App-info"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Fjern"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Avinstaller"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Info om appen"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installere snarveier"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Gir apper tillatelse til å legge til snarveier uten innblanding fra brukeren."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lese startsideinnstillinger og -snarveier"</string>
diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml
index b480826d9..f197979fd 100644
--- a/res/values-ne-rNP/strings.xml
+++ b/res/values-ne-rNP/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"मनपर्ने ट्रे अब कुनै ठाँउ छैन"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"अनुप्रयोगहरू"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"गृह"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"हटाउनुहोस्"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"स्थापना हटाउनुहोस्"</string>
- <string name="info_target_label" msgid="8053346143994679532">"अनुप्रयोग जानकारी"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"हटाउनुहोस्"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"विस्थापित गर्नुहोस्"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"अनुप्रयोग जानकारी"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"सर्टकट स्थापना गर्नेहोस्"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा अनुप्रयोगलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"गृह सेटिङहरू र सर्टकटहरू पढ्नुहोस्"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 45c5e4565..0d341444e 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Geen ruimte meer in het vak \'Favorieten\'"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Startpagina"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Verwijderen"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Deïnstalleren"</string>
- <string name="info_target_label" msgid="8053346143994679532">"App-info"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Verwijderen"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deïnstalleren"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"App-info"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Snelkoppelingen instellen"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Een app toestaan snelkoppelingen toe te voegen zonder tussenkomst van de gebruiker."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"instellingen en snelkoppelingen op de startpagina lezen"</string>
diff --git a/res/values-pa-rIN/strings.xml b/res/values-pa-rIN/strings.xml
index 30c44280e..5647afe5b 100644
--- a/res/values-pa-rIN/strings.xml
+++ b/res/values-pa-rIN/strings.xml
@@ -19,15 +19,15 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="application_name" msgid="5181331383435256801">"ਲਾਂਚਰ3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"ਦਫ਼ਤਰ"</string>
<string name="activity_not_found" msgid="8071924732094499514">"ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ।"</string>
<string name="activity_not_available" msgid="7456344436509528827">"ਐਪ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"ਡਾਊਨਲੋਡ ਕੀਤਾ ਐਪ ਸੁਰੱਖਿਅਤ ਮੋਡ ਵਿੱਚ ਅਸਮਰਥਿਤ"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"ਵਿਜੇਟਸ ਸੁਰੱਖਿਅਤ ਮੋਡ ਵਿੱਚ ਅਸਮਰਥਿਤ"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"ਮੈਮ ਦਿਖਾਓ"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਛੋਹਵੋT &amp; ਹੋਲਡ ਕਰੋ।"</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ਡਬਲ-ਟੈਪ &amp; ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਹੋਲਡ ਕਰੋ ਅਤੇ ਕਸਟਮ ਕਿਰਿਆਵਾਂ ਵਰਤੋ।"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ਐਪਸ ਖੋਜੋ"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"ਐਪਸ ਲੋਡ ਕਰ ਰਿਹਾ ਹੈ..."</string>
@@ -37,21 +37,23 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ਮਨਪਸੰਦ ਟ੍ਰੇ ਵਿੱਚ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ।"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"ਐਪਸ"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"ਹੋਮ"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"ਹਟਾਓ"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"ਅਣਇੰਸਟੌਲ ਕਰੋ"</string>
- <string name="info_target_label" msgid="8053346143994679532">"ਐਪ ਜਾਣਕਾਰੀ"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"ਹਟਾਓ"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ਸਥਾਪਨਾ ਰੱਦ ਕਰੋ"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"ਐਪ ਜਾਣਕਾਰੀ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ਸ਼ੌਰਟਕਟ ਇੰਸਟੌਲ ਕਰੋ"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ਇੱਕ ਐਪ ਨੂੰ ਉਪਭੋਗਤਾ ਦੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਸ਼ੌਰਟਕਟ ਜੋੜਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ੌਰਟਕਟ ਪੜ੍ਹੋ"</string>
<string name="permdesc_read_settings" msgid="5833423719057558387">"ਐਪ ਨੂੰ ਹੋਮ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ੌਰਟਕਟ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ੌਰਟਕਟ ਲਿਖੋ"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"ਐਪ ਨੂੰ ਹੋਮ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ੌਰਟਕਟ ਬਦਲਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਫੋਨ ਕਾਲਾਂ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਹੈ"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"ਵਿਜੇਟ ਲੋਡ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"ਸੈਟਅਪ"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"ਇਹ ਇੱਕ ਸਿਸਟਮ ਐਪ ਹੈ ਅਤੇ ਇਸਨੂੰ ਅਣਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"ਬਿਨਾਂ ਨਾਮ ਦਿੱਤਾ ਫੋਲਡਰ"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"ਸਫ਼ਾ %2$d ਦਾ %1$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"ਹੋਮ ਸਕ੍ਰੀਨ %2$d ਦੀ %1$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"ਨਵਾਂ ਹੋਮ ਸਕ੍ਰੀਨ ਸਫ਼ਾ"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"ਸੁਆਗਤ ਹੈ"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"ਆਪਣੇ ਐਪ ਆਈਕਨਾਂ ਨੂੰ ਕਾਪੀ ਕਰੋ"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"ਕੀ ਆਪਣੀਆਂ ਪੁਰਾਣੀਆਂ ਹੋਮ ਸਕ੍ਰੀਨਾਂ ਤੋਂ ਆਈਕਨਾਂ ਅਤੇ ਫੋਲਡਰ ਆਯਾਤ ਕਰਨੇ ਹਨ?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"ਖੋਜੋ"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"ਇਹ ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ।"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"ਇਸ ਆਈਕਨ ਲਈ ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ। ਤੁਸੀਂ ਇਸਨੂੰ ਹਟਾ ਸਕਦੇ ਹੋ ਜਾਂ ਐਪ ਖੋਜ ਸਕਦੇ ਹੋ ਅਤੇ ਇਸਨੂੰ ਮੈਨੂਅਲੀ ਇੰਸਟੌਲ ਕਰ ਸਕਦੇ ਹੋ।"</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ਡਾਉਨਲੋਡ ਹੋਰ ਰਿਹਾ ਹੈ, <xliff:g id="PROGRESS">%2$s</xliff:g> ਸੰਪੂਰਣ"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ਸਥਾਪਿਤ ਕਰਨ ਦੀ ਉਡੀਕ ਕਰ ਰਿਹਾ ਹੈ"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜੋ"</string>
<string name="action_move_here" msgid="2170188780612570250">"ਆਈਟਮ ਨੂੰ ਇੱਥੇ ਮੂਵ ਕਰੋ"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"ਆਈਟਮ ਨੂੰ ਮੁੱਖ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index bbed949cf..5ebd6dc17 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Brak miejsca w Ulubionych"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacje"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Ekran główny"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Usuń"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Odinstaluj"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Informacje o aplikacji"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Usuń"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinstaluj"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"O aplikacji"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalowanie skrótów"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Pozwala aplikacji dodawać skróty bez interwencji użytkownika."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"odczytywanie ustawień i skrótów na ekranie głównym"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index a8c9b5e0a..004b802d7 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Não existe mais espaço no tabuleiro de Favoritos"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplicações"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Ecrã principal"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Remover"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstalar"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Informações da aplicação"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Remover"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Inf. da aplicação"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a uma aplicação adicionar atalhos sem a intervenção do utilizador."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ler definições e atalhos do Ecrã Principal"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index f46dca15c..a2c254e10 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Sem espaço na bandeja de favoritos"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Início"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Remover"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstalar"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Informações do app"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Remover"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Informações do app"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que um app adicione atalhos sem intervenção do usuário."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ler configurações e atalhos da tela inicial"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 512e5cfb4..afa56792e 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Spațiu epuizat în bara Preferate"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplicații"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Ecran de pornire"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Eliminați"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Dezinstalați"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Informații despre aplicație"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Eliminați"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Dezinstalați"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Informații aplicație"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalează comenzi rapide"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite unei aplicații să adauge comenzi rapide fără intervenția utilizatorului."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"citește setări și comenzi rapide pentru ecranul de pornire"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 963c89569..a4e70bbfd 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"В разделе \"Избранное\" больше нет места"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Приложения"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Главный экран"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Убрать"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Удалить"</string>
- <string name="info_target_label" msgid="8053346143994679532">"О приложении"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Убрать"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Удалить"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"О приложении"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Создание ярлыков"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Приложение сможет самостоятельно добавлять ярлыки."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Доступ к настройкам и ярлыкам главного экрана"</string>
diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml
index 84304f6b7..e9f06fc6f 100644
--- a/res/values-si-rLK/strings.xml
+++ b/res/values-si-rLK/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ප්‍රියතම දෑ ඇති තැටියේ තවත් ඉඩ නොමැත"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"යෙදුම්"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"මුල් පිටුව"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"ඉවත් කරන්න"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"අස්ථාපනය කරන්න"</string>
- <string name="info_target_label" msgid="8053346143994679532">"යෙදුම් තොරතුරු"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"ඉවත් කරන්න"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"අස්ථාපනය කරන්න"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"යෙදුම් තොරතුරු"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"කෙටිමං ස්ථාපනය කරන්න"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"පරිශීලක මැදිහත්වීමෙන් තොරව කෙටිමං එක් කිරීමට යෙදුමකට අවසර දෙයි."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"මුල් පිටු සැකසීම් සහ කෙටිමං කියවන්න"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 2875e96b3..239af4ecf 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Na paneli Obľúbené položky už nie je miesto"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikácie"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Domovská stránka"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Odstrániť"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Odinštalovať"</string>
- <string name="info_target_label" msgid="8053346143994679532">"O aplikácii"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Odstrániť"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinštalovať"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Info o aplikácii"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"inštalovať odkazy"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Povoľuje aplikácii pridať odkazy bez zásahu používateľa."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"čítanie nastavení a odkazov plochy"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 2eea38a1c..ad33be122 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"V vrstici za priljubljene ni več prostora"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacije"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Začetni zaslon"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Odstrani"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Odstrani namestitev"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Podatki o aplikaciji"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Odstrani"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odstrani"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Podatki o aplikaciji"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"namestitev bližnjic"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Aplikaciji dovoli dodajanje bližnjic brez posredovanja uporabnika."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"branje nastavitev in bližnjic na začetnem zaslonu"</string>
diff --git a/res/values-sq-rAL/strings.xml b/res/values-sq-rAL/strings.xml
index 2b535a3a5..0648d3ce0 100644
--- a/res/values-sq-rAL/strings.xml
+++ b/res/values-sq-rAL/strings.xml
@@ -19,15 +19,15 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="application_name" msgid="5181331383435256801">"Nisësi3"</string>
+ <string name="app_name" msgid="649227358658669779">"Nisësi3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Puna"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikacioni nuk është i instaluar."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikacioni nuk mundësohet"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplikacioni i shkarkuar është i çaktivizuar në modalitetin e sigurt"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Miniaplikacionet janë të çaktivizuara në modalitetin e sigurt"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Trego memorandumin"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Prek dhe mbaj shtypur për të zgjedhur një miniaplikacion."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Prek dy herë dhe mbaj shtypur për të zgjedhur një miniaplikacion ose për të përdorur veprimet e personalizuara."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Kërko për aplikacione"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Po ngarkon aplikacionet..."</string>
@@ -37,21 +37,23 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Nuk ka më hapësirë në tabakanë \"Të preferuarat\""</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacionet"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Faqja kryesore"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Hiq"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Çinstalo"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Informacion mbi aplikacionin"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Hiqe"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Çinstalo"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Informacion mbi aplikacionin"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalo shkurtore"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Lejon një aplikacion të shtojë shkurtore pa ndërhyrjen e përdoruesit."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lexo cilësimet dhe shkurtoret e ekranit bazë"</string>
<string name="permdesc_read_settings" msgid="5833423719057558387">"Lejon aplikacionin të lexojë cilësimet dhe shkurtoret në ekranin bazë."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"shkruaj cilësimet dhe shkurtoret e ekranit bazë"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Lejon aplikacionin të ndryshojë cilësimet dhe shkurtoret në ekranin bazë."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> nuk lejohet të kryejë telefonata"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problem në ngarkimin e miniaplikacionit"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Konfiguro"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ky është aplikacion sistemi dhe nuk mund të çinstalohet."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Dosje e paemërtuar"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Faqja: %1$d nga gjithsej %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Ekrani bazë: %1$d nga gjithsej %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Faqja e ekranit të ri kryesor"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Mirë se erdhe"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Kopjo ikonat e aplikacionit"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Të importohen ikona dhe dosje nga ekranet e vjetra bazë?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Kërko"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Aplikacioni nuk është i instaluar"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikacioni për këtë ikonë nuk është i instaluar. Mund ta heqësh ose të kërkosh aplikacionin dhe ta instalosh atë në mënyrë manuale."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> po shkarkohet, <xliff:g id="PROGRESS">%2$s</xliff:g> të përfunduara"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> po pret të instalohet"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Shto në Ekranin bazë"</string>
<string name="action_move_here" msgid="2170188780612570250">"Zhvendose artikullin këtu"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Artikulli u shtua tek ekrani bazë"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 9fdf57410..2d6a4c0e5 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Нема више простора на траци Омиљено"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Апликације"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Почетна"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Уклони"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Деинсталирај"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Информације о апликацији"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Уклони"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Деинсталирај"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Информ. о апликацији"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталирање пречица"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Дозвољава апликацији да додаје пречице без интервенције корисника."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"читање подешавања и пречица на почетном екрану"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 4d9589ea3..82f8da8be 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritfältet är fullt"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Appar"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Startskärm"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Ta bort"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Avinstallera"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Info om appen"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Ta bort"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Avinstallera"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Info om appen"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installera genvägar"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Tillåter att en app lägger till genvägar utan åtgärd från användaren."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"läsa inställningar och genvägar för startsidan"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index bcd5082dc..2633a2f4d 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Hakuna nafasi zaidi katika treya ya Vipendeleo"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Programu"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Mwanzo"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Ondoa"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Sakinusha"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Maelezo ya programu"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Ondoa"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Ondoa"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Maelezo ya programu"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"kuweka njia za mkato"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Huruhusu programu kuongeza njia za mkato bila mtumiaji kuingilia kati."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"soma mipangilio ya Mwanzo na njia za mkato"</string>
diff --git a/res/values-sw340dp-port/styles.xml b/res/values-sw340dp-port/styles.xml
deleted file mode 100644
index 8ac3b5ea1..000000000
--- a/res/values-sw340dp-port/styles.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2011 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<resources>
-<!-- Workspace -->
- <style name="SearchButton">
- <item name="android:layout_gravity">center_vertical</item>
- <item name="android:paddingTop">@dimen/toolbar_button_vertical_padding</item>
- <item name="android:paddingBottom">@dimen/toolbar_button_vertical_padding</item>
- </style>
-</resources>
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
index a7345a705..eb9af9793 100644
--- a/res/values-sw600dp/config.xml
+++ b/res/values-sw600dp/config.xml
@@ -1,7 +1,4 @@
<resources>
<bool name="is_tablet">true</bool>
<bool name="allow_rotation">true</bool>
-
-<!-- DragController -->
- <integer name="config_flingToDeleteMinVelocity">-1000</integer>
</resources>
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index feb1a00a6..8e1e4f180 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -31,4 +31,10 @@
<dimen name="cling_migration_content_margin">64dp</dimen>
<dimen name="cling_migration_content_width">280dp</dimen>
+<!-- Widget tray -->
+ <dimen name="widget_section_indent">56dp</dimen>
+
+
+<!-- DragController -->
+ <dimen name="drag_flingToDeleteMinVelocity">-1000dp</dimen>
</resources>
diff --git a/res/values-sw720dp-land/dimens.xml b/res/values-sw720dp-land/dimens.xml
deleted file mode 100644
index 514980fe0..000000000
--- a/res/values-sw720dp-land/dimens.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <!-- the area at the edge of the screen that makes the workspace go left
- or right while you're dragging. -->
- <dimen name="scroll_zone">100dip</dimen>
-</resources>
diff --git a/res/values-sw720dp-port/dimens.xml b/res/values-sw720dp-port/dimens.xml
deleted file mode 100644
index 6f594d518..000000000
--- a/res/values-sw720dp-port/dimens.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
-<!-- Workspace -->
- <!-- the area at the edge of the screen that makes the workspace go left
- or right while you're dragging. -->
- <dimen name="scroll_zone">40dp</dimen>
-</resources>
diff --git a/res/values-sw720dp/styles.xml b/res/values-sw720dp/styles.xml
index e8b706e23..b7a16f3a7 100644
--- a/res/values-sw720dp/styles.xml
+++ b/res/values-sw720dp/styles.xml
@@ -20,8 +20,6 @@
<resources>
<!-- Workspace -->
- <style name="SearchButton"></style>
-
<style name="DropTargetButtonContainer">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml
index a080bce85..1424cae45 100644
--- a/res/values-ta-rIN/strings.xml
+++ b/res/values-ta-rIN/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"பிடித்தவை ட்ரேயில் இடமில்லை"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"பயன்பாடுகள்"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"முகப்பு"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"அகற்று"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"நிறுவல் நீக்கு"</string>
- <string name="info_target_label" msgid="8053346143994679532">"பயன்பாட்டுத் தகவல்"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"அகற்று"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"நிறுவல் நீக்கு"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"பயன்பாட்டுத் தகவல்"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"குறுக்குவழிகளை நிறுவுதல்"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளைச் சேர்க்கப் பயன்பாட்டை அனுமதிக்கிறது."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"முகப்பின் அமைப்பு மற்றும் குறுக்குவழிகளைப் படித்தல்"</string>
diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml
index 65d9df803..7c950e4aa 100644
--- a/res/values-te-rIN/strings.xml
+++ b/res/values-te-rIN/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ఇష్టమైనవి ట్రేలో ఖాళీ లేదు"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"అనువర్తనాలు"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"హోమ్"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"తీసివేయి"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"అన్ఇన్‌స్టాల్ చేయి"</string>
- <string name="info_target_label" msgid="8053346143994679532">"అనువర్తన సమాచారం"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"తీసివేయి"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"అన్ఇన్‌స్టాల్ చేయి"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"అనువర్తన సమాచారం"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"సత్వరమార్గాలను ఇన్‌స్టాల్ చేయడం"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"వినియోగదారు ప్రమేయం లేకుండా సత్వరమార్గాలను జోడించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"హోమ్ సెట్టింగ్‌లు మరియు సత్వరమార్గాలను చదవడం"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 2813a1f3a..3924463fc 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ไม่มีพื้นที่เหลือในถาดรายการโปรด"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"แอป"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"หน้าแรก"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"ลบ"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"ถอนการติดตั้ง"</string>
- <string name="info_target_label" msgid="8053346143994679532">"ข้อมูลแอป"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"นำออก"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ถอนการติดตั้ง"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"ข้อมูลแอป"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ติดตั้งทางลัด"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"อนุญาตให้แอปเพิ่มทางลัดโดยไม่ต้องให้ผู้ใช้จัดการ"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"อ่านการตั้งค่าและทางลัดหน้าแรกแล้ว"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 23e70eab4..a88f3d261 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Wala nang lugar sa tray ng Mga Paborito"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Home"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Alisin"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"I-uninstall"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Impormasyon ng app"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Alisin"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"I-uninstall"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Impormasyon ng app"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"i-install ang mga shortcut"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Pinapayagan ang isang app na magdagdag ng mga shortcut nang walang panghihimasok ng user."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"basahin ang mga setting at shortcut ng Home"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 4149e9dcc..39e0c6e8b 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoriler tepsisinde başka yer kalmadı"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Uygulamalar"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Ana ekran"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Kaldır"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Yüklemeyi kaldır"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Uygulama bilgileri"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Kaldır"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Yüklemeyi kaldır"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Uygulama bilgileri"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"kısayolları yükle"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Uygulamaya, kullanıcı müdahalesi olmadan kısayol ekleme izni verir."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Ana ekran ayarlarını ve kısayollarını oku"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 804f66fa6..ab5385f10 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"В області \"Вибране\" немає місця"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Додатки"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Головний екран"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Вилучити"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Видалити"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Про програму"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Видалити"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Видалити"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Про додаток"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"створення ярликів"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Дозволяє програмі самостійно додавати ярлики."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"читати налаштування та ярлики головного екрана"</string>
diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml
index 6bfe9b845..abce1ff1b 100644
--- a/res/values-ur-rPK/strings.xml
+++ b/res/values-ur-rPK/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"پسندیدہ ٹرے میں مزید کوئی گنجائش نہیں ہے"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"ایپس"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"ہوم"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"ہٹائیں"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"اَن انسٹال کریں"</string>
- <string name="info_target_label" msgid="8053346143994679532">"ایپ کی معلومات"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"ہٹائیں"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"اَن انسٹال کریں"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"ایپ کی معلومات"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"شارٹ کٹس انسٹال کریں"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"کسی ایپ کو صارف کی مداخلت کے بغیر شارٹ کٹس شامل کرنے کی اجازت دیتا ہے۔"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ہوم ترتیبات اور شارٹ کٹس کو پڑھیں"</string>
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index 34ed118cb..70b15cb3b 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ajratilganlarda birorta ham xona yo‘q"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Ilovalar"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Uy"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Olib tashlash"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"O‘chirish"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Ilova haqida"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Olib tashlash"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"O‘chirish"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Ilova haqida"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"yorliqlar yaratish"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ilovalarga foydalanuvchidan so‘ramasdan yorliqlar qo‘shishga ruxsat beradi."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Uy sozlamalari va yorliqlarini o‘qish"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 7b4b2419c..8ffc97f22 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Không còn chỗ trong khay Mục yêu thích"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Ứng dụng"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Màn hình chính"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Xóa"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Gỡ cài đặt"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Thông tin ứng dụng"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Xóa"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Gỡ cài đặt"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Thông tin ứng dụng"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"cài đặt lối tắt"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Cho phép ứng dụng thêm lối tắt mà không cần sự can thiệp của người dùng."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"đọc cài đặt và lối tắt trên Màn hình chính"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index b42e0216f..b76991bcf 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"收藏栏已满"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"应用"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"主屏幕"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"删除"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"卸载"</string>
- <string name="info_target_label" msgid="8053346143994679532">"应用信息"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"移除"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"卸载"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"应用信息"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"安装快捷方式"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"允许应用自行添加快捷方式。"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"读取主屏幕设置和快捷方式"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index beb3be1af..12bdfd64d 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"我的收藏寄存區沒有足夠空間"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"應用程式"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"主畫面"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"移除"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"解除安裝"</string>
- <string name="info_target_label" msgid="8053346143994679532">"應用程式資料"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"移除"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"解除安裝"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"應用程式資料"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"安裝捷徑"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"允許應用程式無需使用者許可也可新增捷徑。"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"讀取主畫面的設定和捷徑"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 611e70861..41d199a96 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"「我的最愛」匣已無可用空間"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"應用程式"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"主螢幕"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"移除"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"解除安裝"</string>
- <string name="info_target_label" msgid="8053346143994679532">"應用程式資訊"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"移除"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"解除安裝"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"應用程式資訊"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"安裝捷徑"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"允許應用程式自動新增捷徑。"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"讀取主螢幕的設定和捷徑"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 6b6fc8ef3..14c6819d1 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -37,9 +37,9 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Asisekho isikhala kwitreyi lezintandokazi"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Izinhlelo zokusebenza"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Ikhaya"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Susa"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Khipha"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Ulwazi lohlelo lokusebenza"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Susa"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Khipha"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Ulwazi lohlelo lokusebenza"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"faka izinqamuleli"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ivumela uhlelo lokusebenza ukufaka izinqamuleli ngaphandle kokungenelela komsebenzisi."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"funda izilungiselelo zokuthi Ikhaya nezinqamuleli"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 827332ad7..55166cf24 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -36,49 +36,9 @@
<attr name="windowSize" format="integer" />
</declare-styleable>
- <!-- Workspace specific attributes. These attributes are used to customize
- the workspace in XML files. -->
- <declare-styleable name="Workspace">
- <!-- The first screen the workspace should display. -->
- <attr name="defaultScreen" format="integer" />
- <!-- The number of horizontal cells in the CellLayout -->
- <attr name="cellCountX" format="integer" />
- <!-- The number of vertical cells in the CellLayout -->
- <attr name="cellCountY" format="integer" />
- </declare-styleable>
-
- <!-- Hotseat specific attributes. These attributes are used to customize
- the hotseat in XML files. -->
- <declare-styleable name="Hotseat">
- <!-- The number of horizontal cells in the CellLayout -->
- <attr name="cellCountX" />
- <!-- The number of vertical cells in the CellLayout -->
- <attr name="cellCountY" />
- </declare-styleable>
-
- <!-- CellLayout specific attributes. These attributes are used to customize
- a CellLayout view in XML files. -->
- <declare-styleable name="CellLayout">
- <!-- The width of a single cell -->
- <attr name="cellWidth" format="dimension" />
- <!-- The height of a single cell -->
- <attr name="cellHeight" format="dimension" />
- <!-- An override for the width and height gap to allow users to specify
- a specific size for the page using spacing instead of resolving the
- spacing from the width of the page -->
- <attr name="widthGap" format="dimension" />
- <attr name="heightGap" format="dimension" />
- <!-- The max gap size for each dimension -->
- <attr name="maxGap" format="dimension" />
- </declare-styleable>
-
<!-- PagedView specific attributes. These attributes are used to customize
a PagedView view in XML files. -->
<declare-styleable name="PagedView">
- <!-- A spacing override for the icons within a page -->
- <attr name="pageLayoutWidthGap" format="dimension" />
- <attr name="pageLayoutHeightGap" format="dimension" />
-
<!-- The page indicator for this workspace -->
<attr name="pageIndicator" format="reference" />
</declare-styleable>
diff --git a/res/values/config.xml b/res/values/config.xml
index 85500883a..4efffffe1 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -17,7 +17,6 @@
<bool name="enable_backup">false</bool>
<!-- DragController -->
- <integer name="config_flingToDeleteMinVelocity">-1500</integer>
<item type="id" name="drag_event_parity" />
<!-- AllApps & Launcher transitions -->
@@ -41,8 +40,6 @@
is used for internal (baked-in) padding -->
<integer name="config_allAppsButtonPaddingPercent">17</integer>
- <integer name="config_workspaceDefaultScreen">0</integer>
-
<!-- Workspace -->
<!-- The duration (in ms) of the fade animation on the object outlines, used when
we are dragging objects around on the home screen. -->
@@ -71,10 +68,6 @@
filter the activities shown in the launcher. Can be empty. -->
<string name="app_filter_class" translatable="false"></string>
- <!-- Name of a subclass of com.android.launcher3.BuildInfo used to
- get build information. Can be empty. -->
- <string name="build_info_class" translatable="false"></string>
-
<!-- View ID to use for QSB widget -->
<item type="id" name="qsb_widget" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1025abaa6..90083c09c 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -15,8 +15,6 @@
-->
<resources>
- <dimen name="app_icon_size">64dp</dimen>
-
<!-- Dynamic Grid -->
<dimen name="dynamic_grid_edge_margin">6dp</dimen>
<dimen name="dynamic_grid_search_bar_height">48dp</dimen>
@@ -49,9 +47,6 @@
<dimen name="cling_migration_content_margin">16dp</dimen>
<dimen name="cling_migration_content_width">280dp</dimen>
-<!-- Workspace -->
- <dimen name="workspace_max_gap">16dp</dimen>
-
<!-- QSB -->
<dimen name="toolbar_button_vertical_padding">4dip</dimen>
<dimen name="toolbar_button_horizontal_padding">12dip</dimen>
@@ -75,7 +70,6 @@
<dimen name="all_apps_grid_section_y_offset">8dp</dimen>
<dimen name="all_apps_grid_section_text_size">24sp</dimen>
<dimen name="all_apps_search_bar_height">48dp</dimen>
- <dimen name="all_apps_search_bar_prediction_bar_padding">8dp</dimen>
<dimen name="all_apps_icon_top_bottom_padding">8dp</dimen>
<dimen name="all_apps_icon_width_gap">24dp</dimen>
<!-- The top padding should account for the existing all_apps_list_top_bottom_padding -->
@@ -88,15 +82,14 @@
<dimen name="all_apps_background_canvas_height">475dp</dimen>
<!-- Widget tray -->
- <dimen name="widget_container_inset">8dp</dimen>
<dimen name="widget_preview_label_vertical_padding">8dp</dimen>
<dimen name="widget_preview_label_horizontal_padding">8dp</dimen>
- <dimen name="widget_preview_horizontal_padding">8dp</dimen>
<dimen name="widget_section_height">56dp</dimen>
<dimen name="widget_section_icon_size">40dp</dimen>
<dimen name="widget_section_vertical_padding">8dp</dimen>
<dimen name="widget_section_horizontal_padding">16dp</dimen>
+ <dimen name="widget_section_indent">0dp</dimen>
<dimen name="widget_row_padding">8dp</dimen>
<dimen name="widget_row_divider">2dp</dimen>
@@ -111,10 +104,6 @@
<dimen name="drop_target_drag_padding">14dp</dimen>
<dimen name="drop_target_text_size">14sp</dimen>
- <!-- the area at the edge of the screen that makes the workspace go left
- or right while you're dragging. -->
- <dimen name="scroll_zone">20dp</dimen>
-
<!-- When dragging an item, how much bigger (fixed dps) the dragged view
should be. If 0, it will not be scaled at all. -->
<dimen name="dragViewScale">12dp</dimen>
@@ -123,6 +112,8 @@
and drop targets like all-apps and folders -->
<dimen name="drag_elevation">30dp</dimen>
+ <dimen name="drag_flingToDeleteMinVelocity">-1500dp</dimen>
+
<!-- Theme -->
<dimen name="quantum_panel_outer_padding">4dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6daa4526c..ecbdcc2ad 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -78,12 +78,12 @@
for accessibilty (spoken when the button gets focus). -->
<string name="all_apps_home_button_label">Home</string>
- <!-- Label for delete drop target. [CHAR_LIMIT=20] -->
- <string name="delete_target_label">Remove</string>
+ <!-- Label for remove drop target. [CHAR_LIMIT=20] -->
+ <string name="remove_drop_target_label">Remove</string>
<!-- Label for uninstall drop target. [CHAR_LIMIT=20]-->
- <string name="delete_target_uninstall_label">Uninstall</string>
- <!-- Label for the info icon. [CHAR_LIMIT=20] -->
- <string name="info_target_label">App info</string>
+ <string name="uninstall_drop_target_label">Uninstall</string>
+ <!-- Label for app info drop target. [CHAR_LIMIT=20] -->
+ <string name="app_info_drop_target_label">App info</string>
<!-- Permissions: -->
<skip />
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 4eee13099..c03f08d4e 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -56,8 +56,6 @@
<item name="customShadows">false</item>
</style>
- <style name="SearchButton"></style>
-
<style name="DropTargetButtonContainer">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
diff --git a/src/com/android/launcher3/AnotherWindowDropTarget.java b/src/com/android/launcher3/AnotherWindowDropTarget.java
new file mode 100644
index 000000000..0e188743f
--- /dev/null
+++ b/src/com/android/launcher3/AnotherWindowDropTarget.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 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.launcher3;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.graphics.Rect;
+
+/**
+ * Drop target used when another window (i.e. another process) has accepted a global system drag.
+ * If the accepted item was a shortcut, we delete it from Launcher.
+ */
+public class AnotherWindowDropTarget implements DropTarget {
+ final Launcher mLauncher;
+
+ public AnotherWindowDropTarget (Context context) { mLauncher = (Launcher) context; }
+
+ @Override
+ public boolean isDropEnabled() { return true; }
+
+ @Override
+ public void onDrop(DragObject dragObject) {
+ dragObject.deferDragViewCleanupPostAnimation = false;
+ LauncherModel.deleteItemFromDatabase(mLauncher, (ShortcutInfo) dragObject.dragInfo);
+ }
+
+ @Override
+ public void onDragEnter(DragObject dragObject) {}
+
+ @Override
+ public void onDragOver(DragObject dragObject) {}
+
+ @Override
+ public void onDragExit(DragObject dragObject) {}
+
+ @Override
+ public void onFlingToDelete(DragObject dragObject, PointF vec) {}
+
+ @Override
+ public boolean acceptDrop(DragObject dragObject) {
+ return dragObject.dragInfo instanceof ShortcutInfo;
+ }
+
+ @Override
+ public void prepareAccessibilityDrop() {}
+
+ // These methods are implemented in Views
+ @Override
+ public void getHitRectRelativeToDragLayer(Rect outRect) {}
+}
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index ede6c71c1..1923df76a 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -28,13 +28,11 @@ import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.util.ComponentKey;
import java.util.ArrayList;
-import java.util.Arrays;
/**
* Represents an app in AllAppsView.
*/
public class AppInfo extends ItemInfo {
- private static final String TAG = "Launcher3.AppInfo";
/**
* The intent used to start the application.
@@ -111,8 +109,7 @@ public class AppInfo extends ItemInfo {
return "ApplicationInfo(title=" + title + " id=" + this.id
+ " type=" + this.itemType + " container=" + this.container
+ " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY
- + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
- + " user=" + user + ")";
+ + " spanX=" + spanX + " spanY=" + spanY + " user=" + user + ")";
}
/**
@@ -121,7 +118,7 @@ public class AppInfo extends ItemInfo {
public static void dumpApplicationInfoList(String tag, String label, ArrayList<AppInfo> list) {
Log.d(tag, label + " size=" + list.size());
for (AppInfo info: list) {
- Log.d(tag, " title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap
+ Log.d(tag, " title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap
+ " componentName=" + info.componentName.getPackageName());
}
}
diff --git a/src/com/android/launcher3/AppInfoDropTargetBar.java b/src/com/android/launcher3/AppInfoDropTargetBar.java
new file mode 100644
index 000000000..99a1f415d
--- /dev/null
+++ b/src/com/android/launcher3/AppInfoDropTargetBar.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.launcher3.dragndrop.DragController;
+
+public class AppInfoDropTargetBar extends BaseDropTargetBar {
+ private ButtonDropTarget mAppInfoDropTarget;
+
+ public AppInfoDropTargetBar(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public AppInfoDropTargetBar(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ // Get the individual components
+ mAppInfoDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.info_target_text);
+
+ mAppInfoDropTarget.setDropTargetBar(this);
+ }
+
+ @Override
+ public void setup(Launcher launcher, DragController dragController) {
+ dragController.addDragListener(this);
+
+ dragController.addDragListener(mAppInfoDropTarget);
+
+ dragController.addDropTarget(mAppInfoDropTarget);
+
+ mAppInfoDropTarget.setLauncher(launcher);
+ }
+
+ @Override
+ public void showDropTargets() {
+ animateDropTargetBarToAlpha(1f, DEFAULT_DRAG_FADE_DURATION);
+ }
+
+ @Override
+ public void hideDropTargets() {
+ animateDropTargetBarToAlpha(0f, DEFAULT_DRAG_FADE_DURATION);
+ }
+
+ private void animateDropTargetBarToAlpha(float alpha, int duration) {
+ animateViewAlpha(mDropTargetBarAnimator, mDropTargetBar, alpha,duration);
+ }
+
+ @Override
+ public void enableAccessibleDrag(boolean enable) {
+ mAppInfoDropTarget.enableAccessibleDrag(enable);
+ }
+}
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index d86853608..7bd5284fc 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -1,5 +1,7 @@
package com.android.launcher3;
+import com.android.launcher3.dragndrop.DragLayer;
+
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
@@ -9,6 +11,7 @@ import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Point;
import android.graphics.Rect;
import android.view.Gravity;
import android.widget.FrameLayout;
@@ -21,7 +24,10 @@ public class AppWidgetResizeFrame extends FrameLayout {
private static final float DIMMED_HANDLE_ALPHA = 0f;
private static final float RESIZE_THRESHOLD = 0.66f;
- private static Rect sTmpRect = new Rect();
+ private static final Rect sTmpRect = new Rect();
+
+ // Represents the cell size on the grid in the two orientations.
+ private static Point[] sCellSize;
private final Launcher mLauncher;
private final LauncherAppWidgetHostView mWidgetView;
@@ -353,28 +359,27 @@ public class AppWidgetResizeFrame extends FrameLayout {
}
public static Rect getWidgetSizeRanges(Launcher launcher, int spanX, int spanY, Rect rect) {
+ if (sCellSize == null) {
+ InvariantDeviceProfile inv = LauncherAppState.getInstance().getInvariantDeviceProfile();
+
+ // Initiate cell sizes.
+ sCellSize = new Point[2];
+ sCellSize[0] = inv.landscapeProfile.getCellSize();
+ sCellSize[1] = inv.portraitProfile.getCellSize();
+ }
+
if (rect == null) {
rect = new Rect();
}
- Rect landMetrics = Workspace.getCellLayoutMetrics(launcher, CellLayout.LANDSCAPE);
- Rect portMetrics = Workspace.getCellLayoutMetrics(launcher, CellLayout.PORTRAIT);
final float density = launcher.getResources().getDisplayMetrics().density;
// Compute landscape size
- int cellWidth = landMetrics.left;
- int cellHeight = landMetrics.top;
- int widthGap = landMetrics.right;
- int heightGap = landMetrics.bottom;
- int landWidth = (int) ((spanX * cellWidth + (spanX - 1) * widthGap) / density);
- int landHeight = (int) ((spanY * cellHeight + (spanY - 1) * heightGap) / density);
+ int landWidth = (int) ((spanX * sCellSize[0].x) / density);
+ int landHeight = (int) ((spanY * sCellSize[0].y) / density);
// Compute portrait size
- cellWidth = portMetrics.left;
- cellHeight = portMetrics.top;
- widthGap = portMetrics.right;
- heightGap = portMetrics.bottom;
- int portWidth = (int) ((spanX * cellWidth + (spanX - 1) * widthGap) / density);
- int portHeight = (int) ((spanY * cellHeight + (spanY - 1) * heightGap) / density);
+ int portWidth = (int) ((spanX * sCellSize[1].x) / density);
+ int portHeight = (int) ((spanY * sCellSize[1].y) / density);
rect.set(portWidth, landHeight, landWidth, portHeight);
return rect;
}
@@ -453,10 +458,10 @@ public class AppWidgetResizeFrame extends FrameLayout {
PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", lp.y, newY);
ObjectAnimator oa =
LauncherAnimUtils.ofPropertyValuesHolder(lp, this, width, height, x, y);
- ObjectAnimator leftOa = LauncherAnimUtils.ofFloat(mLeftHandle, "alpha", 1.0f);
- ObjectAnimator rightOa = LauncherAnimUtils.ofFloat(mRightHandle, "alpha", 1.0f);
- ObjectAnimator topOa = LauncherAnimUtils.ofFloat(mTopHandle, "alpha", 1.0f);
- ObjectAnimator bottomOa = LauncherAnimUtils.ofFloat(mBottomHandle, "alpha", 1.0f);
+ ObjectAnimator leftOa = LauncherAnimUtils.ofFloat(mLeftHandle, ALPHA, 1.0f);
+ ObjectAnimator rightOa = LauncherAnimUtils.ofFloat(mRightHandle, ALPHA, 1.0f);
+ ObjectAnimator topOa = LauncherAnimUtils.ofFloat(mTopHandle, ALPHA, 1.0f);
+ ObjectAnimator bottomOa = LauncherAnimUtils.ofFloat(mBottomHandle, ALPHA, 1.0f);
oa.addUpdateListener(new AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
requestLayout();
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
index c11824054..e0946ea14 100644
--- a/src/com/android/launcher3/BaseContainerView.java
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -22,6 +22,8 @@ import android.util.AttributeSet;
import android.util.Log;
import android.widget.LinearLayout;
+import com.android.launcher3.config.ProviderConfig;
+
/**
* A base container view, which supports resizing.
*/
@@ -71,7 +73,7 @@ public abstract class BaseContainerView extends LinearLayout implements Insettab
* Sets the search bar bounds for this container view to match.
*/
final public void setSearchBarBounds(Rect bounds) {
- if (LauncherAppState.isDogfoodBuild() && !isValidSearchBarBounds(bounds)) {
+ if (ProviderConfig.IS_DOGFOOD_BUILD && !isValidSearchBarBounds(bounds)) {
Log.e(TAG, "Invalid search bar bounds: " + bounds);
}
diff --git a/src/com/android/launcher3/BaseDropTargetBar.java b/src/com/android/launcher3/BaseDropTargetBar.java
new file mode 100644
index 000000000..303acd702
--- /dev/null
+++ b/src/com/android/launcher3/BaseDropTargetBar.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.AccelerateInterpolator;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.dragndrop.DragController;
+
+/**
+ * Base class for drop target bars (where you can drop apps to do actions such as uninstall).
+ */
+public abstract class BaseDropTargetBar extends FrameLayout implements DragController.DragListener {
+ protected static final int DEFAULT_DRAG_FADE_DURATION = 175;
+
+ protected View mDropTargetBar;
+
+ protected LauncherViewPropertyAnimator mDropTargetBarAnimator;
+ protected static final AccelerateInterpolator sAccelerateInterpolator =
+ new AccelerateInterpolator();
+ protected boolean mAccessibilityEnabled = false;
+
+ protected boolean mDeferOnDragEnd;
+
+ public BaseDropTargetBar(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public BaseDropTargetBar(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mDropTargetBar = findViewById(R.id.drag_target_bar);
+
+ // Create the various fade animations
+ mDropTargetBar.setAlpha(0f);
+ mDropTargetBarAnimator = new LauncherViewPropertyAnimator(mDropTargetBar);
+ mDropTargetBarAnimator.setInterpolator(sAccelerateInterpolator);
+ mDropTargetBarAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // Ensure that the view is visible for the animation
+ mDropTargetBar.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mDropTargetBar != null) {
+ AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
+ }
+ }
+ });
+ }
+
+
+ /**
+ * Convenience method to animate the alpha of a view using hardware layers.
+ */
+ protected void animateViewAlpha(LauncherViewPropertyAnimator animator, View v, float alpha,
+ int duration) {
+ if (v == null) {
+ return;
+ }
+
+ animator.cancel();
+ if (Float.compare(v.getAlpha(), alpha) != 0) {
+ if (duration > 0) {
+ animator.alpha(alpha).withLayer().setDuration(duration).start();
+ } else {
+ v.setAlpha(alpha);
+ AlphaUpdateListener.updateVisibility(v, mAccessibilityEnabled);
+ }
+ }
+ }
+
+ /*
+ * DragController.DragListener implementation
+ */
+ @Override
+ public void onDragStart(DragSource source, ItemInfo info, int dragAction) {
+ showDropTargets();
+ }
+
+ /**
+ * This is called to defer hiding the delete drop target until the drop animation has completed,
+ * instead of hiding immediately when the drag has ended.
+ */
+ protected void deferOnDragEnd() {
+ mDeferOnDragEnd = true;
+ }
+
+ @Override
+ public void onDragEnd() {
+ if (!mDeferOnDragEnd) {
+ hideDropTargets();
+ } else {
+ mDeferOnDragEnd = false;
+ }
+ }
+
+ public abstract void showDropTargets();
+
+ public abstract void hideDropTargets();
+
+ public abstract void enableAccessibleDrag(boolean enable);
+
+ public abstract void setup(Launcher launcher, DragController dragController);
+}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index c8f5a4594..a3b92dcf7 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -131,7 +131,7 @@ public class BubbleTextView extends TextView
}
mLongPressHelper = new CheckLongPressHelper(this);
- mStylusEventHelper = new StylusEventHelper(this);
+ mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
mOutlineHelper = HolographicOutlineHelper.obtain(getContext());
if (mCustomShadowsEnabled) {
@@ -268,7 +268,7 @@ public class BubbleTextView extends TextView
boolean result = super.onTouchEvent(event);
// Check for a stylus button press, if it occurs cancel any long press checks.
- if (mStylusEventHelper.checkAndPerformStylusEvent(event)) {
+ if (mStylusEventHelper.onMotionEvent(event)) {
mLongPressHelper.cancelLongPress();
result = true;
}
diff --git a/src/com/android/launcher3/BuildInfo.java b/src/com/android/launcher3/BuildInfo.java
index b49ee0d9b..1392d7a43 100644
--- a/src/com/android/launcher3/BuildInfo.java
+++ b/src/com/android/launcher3/BuildInfo.java
@@ -1,32 +1,9 @@
package com.android.launcher3;
-import android.text.TextUtils;
-import android.util.Log;
-
+// TODO: Remove this class once all its references are gone.
public class BuildInfo {
- private static final boolean DBG = false;
- private static final String TAG = "BuildInfo";
public boolean isDogfoodBuild() {
return false;
}
-
- public static BuildInfo loadByName(String className) {
- if (TextUtils.isEmpty(className)) return new BuildInfo();
-
- if (DBG) Log.d(TAG, "Loading BuildInfo: " + className);
- try {
- Class<?> cls = Class.forName(className);
- return (BuildInfo) cls.newInstance();
- } catch (ClassNotFoundException e) {
- Log.e(TAG, "Bad BuildInfo class", e);
- } catch (InstantiationException e) {
- Log.e(TAG, "Bad BuildInfo class", e);
- } catch (IllegalAccessException e) {
- Log.e(TAG, "Bad BuildInfo class", e);
- } catch (ClassCastException e) {
- Log.e(TAG, "Bad BuildInfo class", e);
- }
- return new BuildInfo();
- }
}
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 40a4678de..d8826831a 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -39,6 +39,9 @@ import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import android.widget.TextView;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.util.Thunk;
/**
@@ -47,11 +50,11 @@ import com.android.launcher3.util.Thunk;
public abstract class ButtonDropTarget extends TextView
implements DropTarget, DragController.DragListener, OnClickListener {
- private static int DRAG_VIEW_DROP_DURATION = 285;
+ private static final int DRAG_VIEW_DROP_DURATION = 285;
protected Launcher mLauncher;
private int mBottomDragPadding;
- protected SearchDropTargetBar mSearchDropTargetBar;
+ protected BaseDropTargetBar mDropTargetBar;
/** Whether this drop target is active for the current drag */
protected boolean mActive;
@@ -62,6 +65,8 @@ public abstract class ButtonDropTarget extends TextView
protected ColorStateList mOriginalTextColor;
protected Drawable mDrawable;
+ protected DeviceProfile mDeviceProfile;
+
private AnimatorSet mCurrentColorAnim;
@Thunk ColorMatrix mSrcFilter, mDstFilter, mCurrentFilter;
@@ -81,8 +86,8 @@ public abstract class ButtonDropTarget extends TextView
mOriginalTextColor = getTextColors();
// Remove the text in the Phone UI in landscape
- DeviceProfile grid = ((Launcher) getContext()).getDeviceProfile();
- if (grid.isVerticalBarLayout()) {
+ mDeviceProfile = ((Launcher) getContext()).getDeviceProfile();
+ if (mDeviceProfile.isVerticalBarLayout()) {
setText("");
}
}
@@ -104,8 +109,8 @@ public abstract class ButtonDropTarget extends TextView
mLauncher = launcher;
}
- public void setSearchDropTargetBar(SearchDropTargetBar searchDropTargetBar) {
- mSearchDropTargetBar = searchDropTargetBar;
+ public void setDropTargetBar(BaseDropTargetBar dropTargetBar) {
+ mDropTargetBar = dropTargetBar;
}
@Override
@@ -189,8 +194,8 @@ public abstract class ButtonDropTarget extends TextView
}
}
- @Override
- public final void onDragStart(DragSource source, Object info, int dragAction) {
+ @Override
+ public void onDragStart(DragSource source, ItemInfo info, int dragAction) {
mActive = supportsDrop(source, info);
mDrawable.setColorFilter(null);
if (mCurrentColorAnim != null) {
@@ -206,7 +211,7 @@ public abstract class ButtonDropTarget extends TextView
return supportsDrop(dragObject.dragSource, dragObject.dragInfo);
}
- protected abstract boolean supportsDrop(DragSource source, Object info);
+ protected abstract boolean supportsDrop(DragSource source, ItemInfo info);
@Override
public boolean isDropEnabled() {
@@ -232,13 +237,13 @@ public abstract class ButtonDropTarget extends TextView
final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
width, height);
final float scale = (float) to.width() / from.width();
- mSearchDropTargetBar.deferOnDragEnd();
+ mDropTargetBar.deferOnDragEnd();
Runnable onAnimationEndRunnable = new Runnable() {
@Override
public void run() {
completeDrop(d);
- mSearchDropTargetBar.onDragEnd();
+ mDropTargetBar.onDragEnd();
mLauncher.exitSpringLoadedDragModeDelayed(true, 0, null);
}
};
@@ -297,11 +302,6 @@ public abstract class ButtonDropTarget extends TextView
return to;
}
- @Override
- public void getLocationInDragLayer(int[] loc) {
- mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
- }
-
public void enableAccessibleDrag(boolean enable) {
setOnClickListener(enable ? this : null);
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 0a2a01728..eb70b1362 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -18,7 +18,6 @@ package com.android.launcher3;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
@@ -46,12 +45,15 @@ import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.DecelerateInterpolator;
+import android.widget.Toast;
import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
import com.android.launcher3.FolderIcon.FolderRingAnimator;
+import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
import com.android.launcher3.accessibility.FolderAccessibilityHelper;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
+import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.util.ParcelableSparseArray;
import com.android.launcher3.util.Thunk;
@@ -150,9 +152,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
private static final boolean DESTRUCTIVE_REORDER = false;
private static final boolean DEBUG_VISUALIZE_OCCUPIED = false;
- static final int LANDSCAPE = 0;
- static final int PORTRAIT = 1;
-
private static final float REORDER_PREVIEW_MAGNITUDE = 0.12f;
private static final int REORDER_ANIMATION_DURATION = 150;
@Thunk float mReorderPreviewAnimationMagnitude;
@@ -272,7 +271,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap,
mCountX, mCountY);
- mStylusEventHelper = new StylusEventHelper(this);
+ mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
mTouchFeedbackView = new ClickShadowView(context);
addView(mTouchFeedbackView);
@@ -334,7 +333,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
// enabled to allow rearranging the different home screens. So check what mode
// the workspace is in, and only perform stylus button presses while in overview mode.
if (mLauncher.mWorkspace.isInOverviewMode()
- && mStylusEventHelper.checkAndPerformStylusEvent(ev)) {
+ && mStylusEventHelper.onMotionEvent(ev)) {
return true;
}
return handled;
@@ -400,10 +399,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
mIsDragTarget = false;
}
- boolean isDragTarget() {
- return mIsDragTarget;
- }
-
void setIsDragOverlapping(boolean isDragOverlapping) {
if (mIsDragOverlapping != isDragOverlapping) {
mIsDragOverlapping = isDragOverlapping;
@@ -420,10 +415,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
}
}
- public boolean getIsDragOverlapping() {
- return mIsDragOverlapping;
- }
-
public void disableJailContent() {
mJailContent = false;
}
@@ -469,12 +460,9 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
for (int i = 0; i < mDragOutlines.length; i++) {
final float alpha = mDragOutlineAlphas[i];
if (alpha > 0) {
- final Rect r = mDragOutlines[i];
- mTempRect.set(r);
- Utilities.scaleRectAboutCenter(mTempRect, getChildrenScale());
final Bitmap b = (Bitmap) mDragOutlineAnims[i].getTag();
paint.setAlpha((int)(alpha + .5f));
- canvas.drawBitmap(b, null, mTempRect, paint);
+ canvas.drawBitmap(b, null, mDragOutlines[i], paint);
}
}
@@ -589,7 +577,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
try {
dispatchRestoreInstanceState(states);
} catch (IllegalArgumentException ex) {
- if (LauncherAppState.isDogfoodBuild()) {
+ if (ProviderConfig.IS_DOGFOOD_BUILD) {
throw ex;
}
// Mismatched viewId / viewType preventing restore. Skip restore on production builds.
@@ -1063,53 +1051,57 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
mDragCell[0] = cellX;
mDragCell[1] = cellY;
- // Find the top left corner of the rect the object will occupy
- final int[] topLeft = mTmpPoint;
- cellToPoint(cellX, cellY, topLeft);
-
- int left = topLeft[0];
- int top = topLeft[1];
-
- if (v != null && dragOffset == null) {
- // When drawing the drag outline, it did not account for margin offsets
- // added by the view's parent.
- MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
- left += lp.leftMargin;
- top += lp.topMargin;
-
- // Offsets due to the size difference between the View and the dragOutline.
- // There is a size difference to account for the outer blur, which may lie
- // outside the bounds of the view.
- top += (v.getHeight() - dragOutline.getHeight()) / 2;
- // We center about the x axis
- left += ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
- - dragOutline.getWidth()) / 2;
- } else {
- if (dragOffset != null && dragRegion != null) {
- // Center the drag region *horizontally* in the cell and apply a drag
- // outline offset
- left += dragOffset.x + ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
- - dragRegion.width()) / 2;
- int cHeight = getShortcutsAndWidgets().getCellContentHeight();
- int cellPaddingY = (int) Math.max(0, ((mCellHeight - cHeight) / 2f));
- top += dragOffset.y + cellPaddingY;
- } else {
- // Center the drag outline in the cell
- left += ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
- - dragOutline.getWidth()) / 2;
- top += ((mCellHeight * spanY) + ((spanY - 1) * mHeightGap)
- - dragOutline.getHeight()) / 2;
- }
- }
+
final int oldIndex = mDragOutlineCurrent;
mDragOutlineAnims[oldIndex].animateOut();
mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length;
Rect r = mDragOutlines[mDragOutlineCurrent];
- r.set(left, top, left + dragOutline.getWidth(), top + dragOutline.getHeight());
+
if (resize) {
cellToRect(cellX, cellY, spanX, spanY, r);
+ } else {
+ // Find the top left corner of the rect the object will occupy
+ final int[] topLeft = mTmpPoint;
+ cellToPoint(cellX, cellY, topLeft);
+
+ int left = topLeft[0];
+ int top = topLeft[1];
+
+ if (v != null && dragOffset == null) {
+ // When drawing the drag outline, it did not account for margin offsets
+ // added by the view's parent.
+ MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
+ left += lp.leftMargin;
+ top += lp.topMargin;
+
+ // Offsets due to the size difference between the View and the dragOutline.
+ // There is a size difference to account for the outer blur, which may lie
+ // outside the bounds of the view.
+ top += (v.getHeight() - dragOutline.getHeight()) / 2;
+ // We center about the x axis
+ left += ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
+ - dragOutline.getWidth()) / 2;
+ } else {
+ if (dragOffset != null && dragRegion != null) {
+ // Center the drag region *horizontally* in the cell and apply a drag
+ // outline offset
+ left += dragOffset.x + ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
+ - dragRegion.width()) / 2;
+ int cHeight = getShortcutsAndWidgets().getCellContentHeight();
+ int cellPaddingY = (int) Math.max(0, ((mCellHeight - cHeight) / 2f));
+ top += dragOffset.y + cellPaddingY;
+ } else {
+ // Center the drag outline in the cell
+ left += ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
+ - dragOutline.getWidth()) / 2;
+ top += ((mCellHeight * spanY) + ((spanY - 1) * mHeightGap)
+ - dragOutline.getHeight()) / 2;
+ }
+ }
+ r.set(left, top, left + dragOutline.getWidth(), top + dragOutline.getHeight());
}
+ Utilities.scaleRectAboutCenter(r, getChildrenScale());
mDragOutlineAnims[mDragOutlineCurrent].setTag(dragOutline);
mDragOutlineAnims[mDragOutlineCurrent].animateIn();
@@ -1139,23 +1131,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
*
* @param pixelX The X location at which you want to search for a vacant area.
* @param pixelY The Y location at which you want to search for a vacant area.
- * @param spanX Horizontal span of the object.
- * @param spanY Vertical span of the object.
- * @param result Array in which to place the result, or null (in which case a new array will
- * be allocated)
- * @return The X, Y cell of a vacant area that can contain this object,
- * nearest the requested location.
- */
- int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY, int[] result) {
- return findNearestVacantArea(pixelX, pixelY, spanX, spanY, spanX, spanY, result, null);
- }
-
- /**
- * Find a vacant area that will fit the given bounds nearest the requested
- * cell location. Uses Euclidean distance to score multiple vacant areas.
- *
- * @param pixelX The X location at which you want to search for a vacant area.
- * @param pixelY The Y location at which you want to search for a vacant area.
* @param minSpanX The minimum horizontal span required
* @param minSpanY The minimum vertical span required
* @param spanX Horizontal span of the object.
@@ -1331,9 +1306,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
* @param spanX Horizontal span of the object.
* @param spanY Vertical span of the object.
* @param direction The favored direction in which the views should move from x, y
- * @param exactDirectionOnly If this parameter is true, then only solutions where the direction
- * matches exactly. Otherwise we find the best matching direction.
- * @param occoupied The array which represents which cells in the CellLayout are occupied
+ * @param occupied The array which represents which cells in the CellLayout are occupied
* @param blockOccupied The array which represents which cells in the specified block (cellX,
* cellY, spanX, spanY) are occupied. This is used when try to move a group of views.
* @param result Array in which to place the result, or null (in which case a new array will
@@ -2227,17 +2200,14 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
a.cancel();
}
- AnimatorSet s = LauncherAnimUtils.createAnimatorSet();
- a = s;
- s.playTogether(
- LauncherAnimUtils.ofFloat(child, "scaleX", getChildrenScale()),
- LauncherAnimUtils.ofFloat(child, "scaleY", getChildrenScale()),
- LauncherAnimUtils.ofFloat(child, "translationX", 0f),
- LauncherAnimUtils.ofFloat(child, "translationY", 0f)
- );
- s.setDuration(REORDER_ANIMATION_DURATION);
- s.setInterpolator(new android.view.animation.DecelerateInterpolator(1.5f));
- s.start();
+ a = new LauncherViewPropertyAnimator(child)
+ .scaleX(getChildrenScale())
+ .scaleY(getChildrenScale())
+ .translationX(0)
+ .translationY(0)
+ .setDuration(REORDER_ANIMATION_DURATION);
+ a.setInterpolator(new android.view.animation.DecelerateInterpolator(1.5f));
+ a.start();
}
}
@@ -2254,6 +2224,15 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
mOccupied[i][j] = mTmpOccupied[i][j];
}
}
+
+ long screenId = mLauncher.getWorkspace().getIdForScreen(this);
+ int container = Favorites.CONTAINER_DESKTOP;
+
+ if (mLauncher.isHotseatLayout(this)) {
+ screenId = -1;
+ container = Favorites.CONTAINER_HOTSEAT;
+ }
+
int childCount = mShortcutsAndWidgets.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = mShortcutsAndWidgets.getChildAt(i);
@@ -2262,17 +2241,21 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
// We do a null check here because the item info can be null in the case of the
// AllApps button in the hotseat.
if (info != null) {
- if (info.cellX != lp.tmpCellX || info.cellY != lp.tmpCellY ||
- info.spanX != lp.cellHSpan || info.spanY != lp.cellVSpan) {
- info.requiresDbUpdate = true;
- }
+ final boolean requiresDbUpdate = (info.cellX != lp.tmpCellX
+ || info.cellY != lp.tmpCellY || info.spanX != lp.cellHSpan
+ || info.spanY != lp.cellVSpan);
+
info.cellX = lp.cellX = lp.tmpCellX;
info.cellY = lp.cellY = lp.tmpCellY;
info.spanX = lp.cellHSpan;
info.spanY = lp.cellVSpan;
+
+ if (requiresDbUpdate) {
+ LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId,
+ info.cellX, info.cellY, info.spanX, info.spanY);
+ }
}
}
- mLauncher.getWorkspace().updateItemLocationsInDatabase(this);
}
private void setUseTempCoords(boolean useTempCoords) {
@@ -2860,10 +2843,10 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
// X coordinate of the view in the layout.
@ViewDebug.ExportedProperty
- int x;
+ public int x;
// Y coordinate of the view in the layout.
@ViewDebug.ExportedProperty
- int y;
+ public int y;
boolean dropped;
@@ -2989,6 +2972,26 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
return Utilities.findVacantCell(outXY, spanX, spanY, mCountX, mCountY, mOccupied);
}
+ /**
+ * Returns whether an item can be placed in this CellLayout (after rearranging and/or resizing
+ * if necessary).
+ */
+ public boolean hasReorderSolution(ItemInfo itemInfo) {
+ int[] cellPoint = new int[2];
+ // Check for a solution starting at every cell.
+ for (int cellX = 0; cellX < getCountX(); cellX++) {
+ for (int cellY = 0; cellY < getCountY(); cellY++) {
+ cellToPoint(cellX, cellY, cellPoint);
+ if (findReorderSolution(cellPoint[0], cellPoint[1], itemInfo.minSpanX,
+ itemInfo.minSpanY, itemInfo.spanX, itemInfo.spanY, mDirectionVector, null,
+ true, new ItemConfiguration()).isSolution) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
public boolean isRegionVacant(int x, int y, int spanX, int spanY) {
int x2 = x + spanX - 1;
int y2 = y + spanY - 1;
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index edaf525d6..173e6ab65 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -23,6 +23,7 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AnimationUtils;
+import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.util.FlingAnimation;
import com.android.launcher3.util.Thunk;
@@ -45,20 +46,37 @@ public class DeleteDropTarget extends ButtonDropTarget {
setDrawable(R.drawable.ic_remove_launcher);
}
- public static boolean supportsDrop(Object info) {
+ @Override
+ public void onDragStart(DragSource source, ItemInfo info, int dragAction) {
+ super.onDragStart(source, info, dragAction);
+ setTextBasedOnDragSource(source);
+ }
+
+ /** @return true for items that should have a "Remove" action in accessibility. */
+ public static boolean supportsAccessibleDrop(ItemInfo info) {
return (info instanceof ShortcutInfo)
|| (info instanceof LauncherAppWidgetInfo)
|| (info instanceof FolderInfo);
}
@Override
- protected boolean supportsDrop(DragSource source, Object info) {
- return source.supportsDeleteDropTarget() && supportsDrop(info);
+ protected boolean supportsDrop(DragSource source, ItemInfo info) {
+ return true;
+ }
+
+ /**
+ * Set the drop target's text to either "Remove" or "Cancel" depending on the drag source.
+ */
+ public void setTextBasedOnDragSource(DragSource dragSource) {
+ if (!mDeviceProfile.isVerticalBarLayout()) {
+ setText(dragSource.supportsDeleteDropTarget() ? R.string.remove_drop_target_label
+ : android.R.string.cancel);
+ }
}
@Override
@Thunk void completeDrop(DragObject d) {
- ItemInfo item = (ItemInfo) d.dragInfo;
+ ItemInfo item = d.dragInfo;
if ((d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder)) {
removeWorkspaceOrFolderItem(mLauncher, item, null);
}
@@ -80,7 +98,6 @@ public class DeleteDropTarget extends ButtonDropTarget {
public void onFlingToDelete(final DragObject d, PointF vel) {
// Don't highlight the icon as it's animating
d.dragView.setColor(0);
- d.dragView.updateInitialScaleToCurrentScale();
final DragLayer dragLayer = mLauncher.getDragLayer();
FlingAnimation fling = new FlingAnimation(d, vel,
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index ccbfba178..5032dcf19 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -319,6 +319,18 @@ public class DeviceProfile {
return bounds;
}
+ public Point getCellSize() {
+ Point result = new Point();
+ // Since we are only concerned with the overall padding, layout direction does
+ // not matter.
+ Rect padding = getWorkspacePadding(false /* isLayoutRtl */ );
+ result.x = calculateCellWidth(availableWidthPx - padding.left - padding.right,
+ inv.numColumns);
+ result.y = calculateCellHeight(availableHeightPx - padding.top - padding.bottom,
+ inv.numRows);
+ return result;
+ }
+
/** Returns the workspace padding in the specified orientation */
Rect getWorkspacePadding(boolean isLayoutRtl) {
Rect searchBarBounds = getSearchBarBounds(isLayoutRtl);
@@ -381,13 +393,13 @@ public class DeviceProfile {
}
// The rect returned will be extended to below the system ui that covers the workspace
- Rect getHotseatRect() {
+ public boolean isInHotseatRect(int x, int y) {
if (isVerticalBarLayout()) {
- return new Rect(availableWidthPx - normalHotseatBarHeightPx, 0,
- Integer.MAX_VALUE, availableHeightPx);
+ return (x >= (availableWidthPx - hotseatBarHeightPx))
+ && (y >= 0) && (y <= availableHeightPx);
} else {
- return new Rect(0, availableHeightPx - hotseatBarHeightPx,
- availableWidthPx, Integer.MAX_VALUE);
+ return (x >= 0) && (x <= availableWidthPx)
+ && (y >= (availableHeightPx - hotseatBarHeightPx));
}
}
@@ -403,7 +415,7 @@ public class DeviceProfile {
* When {@code false}, either device is in portrait mode or the device is in landscape mode and
* the hotseat is on the bottom row.
*/
- boolean isVerticalBarLayout() {
+ public boolean isVerticalBarLayout() {
return isLandscape && transposeLayoutWithOrientation;
}
@@ -442,26 +454,17 @@ public class DeviceProfile {
// Layout the search bar space
Rect searchBarBounds = getSearchBarBounds(isLayoutRtl);
View searchBar = launcher.getSearchDropTargetBar();
- lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
+ lp = getDropTargetBarLayoutParams(hasVerticalBarLayout, searchBar, Gravity.TOP);
lp.width = searchBarBounds.width();
lp.height = searchBarBounds.height();
- if (hasVerticalBarLayout) {
- // Vertical search bar space -- The search bar is fixed in the layout to be on the left
- // of the screen regardless of RTL
- lp.gravity = Gravity.LEFT;
-
- LinearLayout targets = (LinearLayout) searchBar.findViewById(R.id.drag_target_bar);
- targets.setOrientation(LinearLayout.VERTICAL);
- FrameLayout.LayoutParams targetsLp = (FrameLayout.LayoutParams) targets.getLayoutParams();
- targetsLp.gravity = Gravity.TOP;
- targetsLp.height = LayoutParams.WRAP_CONTENT;
-
- } else {
- // Horizontal search bar space
- lp.gravity = Gravity.TOP|Gravity.CENTER_HORIZONTAL;
- }
searchBar.setLayoutParams(lp);
+ // Layout the app info bar space
+ View appInfoBar = launcher.getAppInfoDropTargetBar();
+ lp = getDropTargetBarLayoutParams(hasVerticalBarLayout, appInfoBar, Gravity.BOTTOM);
+ lp.bottomMargin = hotseatBarHeightPx;
+ appInfoBar.setLayoutParams(lp);
+
// Layout the workspace
PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace);
lp = (FrameLayout.LayoutParams) workspace.getLayoutParams();
@@ -528,7 +531,6 @@ public class DeviceProfile {
// Layout the Overview Mode
ViewGroup overviewMode = launcher.getOverviewPanel();
if (overviewMode != null) {
- int overviewButtonBarHeight = getOverviewModeButtonBarHeight();
lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams();
lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
@@ -537,7 +539,7 @@ public class DeviceProfile {
int maxWidth = totalItemWidth + (visibleChildCount-1) * overviewModeBarSpacerWidthPx;
lp.width = Math.min(availableWidthPx, maxWidth);
- lp.height = overviewButtonBarHeight;
+ lp.height = getOverviewModeButtonBarHeight();
overviewMode.setLayoutParams(lp);
if (lp.width > totalItemWidth && visibleChildCount > 1) {
@@ -566,6 +568,28 @@ public class DeviceProfile {
}
}
+ private FrameLayout.LayoutParams getDropTargetBarLayoutParams(boolean hasVerticalBarLayout,
+ View dropTargetBar, int verticalGravity) {
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) dropTargetBar.getLayoutParams();
+ if (hasVerticalBarLayout) {
+ // Vertical drop target bar space -- The drop target bar is fixed in the layout to be on
+ // the left of the screen regardless of RTL
+ lp.gravity = Gravity.LEFT;
+ lp.width = normalSearchBarSpaceHeightPx;
+
+ LinearLayout targets = (LinearLayout) dropTargetBar.findViewById(R.id.drag_target_bar);
+ targets.setOrientation(LinearLayout.VERTICAL);
+ FrameLayout.LayoutParams targetsLp = (FrameLayout.LayoutParams) targets.getLayoutParams();
+ targetsLp.gravity = verticalGravity;
+ targetsLp.height = LayoutParams.WRAP_CONTENT;
+ } else {
+ // Horizontal drop target bar space
+ lp.gravity = verticalGravity | Gravity.CENTER_HORIZONTAL;
+ lp.height = searchBarSpaceHeightPx;
+ }
+ return lp;
+ }
+
private int getCurrentWidth() {
return isLandscape
? Math.max(widthPx, heightPx)
diff --git a/src/com/android/launcher3/DragSource.java b/src/com/android/launcher3/DragSource.java
index 2a1346ef5..da32d82a6 100644
--- a/src/com/android/launcher3/DragSource.java
+++ b/src/com/android/launcher3/DragSource.java
@@ -37,7 +37,7 @@ public interface DragSource {
/**
* @return whether items dragged from this source supports 'Delete' drop target (e.g. to remove
- * a shortcut.
+ * a shortcut.) If this returns false, the drop target will say "Cancel" instead of "Remove."
*/
boolean supportsDeleteDropTarget();
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index 434059168..90b8f1c75 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -16,6 +16,8 @@
package com.android.launcher3;
+import com.android.launcher3.dragndrop.DragView;
+
import android.graphics.PointF;
import android.graphics.Rect;
@@ -27,9 +29,7 @@ import com.android.launcher3.accessibility.DragViewStateAnnouncer;
*/
public interface DropTarget {
- public static final String TAG = "DropTarget";
-
- public static class DragObject {
+ class DragObject {
public int x = -1;
public int y = -1;
@@ -49,7 +49,7 @@ public interface DropTarget {
public DragView dragView = null;
/** The data associated with the object being dragged */
- public Object dragInfo = null;
+ public ItemInfo dragInfo = null;
/** Where the drag originated */
public DragSource dragSource = null;
@@ -152,7 +152,4 @@ public interface DropTarget {
// These methods are implemented in Views
void getHitRectRelativeToDragLayer(Rect outRect);
- void getLocationInDragLayer(int[] loc);
- int getLeft();
- int getTop();
}
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index c7b64ec7d..bf4551b26 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -17,6 +17,7 @@ package com.android.launcher3;
import android.content.Context;
import android.util.AttributeSet;
+import android.view.DragEvent;
import android.view.KeyEvent;
import android.widget.EditText;
@@ -62,4 +63,10 @@ public class ExtendedEditText extends EditText {
}
return super.onKeyPreIme(keyCode, event);
}
+
+ @Override
+ public boolean onDragEvent(DragEvent event) {
+ // We don't want this view to interfere with Launcher own drag and drop.
+ return false;
+ }
}
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index d59c64431..2af6a73b9 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -22,6 +22,7 @@ import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewGroup;
+import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.util.FocusLogic;
import com.android.launcher3.util.Thunk;
@@ -90,7 +91,7 @@ public class FocusHelper {
}
if (!(v.getParent() instanceof ShortcutAndWidgetContainer)) {
- if (LauncherAppState.isDogfoodBuild()) {
+ if (ProviderConfig.IS_DOGFOOD_BUILD) {
throw new IllegalStateException("Parent of the focused item is not supported.");
} else {
return false;
diff --git a/src/com/android/launcher3/FocusIndicatorView.java b/src/com/android/launcher3/FocusIndicatorView.java
index 58b38ebcd..a835d9969 100644
--- a/src/com/android/launcher3/FocusIndicatorView.java
+++ b/src/com/android/launcher3/FocusIndicatorView.java
@@ -16,7 +16,7 @@
package com.android.launcher3;
-import android.animation.ObjectAnimator;
+import android.animation.Animator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Canvas;
@@ -37,7 +37,7 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen
private final int[] mIndicatorPos = new int[2];
private final int[] mTargetViewPos = new int[2];
- private ObjectAnimator mCurrentAnimation;
+ private Animator mCurrentAnimation;
private ViewAnimState mTargetState;
private View mLastFocusedView;
@@ -117,12 +117,12 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen
if (getAlpha() > MIN_VISIBLE_ALPHA) {
mTargetState = nextState;
- mCurrentAnimation = LauncherAnimUtils.ofPropertyValuesHolder(this,
- PropertyValuesHolder.ofFloat(View.ALPHA, 1),
- PropertyValuesHolder.ofFloat(View.TRANSLATION_X, mTargetState.x),
- PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, mTargetState.y),
- PropertyValuesHolder.ofFloat(View.SCALE_X, mTargetState.scaleX),
- PropertyValuesHolder.ofFloat(View.SCALE_Y, mTargetState.scaleY));
+ mCurrentAnimation = new LauncherViewPropertyAnimator(this)
+ .alpha(1)
+ .translationX(mTargetState.x)
+ .translationY(mTargetState.y)
+ .scaleX(mTargetState.scaleX)
+ .scaleY(mTargetState.scaleY);
} else {
applyState(nextState);
mCurrentAnimation = LauncherAnimUtils.ofPropertyValuesHolder(this,
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index da895c685..1df12084c 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -36,6 +36,7 @@ import android.text.Spannable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ActionMode;
+import android.view.FocusFinder;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
@@ -52,11 +53,13 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.launcher3.CellLayout.CellInfo;
-import com.android.launcher3.DragController.DragListener;
import com.android.launcher3.FolderInfo.FolderListener;
import com.android.launcher3.UninstallDropTarget.UninstallSource;
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragController.DragListener;
+import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.UiThreadCircularReveal;
@@ -207,8 +210,26 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
});
mFolderName.setOnFocusChangeListener(this);
- // We disable action mode for now since it messes up the view on phones
- mFolderName.setCustomSelectionActionModeCallback(mActionModeCallback);
+ if (!Utilities.ATLEAST_MARSHMALLOW) {
+ // We disable action mode in older OSes where floating selection menu is not yet
+ // available.
+ mFolderName.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return false;
+ }
+
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ public void onDestroyActionMode(ActionMode mode) {
+ }
+
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+ });
+ }
mFolderName.setOnEditorActionListener(this);
mFolderName.setSelectAllOnFocus(true);
mFolderName.setInputType(mFolderName.getInputType() |
@@ -223,23 +244,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mFooterHeight = mFooter.getMeasuredHeight();
}
- private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- return false;
- }
-
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- return false;
- }
-
- public void onDestroyActionMode(ActionMode mode) {
- }
-
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- return false;
- }
- };
-
public void onClick(View v) {
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
@@ -316,7 +320,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
if (commit) {
sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
- String.format(getContext().getString(R.string.folder_renamed), newTitle));
+ getContext().getString(R.string.folder_renamed, newTitle));
}
// This ensures that focus is gained every time the field is clicked, which selects all
@@ -357,11 +361,26 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
@Override
+ protected void onAttachedToWindow() {
+ // requestFocus() causes the focus onto the folder itself, which doesn't cause visual
+ // effect but the next arrow key can start the keyboard focus inside of the folder, not
+ // the folder itself.
+ requestFocus();
+ super.onAttachedToWindow();
+ }
+
+ @Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
// When the folder gets focus, we don't want to announce the list of items.
return true;
}
+ @Override
+ public View focusSearch(int direction) {
+ // When the folder is focused, further focus search should be within the folder contents.
+ return FocusFinder.getInstance().findNextFocus(this, null, direction);
+ }
+
/**
* @return the FolderInfo object associated with this folder
*/
@@ -463,11 +482,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
positionAndSizeAsIcon();
centerAboutIcon();
- PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1);
- PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f);
- PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f);
- final ObjectAnimator oa =
- LauncherAnimUtils.ofPropertyValuesHolder(this, alpha, scaleX, scaleY);
+ final ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(this, 1, 1, 1);
oa.setDuration(mExpandDuration);
openFolderAnim = oa;
@@ -490,8 +505,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
float transY = - 0.075f * (height / 2 - getPivotY());
setTranslationX(transX);
setTranslationY(transY);
- PropertyValuesHolder tx = PropertyValuesHolder.ofFloat("translationX", transX, 0);
- PropertyValuesHolder ty = PropertyValuesHolder.ofFloat("translationY", transY, 0);
+ PropertyValuesHolder tx = PropertyValuesHolder.ofFloat(TRANSLATION_X, transX, 0);
+ PropertyValuesHolder ty = PropertyValuesHolder.ofFloat(TRANSLATION_Y, transY, 0);
Animator drift = ObjectAnimator.ofPropertyValuesHolder(this, tx, ty);
drift.setDuration(mMaterialExpandDuration);
@@ -570,6 +585,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
final boolean updateAnimationFlag = !mDragInProgress;
openFolderAnim.addListener(new AnimatorListenerAdapter() {
+ @SuppressLint("InlinedApi")
@Override
public void onAnimationEnd(Animator animation) {
mFolderName.animate().setDuration(FOLDER_NAME_ANIMATION_DURATION)
@@ -613,7 +629,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
@Override
- public void onDragStart(DragSource source, Object info, int dragAction) { }
+ public void onDragStart(DragSource source, ItemInfo info, int dragAction) { }
@Override
public void onDragEnd() {
@@ -636,12 +652,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
public void animateClosed() {
if (!(getParent() instanceof DragLayer)) return;
- PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0);
- PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 0.9f);
- PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 0.9f);
- final ObjectAnimator oa =
- LauncherAnimUtils.ofPropertyValuesHolder(this, alpha, scaleX, scaleY);
-
+ final ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(this, 0, 0.9f, 0.9f);
oa.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -689,7 +700,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
public boolean acceptDrop(DragObject d) {
- final ItemInfo item = (ItemInfo) d.dragInfo;
+ final ItemInfo item = d.dragInfo;
final int itemType = item.itemType;
return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) &&
@@ -906,6 +917,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, false, mLauncher);
}
+ if (!isFlingToDelete) {
+ // Fling to delete already exits spring loaded mode after the animation finishes.
+ mLauncher.exitSpringLoadedDragModeDelayed(successfulDrop,
+ Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
+ }
}
@Override
@@ -934,7 +950,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
@Override
public boolean supportsAppInfoDropTarget() {
- return false;
+ return true;
}
@Override
@@ -965,16 +981,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
LauncherModel.moveItemsInDatabase(mLauncher, items, mInfo.id, 0);
}
- public void addItemLocationsInDatabase() {
- ArrayList<View> list = getItemsInReadingOrder();
- for (int i = 0; i < list.size(); i++) {
- View v = list.get(i);
- ItemInfo info = (ItemInfo) v.getTag();
- LauncherModel.addItemToDatabase(mLauncher, info, mInfo.id, 0,
- info.cellX, info.cellY);
- }
- }
-
public void notifyDrop() {
if (mDragInProgress) {
mItemAddedBackToSelfViaIcon = true;
@@ -1111,11 +1117,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mItemsInvalidated = true;
}
- // TODO remove this once GSA code fix is submitted
- public ViewGroup getContent() {
- return (ViewGroup) mContent;
- }
-
public int getItemCount() {
return mContent.getItemCount();
}
@@ -1364,10 +1365,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
return mItemsInReadingOrder;
}
- public void getLocationInDragLayer(int[] loc) {
- mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
- }
-
public void onFocusChange(View v, boolean hasFocus) {
if (v == mFolderName && hasFocus) {
startEditingFolderName();
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index d7b55b3a2..7d1cb75d5 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -43,6 +43,8 @@ import android.widget.TextView;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.FolderInfo.FolderListener;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -129,7 +131,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
private void init() {
mLongPressHelper = new CheckLongPressHelper(this);
- mStylusEventHelper = new StylusEventHelper(this);
+ mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
}
@@ -171,8 +173,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
icon.setOnClickListener(launcher);
icon.mInfo = folderInfo;
icon.mLauncher = launcher;
- icon.setContentDescription(String.format(launcher.getString(R.string.folder_name_format),
- folderInfo.title));
+ icon.setContentDescription(launcher.getString(R.string.folder_name_format, folderInfo.title));
Folder folder = Folder.fromXml(launcher);
folder.setDragController(launcher.getDragController());
folder.setFolderIcon(icon);
@@ -330,8 +331,8 @@ public class FolderIcon extends FrameLayout implements FolderListener {
!mFolder.isFull() && item != mInfo && !mInfo.opened);
}
- public boolean acceptDrop(Object dragInfo) {
- final ItemInfo item = (ItemInfo) dragInfo;
+ public boolean acceptDrop(ItemInfo dragInfo) {
+ final ItemInfo item = dragInfo;
return !mFolder.isDestroyed() && willAcceptItem(item);
}
@@ -339,8 +340,8 @@ public class FolderIcon extends FrameLayout implements FolderListener {
mInfo.add(item);
}
- public void onDragEnter(Object dragInfo) {
- if (mFolder.isDestroyed() || !willAcceptItem((ItemInfo) dragInfo)) return;
+ public void onDragEnter(ItemInfo dragInfo) {
+ if (mFolder.isDestroyed() || !willAcceptItem(dragInfo)) return;
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
CellLayout layout = (CellLayout) getParent().getParent();
mFolderRingAnimator.setCell(lp.cellX, lp.cellY);
@@ -356,10 +357,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
// Workspace#onDropExternal.
mOpenAlarm.setAlarm(ON_OPEN_DELAY);
}
- mDragInfo = (ItemInfo) dragInfo;
- }
-
- public void onDragOver(Object dragInfo) {
+ mDragInfo = dragInfo;
}
OnAlarmListener mOnOpenListener = new OnAlarmListener() {
@@ -712,8 +710,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
public void onTitleChanged(CharSequence title) {
mFolderName.setText(title);
- setContentDescription(String.format(getContext().getString(R.string.folder_name_format),
- title));
+ setContentDescription(getContext().getString(R.string.folder_name_format, title));
}
@Override
@@ -723,7 +720,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
boolean result = super.onTouchEvent(event);
// Check for a stylus button press, if it occurs cancel any long press checks.
- if (mStylusEventHelper.checkAndPerformStylusEvent(event)) {
+ if (mStylusEventHelper.onMotionEvent(event)) {
mLongPressHelper.cancelLongPress();
return true;
}
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index aea21c95b..6e0dcd421 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -22,7 +22,6 @@ import android.content.Context;
import com.android.launcher3.compat.UserHandleCompat;
import java.util.ArrayList;
-import java.util.Arrays;
/**
* Represents a folder containing shortcuts or apps.
@@ -140,7 +139,7 @@ public class FolderInfo extends ItemInfo {
return "FolderInfo(id=" + this.id + " type=" + this.itemType
+ " container=" + this.container + " screen=" + screenId
+ " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
- + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) + ")";
+ + " spanY=" + spanY + ")";
}
public boolean hasOption(int optionFlag) {
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index d503d2cf5..796f56466 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -30,6 +30,7 @@ import android.view.animation.OvershootInterpolator;
import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener;
import com.android.launcher3.PageIndicator.PageMarkerResources;
import com.android.launcher3.Workspace.ItemOperator;
+import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -226,12 +227,6 @@ public class FolderPagedView extends PagedView {
return (CellLayout) getChildAt(index);
}
- public void removeCellLayoutView(View view) {
- for (int i = getChildCount() - 1; i >= 0; i --) {
- getPageAt(i).removeView(view);
- }
- }
-
public CellLayout getCurrentCellLayout() {
return getPageAt(getNextPage());
}
@@ -447,8 +442,7 @@ public class FolderPagedView extends PagedView {
}
public String getAccessibilityDescription() {
- return String.format(getContext().getString(R.string.folder_opened),
- mGridCountX, mGridCountY);
+ return getContext().getString(R.string.folder_opened, mGridCountX, mGridCountY);
}
/**
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 2675d2735..f271601ee 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -106,7 +106,6 @@ public class IconCache {
private final BitmapFactory.Options mLowResOptions;
private String mSystemState;
- private Bitmap mLowResBitmap;
private Canvas mLowResCanvas;
private Paint mLowResPaint;
@@ -117,6 +116,8 @@ public class IconCache {
mLauncherApps = LauncherAppsCompat.getInstance(mContext);
mIconDpi = inv.fillResIconDpi;
mIconDb = new IconDB(context);
+ mLowResCanvas = new Canvas();
+ mLowResPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
@@ -231,7 +232,7 @@ public class IconCache {
long userSerial = mUserManager.getSerialNumberForUser(user);
mIconDb.getWritableDatabase().delete(IconDB.TABLE_NAME,
IconDB.COLUMN_COMPONENT + " LIKE ? AND " + IconDB.COLUMN_USER + " = ?",
- new String[] {packageName + "/%", Long.toString(userSerial)});
+ new String[]{packageName + "/%", Long.toString(userSerial)});
}
public void updateDbIcons(Set<String> ignorePackagesForMainUser) {
@@ -379,7 +380,8 @@ public class IconCache {
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser());
mCache.put(new ComponentKey(app.getComponentName(), app.getUser()), entry);
- return newContentValues(entry.icon, entry.title.toString(), mActivityBgColor);
+ Bitmap lowResIcon = generateLowResIcon(entry.icon, mActivityBgColor);
+ return newContentValues(entry.icon, lowResIcon, entry.title.toString());
}
/**
@@ -617,16 +619,21 @@ public class IconCache {
if (appInfo == null) {
throw new NameNotFoundException("ApplicationInfo is null");
}
- entry.icon = Utilities.createBadgedIconBitmap(
+
+ // Load the full res icon for the application, but if useLowResIcon is set, then
+ // only keep the low resolution icon instead of the larger full-sized icon
+ Bitmap icon = Utilities.createBadgedIconBitmap(
appInfo.loadIcon(mPackageManager), user, mContext);
+ Bitmap lowResIcon = generateLowResIcon(icon, mPackageBgColor);
entry.title = appInfo.loadLabel(mPackageManager);
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
- entry.isLowResIcon = false;
+ entry.icon = useLowResIcon ? lowResIcon : icon;
+ entry.isLowResIcon = useLowResIcon;
// Add the icon in the DB here, since these do not get written during
// package updates.
ContentValues values =
- newContentValues(entry.icon, entry.title.toString(), mPackageBgColor);
+ newContentValues(icon, lowResIcon, entry.title.toString());
addIconToDB(values, cacheKey.componentName, info,
mUserManager.getSerialNumberForUser(user));
@@ -666,9 +673,9 @@ public class IconCache {
// pass
}
- ContentValues values = newContentValues(
- Bitmap.createScaledBitmap(icon, idp.iconBitmapSize, idp.iconBitmapSize, true),
- label, Color.TRANSPARENT);
+ icon = Bitmap.createScaledBitmap(icon, idp.iconBitmapSize, idp.iconBitmapSize, true);
+ Bitmap lowResIcon = generateLowResIcon(icon, Color.TRANSPARENT);
+ ContentValues values = newContentValues(icon, lowResIcon, label);
values.put(IconDB.COLUMN_COMPONENT, componentName.flattenToString());
values.put(IconDB.COLUMN_USER, userSerial);
mIconDb.getWritableDatabase().insertWithOnConflict(IconDB.TABLE_NAME, null, values,
@@ -834,34 +841,37 @@ public class IconCache {
}
}
- private ContentValues newContentValues(Bitmap icon, String label, int lowResBackgroundColor) {
+ private ContentValues newContentValues(Bitmap icon, Bitmap lowResIcon, String label) {
ContentValues values = new ContentValues();
values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(icon));
+ values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap(lowResIcon));
values.put(IconDB.COLUMN_LABEL, label);
values.put(IconDB.COLUMN_SYSTEM_STATE, mSystemState);
+ return values;
+ }
+
+ /**
+ * Generates a new low-res icon given a high-res icon.
+ */
+ private Bitmap generateLowResIcon(Bitmap icon, int lowResBackgroundColor) {
if (lowResBackgroundColor == Color.TRANSPARENT) {
- values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap(
- Bitmap.createScaledBitmap(icon,
- icon.getWidth() / LOW_RES_SCALE_FACTOR,
- icon.getHeight() / LOW_RES_SCALE_FACTOR, true)));
+ return Bitmap.createScaledBitmap(icon,
+ icon.getWidth() / LOW_RES_SCALE_FACTOR,
+ icon.getHeight() / LOW_RES_SCALE_FACTOR, true);
} else {
+ Bitmap lowResIcon = Bitmap.createBitmap(icon.getWidth() / LOW_RES_SCALE_FACTOR,
+ icon.getHeight() / LOW_RES_SCALE_FACTOR, Bitmap.Config.RGB_565);
synchronized (this) {
- if (mLowResBitmap == null) {
- mLowResBitmap = Bitmap.createBitmap(icon.getWidth() / LOW_RES_SCALE_FACTOR,
- icon.getHeight() / LOW_RES_SCALE_FACTOR, Bitmap.Config.RGB_565);
- mLowResCanvas = new Canvas(mLowResBitmap);
- mLowResPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
- }
mLowResCanvas.drawColor(lowResBackgroundColor);
mLowResCanvas.drawBitmap(icon, new Rect(0, 0, icon.getWidth(), icon.getHeight()),
- new Rect(0, 0, mLowResBitmap.getWidth(), mLowResBitmap.getHeight()),
+ new Rect(0, 0, lowResIcon.getWidth(), lowResIcon.getHeight()),
mLowResPaint);
- values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap(mLowResBitmap));
+ mLowResCanvas.setBitmap(null);
}
+ return lowResIcon;
}
- return values;
}
private static Bitmap loadIconNoResize(Cursor c, int iconIndex, BitmapFactory.Options options) {
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index d93cdcc1b..d444640e6 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -20,9 +20,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.util.AttributeSet;
-import com.android.launcher3.compat.UserHandleCompat;
-
-public class InfoDropTarget extends ButtonDropTarget {
+public class InfoDropTarget extends UninstallDropTarget {
public InfoDropTarget(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -41,7 +39,10 @@ public class InfoDropTarget extends ButtonDropTarget {
setDrawable(R.drawable.ic_info_launcher);
}
- public static void startDetailsActivityForInfo(Object info, Launcher launcher) {
+ /**
+ * @return Whether the activity was started.
+ */
+ public static boolean startDetailsActivityForInfo(ItemInfo info, Launcher launcher) {
ComponentName componentName = null;
if (info instanceof AppInfo) {
componentName = ((AppInfo) info).componentName;
@@ -49,30 +50,28 @@ public class InfoDropTarget extends ButtonDropTarget {
componentName = ((ShortcutInfo) info).intent.getComponent();
} else if (info instanceof PendingAddItemInfo) {
componentName = ((PendingAddItemInfo) info).componentName;
+ } else if (info instanceof LauncherAppWidgetInfo) {
+ componentName = ((LauncherAppWidgetInfo) info).providerName;
}
- final UserHandleCompat user;
- if (info instanceof ItemInfo) {
- user = ((ItemInfo) info).user;
- } else {
- user = UserHandleCompat.myUserHandle();
- }
-
if (componentName != null) {
- launcher.startApplicationDetailsActivity(componentName, user);
+ launcher.startApplicationDetailsActivity(componentName, info.user);
+ return true;
}
+ return false;
}
@Override
- protected boolean supportsDrop(DragSource source, Object info) {
- return source.supportsAppInfoDropTarget() && supportsDrop(getContext(), info);
+ protected boolean startActivityWithUninstallAffordance(DragObject d) {
+ return startDetailsActivityForInfo(d.dragInfo, mLauncher);
}
- public static boolean supportsDrop(Context context, Object info) {
- return info instanceof AppInfo || info instanceof PendingAddItemInfo;
+ @Override
+ protected boolean supportsDrop(DragSource source, ItemInfo info) {
+ return source.supportsAppInfoDropTarget() && supportsDrop(getContext(), info);
}
- @Override
- void completeDrop(DragObject d) {
- startDetailsActivityForInfo(d.dragInfo, mLauncher);
+ public static boolean supportsDrop(Context context, ItemInfo info) {
+ return info instanceof AppInfo || info instanceof ShortcutInfo
+ || info instanceof PendingAddItemInfo || info instanceof LauncherAppWidgetInfo;
}
}
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index f7e0ea488..aa5a18d3c 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -24,8 +24,6 @@ import android.graphics.Bitmap;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
-import java.util.Arrays;
-
/**
* Represents an item in the launcher.
*/
@@ -35,14 +33,14 @@ public class ItemInfo {
* Intent extra to store the profile. Format: UserHandle
*/
static final String EXTRA_PROFILE = "profile";
-
+
public static final int NO_ID = -1;
-
+
/**
* The id in the settings database for this item
*/
public long id = NO_ID;
-
+
/**
* One of {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
* {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT},
@@ -50,20 +48,20 @@ public class ItemInfo {
* {@link LauncherSettings.Favorites#ITEM_TYPE_APPWIDGET}.
*/
public int itemType;
-
+
/**
- * The id of the container that holds this item. For the desktop, this will be
+ * The id of the container that holds this item. For the desktop, this will be
* {@link LauncherSettings.Favorites#CONTAINER_DESKTOP}. For the all applications folder it
* will be {@link #NO_ID} (since it is not stored in the settings DB). For user folders
* it will be the id of the folder.
*/
public long container = NO_ID;
-
+
/**
* Iindicates the screen in which the shortcut appears.
*/
public long screenId = -1;
-
+
/**
* Indicates the X position of the associated cell.
*/
@@ -100,11 +98,6 @@ public class ItemInfo {
public int rank = 0;
/**
- * Indicates that this item needs to be updated in the db
- */
- public boolean requiresDbUpdate = false;
-
- /**
* Title of the item
*/
public CharSequence title;
@@ -114,11 +107,6 @@ public class ItemInfo {
*/
public CharSequence contentDescription;
- /**
- * The position of the item in a drag-and-drop operation.
- */
- public int[] dropPos = null;
-
public UserHandleCompat user;
public ItemInfo() {
@@ -146,18 +134,11 @@ public class ItemInfo {
}
public Intent getIntent() {
- throw new RuntimeException("Unexpected Intent");
+ return null;
}
- /**
- * Write the fields of this item to the DB
- *
- * @param context A context object to use for getting UserManagerCompat
- * @param values
- */
-
- void onAddToDatabase(Context context, ContentValues values) {
- values.put(LauncherSettings.BaseLauncherColumns.ITEM_TYPE, itemType);
+ public void writeToValues(ContentValues values) {
+ values.put(LauncherSettings.Favorites.ITEM_TYPE, itemType);
values.put(LauncherSettings.Favorites.CONTAINER, container);
values.put(LauncherSettings.Favorites.SCREEN, screenId);
values.put(LauncherSettings.Favorites.CELLX, cellX);
@@ -165,6 +146,27 @@ public class ItemInfo {
values.put(LauncherSettings.Favorites.SPANX, spanX);
values.put(LauncherSettings.Favorites.SPANY, spanY);
values.put(LauncherSettings.Favorites.RANK, rank);
+ }
+
+ public void readFromValues(ContentValues values) {
+ itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE);
+ container = values.getAsLong(LauncherSettings.Favorites.CONTAINER);
+ screenId = values.getAsLong(LauncherSettings.Favorites.SCREEN);
+ cellX = values.getAsInteger(LauncherSettings.Favorites.CELLX);
+ cellY = values.getAsInteger(LauncherSettings.Favorites.CELLY);
+ spanX = values.getAsInteger(LauncherSettings.Favorites.SPANX);
+ spanY = values.getAsInteger(LauncherSettings.Favorites.SPANY);
+ rank = values.getAsInteger(LauncherSettings.Favorites.RANK);
+ }
+
+ /**
+ * Write the fields of this item to the DB
+ *
+ * @param context A context object to use for getting UserManagerCompat
+ * @param values
+ */
+ void onAddToDatabase(Context context, ContentValues values) {
+ writeToValues(values);
long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user);
values.put(LauncherSettings.Favorites.PROFILE_ID, serialNumber);
@@ -194,7 +196,6 @@ public class ItemInfo {
public String toString() {
return "Item(id=" + this.id + " type=" + this.itemType + " container=" + this.container
+ " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
- + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
- + " user=" + user + ")";
+ + " spanY=" + spanY + " user=" + user + ")";
}
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index a379accd3..687f57a2c 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -21,7 +21,6 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
@@ -37,6 +36,7 @@ import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
+import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -75,7 +75,6 @@ import android.util.Log;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.Surface;
@@ -102,11 +101,16 @@ import com.android.launcher3.compat.LauncherActivityInfoCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.TestingUtils;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetsContainerView;
@@ -130,14 +134,13 @@ import java.util.List;
public class Launcher extends Activity
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {
- static final String TAG = "Launcher";
+ public static final String TAG = "Launcher";
static final boolean LOGD = false;
static final boolean PROFILE_STARTUP = false;
static final boolean DEBUG_WIDGETS = false;
static final boolean DEBUG_STRICT_MODE = false;
static final boolean DEBUG_RESUME_TIME = false;
- static final boolean DEBUG_DUMP_LOG = false;
static final boolean ENABLE_DEBUG_INTENTS = false; // allow DebugIntents to run
@@ -163,8 +166,6 @@ public class Launcher extends Activity
*/
protected static final int REQUEST_LAST = 100;
- static final int SCREEN_COUNT = 5;
-
// To turn on these properties, type
// adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS]
static final String DUMP_STATE_PROPERTY = "launcher_dump_state";
@@ -177,18 +178,8 @@ public class Launcher extends Activity
private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
// Type: int
private static final String RUNTIME_STATE = "launcher.state";
- // Type: int
- private static final String RUNTIME_STATE_PENDING_ADD_CONTAINER = "launcher.add_container";
- // Type: int
- private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen";
- // Type: int
- private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cell_x";
- // Type: int
- private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cell_y";
- // Type: int
- private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_span_x";
- // Type: int
- private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_span_y";
+ // Type: Content Values / parcelable
+ private static final String RUNTIME_STATE_PENDING_ADD_ITEM = "launcher.add_item";
// Type: parcelable
private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_INFO = "launcher.add_widget_info";
// Type: parcelable
@@ -207,7 +198,8 @@ public class Launcher extends Activity
public static final String USER_HAS_MIGRATED = "launcher.user_migrated_from_old_data";
/** The different states that Launcher can be in. */
- enum State { NONE, WORKSPACE, APPS, APPS_SPRING_LOADED, WIDGETS, WIDGETS_SPRING_LOADED }
+ enum State { NONE, WORKSPACE, WORKSPACE_SPRING_LOADED, APPS, APPS_SPRING_LOADED,
+ WIDGETS, WIDGETS_SPRING_LOADED }
@Thunk State mState = State.WORKSPACE;
@Thunk LauncherStateTransitionAnimation mStateTransitionAnimation;
@@ -231,8 +223,6 @@ public class Launcher extends Activity
private final BroadcastReceiver mCloseSystemDialogsReceiver
= new CloseSystemDialogsIntentReceiver();
- private LayoutInflater mInflater;
-
@Thunk Workspace mWorkspace;
private View mLauncherView;
private View mPageIndicators;
@@ -244,7 +234,7 @@ public class Launcher extends Activity
private AppWidgetManagerCompat mAppWidgetManager;
private LauncherAppWidgetHost mAppWidgetHost;
- @Thunk ItemInfo mPendingAddInfo = new ItemInfo();
+ @Thunk final ItemInfo mPendingAddInfo = new ItemInfo();
private LauncherAppWidgetProviderInfo mPendingAddWidgetInfo;
private int mPendingAddWidgetId = -1;
@@ -257,6 +247,7 @@ public class Launcher extends Activity
private View mWidgetsButton;
private SearchDropTargetBar mSearchDropTargetBar;
+ private AppInfoDropTargetBar mAppInfoDropTargetBar;
// Main container view for the all apps screen.
@Thunk AllAppsContainerView mAppsView;
@@ -265,7 +256,6 @@ public class Launcher extends Activity
@Thunk WidgetsContainerView mWidgetsView;
@Thunk WidgetsModel mWidgetsModel;
- private boolean mAutoAdvanceRunning = false;
private AppWidgetHostView mQsb;
private Bundle mSavedState;
@@ -285,8 +275,7 @@ public class Launcher extends Activity
private ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<Runnable>();
private ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<Runnable>();
-
- private Bundle mSavedInstanceState;
+ private ViewOnDrawExecutor mPendingExecutor;
private LauncherModel mModel;
private IconCache mIconCache;
@@ -303,28 +292,27 @@ public class Launcher extends Activity
// Related to the auto-advancing of widgets
private final int ADVANCE_MSG = 1;
- private final int mAdvanceInterval = 20000;
- private final int mAdvanceStagger = 250;
+ private static final int ADVANCE_INTERVAL = 20000;
+ private static final int ADVANCE_STAGGER = 250;
+
+ private boolean mAutoAdvanceRunning = false;
private long mAutoAdvanceSentTime;
private long mAutoAdvanceTimeLeft = -1;
- @Thunk HashMap<View, AppWidgetProviderInfo> mWidgetsToAdvance =
- new HashMap<View, AppWidgetProviderInfo>();
+ @Thunk HashMap<View, AppWidgetProviderInfo> mWidgetsToAdvance = new HashMap<>();
// Determines how long to wait after a rotation before restoring the screen orientation to
// match the sensor state.
- private final int mRestoreScreenOrientationDelay = 500;
+ private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
@Thunk Drawable mWorkspaceBackgroundDrawable;
private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>();
private static final boolean DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE = false;
- static final ArrayList<String> sDumpLogs = new ArrayList<String>();
- static Date sDateStamp = new Date();
- static DateFormat sDateFormat =
+ private static final ArrayList<String> sDumpLogs = new ArrayList<String>();
+ private static final Date sDateStamp = new Date();
+ private static final DateFormat sDateFormat =
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
- static long sRunStart = System.currentTimeMillis();
- static final String CORRUPTION_EMAIL_SENT_KEY = "corruptionEmailSent";
// We only want to get the SharedPreferences once since it does an FS stat each time we get
// it from the context.
@@ -431,7 +419,6 @@ public class Launcher extends Activity
mIconCache = app.getIconCache();
mDragController = new DragController(this);
- mInflater = getLayoutInflater();
mStateTransitionAnimation = new LauncherStateTransitionAnimation(this);
mStats = new Stats(this);
@@ -490,7 +477,7 @@ public class Launcher extends Activity
// In case we are on a device with locked rotation, we should look at preferences to check
// if the user has specifically allowed rotation.
if (!mRotationEnabled) {
- mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext(), false);
+ mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext());
}
// On large interfaces, or on devices that a user has specifically enabled screen rotation,
@@ -638,10 +625,6 @@ public class Launcher extends Activity
return mStats;
}
- public LayoutInflater getInflater() {
- return mInflater;
- }
-
public boolean isDraggingEnabled() {
// We prevent dragging when we are loading the workspace as it is possible to pick up a view
// that is subsequently removed from the workspace in startBinding().
@@ -722,7 +705,7 @@ public class Launcher extends Activity
} else if (requestCode == REQUEST_PICK_WALLPAPER) {
if (resultCode == RESULT_OK && mWorkspace.isInOverviewMode()) {
// User could have free-scrolled between pages before picking a wallpaper; make sure
- // we move to the closest one now to avoid visual jump.
+ // we move to the closest one now.
mWorkspace.setCurrentPage(mWorkspace.getPageNearestToCenterOfScreen());
showWorkspace(false);
}
@@ -808,24 +791,22 @@ public class Launcher extends Activity
return;
}
- // The pattern used here is that a user PICKs a specific application,
- // which, depending on the target, might need to CREATE the actual target.
-
- // For example, the user would PICK_SHORTCUT for "Music playlist", and we
- // launch over to the Music app to actually CREATE_SHORTCUT.
- if (resultCode == RESULT_OK && mPendingAddInfo.container != ItemInfo.NO_ID) {
- final PendingAddArguments args = preparePendingAddArgs(requestCode, data, -1,
- mPendingAddInfo);
- if (isWorkspaceLocked()) {
- sPendingAddItem = args;
- } else {
- completeAdd(args);
+ if (requestCode == REQUEST_CREATE_SHORTCUT) {
+ // Handle custom shortcuts created using ACTION_CREATE_SHORTCUT.
+ if (resultCode == RESULT_OK && mPendingAddInfo.container != ItemInfo.NO_ID) {
+ final PendingAddArguments args = preparePendingAddArgs(requestCode, data, -1,
+ mPendingAddInfo);
+ if (isWorkspaceLocked()) {
+ sPendingAddItem = args;
+ } else {
+ completeAdd(args);
+ mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
+ ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
+ }
+ } else if (resultCode == RESULT_CANCELED) {
mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
}
- } else if (resultCode == RESULT_CANCELED) {
- mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
- ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
}
mDragLayer.clearAnimatedView();
@@ -990,12 +971,6 @@ public class Launcher extends Activity
mPaused = false;
if (mRestoring || mOnResumeNeedsLoad) {
setWorkspaceLoading(true);
-
- // If we're starting binding all over again, clear any bind calls we'd postponed in
- // the past (see waitUntilResume) -- we don't need them since we're starting binding
- // from scratch again
- mBindOnResumeCallbacks.clear();
-
mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
mRestoring = false;
mOnResumeNeedsLoad = false;
@@ -1343,16 +1318,9 @@ public class Launcher extends Activity
mWorkspace.setRestorePage(currentScreen);
}
- final long pendingAddContainer = savedState.getLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, -1);
- final long pendingAddScreen = savedState.getLong(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
-
- if (pendingAddContainer != ItemInfo.NO_ID && pendingAddScreen > -1) {
- mPendingAddInfo.container = pendingAddContainer;
- mPendingAddInfo.screenId = pendingAddScreen;
- mPendingAddInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
- mPendingAddInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
- mPendingAddInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
- mPendingAddInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
+ ContentValues itemValues = savedState.getParcelable(RUNTIME_STATE_PENDING_ADD_ITEM);
+ if (itemValues != null) {
+ mPendingAddInfo.readFromValues(itemValues);
AppWidgetProviderInfo info = savedState.getParcelable(
RUNTIME_STATE_PENDING_ADD_WIDGET_INFO);
mPendingAddWidgetInfo = info == null ?
@@ -1399,9 +1367,12 @@ public class Launcher extends Activity
mWorkspace.setup(dragController);
dragController.addDragListener(mWorkspace);
- // Get the search/delete bar
+ // Get the search/delete/uninstall bar
mSearchDropTargetBar = (SearchDropTargetBar)
mDragLayer.findViewById(R.id.search_drop_target_bar);
+ // Get the app info bar
+ mAppInfoDropTargetBar = (AppInfoDropTargetBar)
+ mDragLayer.findViewById(R.id.app_info_drop_target_bar);
// Setup Apps and Widgets
mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
@@ -1414,13 +1385,15 @@ public class Launcher extends Activity
// Setup the drag controller (drop targets have to be added in reverse order in priority)
dragController.setDragScoller(mWorkspace);
- dragController.setScrollView(mDragLayer);
dragController.setMoveTarget(mWorkspace);
dragController.addDropTarget(mWorkspace);
if (mSearchDropTargetBar != null) {
mSearchDropTargetBar.setup(this, dragController);
mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar());
}
+ if (mAppInfoDropTargetBar != null) {
+ mAppInfoDropTargetBar.setup(this, dragController);
+ }
if (TestingUtils.MEMORY_DUMP_ENABLED) {
TestingUtils.addWeightWatcher(this);
@@ -1518,7 +1491,7 @@ public class Launcher extends Activity
* @return A View inflated from layoutResId.
*/
public View createShortcut(ViewGroup parent, ShortcutInfo info) {
- BubbleTextView favorite = (BubbleTextView) mInflater.inflate(R.layout.app_icon,
+ BubbleTextView favorite = (BubbleTextView) getLayoutInflater().inflate(R.layout.app_icon,
parent, false);
favorite.applyFromShortcutInfo(info, mIconCache);
favorite.setCompoundDrawablePadding(mDeviceProfile.iconDrawablePaddingPx);
@@ -1535,7 +1508,6 @@ public class Launcher extends Activity
private void completeAddShortcut(Intent data, long container, long screenId, int cellX,
int cellY) {
int[] cellXY = mTmpAddItemCellCoordinates;
- int[] touchXY = mPendingAddInfo.dropPos;
CellLayout layout = getCellLayout(container, screenId);
ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(this, data);
@@ -1562,10 +1534,6 @@ public class Launcher extends Activity
true)) {
return;
}
- } else if (touchXY != null) {
- // when dragging and dropping, just find the closest free spot
- int[] result = layout.findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, cellXY);
- foundCellSpan = (result != null);
} else {
foundCellSpan = layout.findCellForSpan(cellXY, 1, 1);
}
@@ -1761,11 +1729,11 @@ public class Launcher extends Activity
if (autoAdvanceRunning != mAutoAdvanceRunning) {
mAutoAdvanceRunning = autoAdvanceRunning;
if (autoAdvanceRunning) {
- long delay = mAutoAdvanceTimeLeft == -1 ? mAdvanceInterval : mAutoAdvanceTimeLeft;
+ long delay = mAutoAdvanceTimeLeft == -1 ? ADVANCE_INTERVAL : mAutoAdvanceTimeLeft;
sendAdvanceMessage(delay);
} else {
if (!mWidgetsToAdvance.isEmpty()) {
- mAutoAdvanceTimeLeft = Math.max(0, mAdvanceInterval -
+ mAutoAdvanceTimeLeft = Math.max(0, ADVANCE_INTERVAL -
(System.currentTimeMillis() - mAutoAdvanceSentTime));
}
mHandler.removeMessages(ADVANCE_MSG);
@@ -1782,7 +1750,7 @@ public class Launcher extends Activity
int i = 0;
for (View key: mWidgetsToAdvance.keySet()) {
final View v = key.findViewById(mWidgetsToAdvance.get(key).autoAdvanceViewId);
- final int delay = mAdvanceStagger * i;
+ final int delay = ADVANCE_STAGGER * i;
if (v instanceof Advanceable) {
mHandler.postDelayed(new Runnable() {
public void run() {
@@ -1792,7 +1760,7 @@ public class Launcher extends Activity
}
i++;
}
- sendAdvanceMessage(mAdvanceInterval);
+ sendAdvanceMessage(ADVANCE_INTERVAL);
}
return true;
}
@@ -1848,6 +1816,10 @@ public class Launcher extends Activity
return mSearchDropTargetBar;
}
+ public AppInfoDropTargetBar getAppInfoDropTargetBar() {
+ return mAppInfoDropTargetBar;
+ }
+
public LauncherAppWidgetHost getAppWidgetHost() {
return mAppWidgetHost;
}
@@ -1984,12 +1956,9 @@ public class Launcher extends Activity
if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screenId > -1 &&
mWaitingForResult) {
- outState.putLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, mPendingAddInfo.container);
- outState.putLong(RUNTIME_STATE_PENDING_ADD_SCREEN, mPendingAddInfo.screenId);
- outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, mPendingAddInfo.cellX);
- outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, mPendingAddInfo.cellY);
- outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, mPendingAddInfo.spanX);
- outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, mPendingAddInfo.spanY);
+ ContentValues itemValues = new ContentValues();
+ mPendingAddInfo.writeToValues(itemValues);
+ outState.putParcelable(RUNTIME_STATE_PENDING_ADD_ITEM, itemValues);
outState.putParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO, mPendingAddWidgetInfo);
outState.putInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID, mPendingAddWidgetId);
}
@@ -2230,7 +2199,6 @@ public class Launcher extends Activity
mPendingAddInfo.cellX = mPendingAddInfo.cellY = -1;
mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1;
mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = 1;
- mPendingAddInfo.dropPos = null;
}
void addAppWidgetFromDropImpl(final int appWidgetId, final ItemInfo info, final
@@ -2305,7 +2273,6 @@ public class Launcher extends Activity
resetAddInfo();
mPendingAddInfo.container = container;
mPendingAddInfo.screenId = screenId;
- mPendingAddInfo.dropPos = null;
if (cell != null) {
mPendingAddInfo.cellX = cell[0];
@@ -2329,7 +2296,6 @@ public class Launcher extends Activity
resetAddInfo();
mPendingAddInfo.container = info.container = container;
mPendingAddInfo.screenId = info.screenId = screenId;
- mPendingAddInfo.dropPos = null;
mPendingAddInfo.minSpanX = info.minSpanX;
mPendingAddInfo.minSpanY = info.minSpanY;
@@ -2557,8 +2523,10 @@ public class Launcher extends Activity
if (v instanceof CellLayout) {
if (mWorkspace.isInOverviewMode()) {
- showWorkspace(mWorkspace.indexOfChild(v), true);
+ mWorkspace.snapToPageFromOverView(mWorkspace.indexOfChild(v));
+ showWorkspace(true);
}
+ return;
}
Object tag = v.getTag();
@@ -3090,10 +3058,6 @@ public class Launcher extends Activity
private void growAndFadeOutFolderIcon(FolderIcon fi) {
if (fi == null) return;
- PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0);
- PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.5f);
- PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.5f);
-
FolderInfo info = (FolderInfo) fi.getTag();
if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
CellLayout cl = (CellLayout) fi.getParent().getParent();
@@ -3105,8 +3069,8 @@ public class Launcher extends Activity
copyFolderIconToImage(fi);
fi.setVisibility(View.INVISIBLE);
- ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
- scaleX, scaleY);
+ ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(
+ mFolderIconImageView, 0, 1.5f, 1.5f);
if (Utilities.ATLEAST_LOLLIPOP) {
oa.setInterpolator(new LogDecelerateInterpolator(100, 0));
}
@@ -3120,17 +3084,12 @@ public class Launcher extends Activity
private void shrinkAndFadeInFolderIcon(final FolderIcon fi, boolean animate) {
if (fi == null) return;
- PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1.0f);
- PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f);
- PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f);
-
final CellLayout cl = (CellLayout) fi.getParent().getParent();
// We remove and re-draw the FolderIcon in-case it has changed
mDragLayer.removeView(mFolderIconImageView);
copyFolderIconToImage(fi);
- ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
- scaleX, scaleY);
+ ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(mFolderIconImageView, 1, 1, 1);
oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
oa.addListener(new AnimatorListenerAdapter() {
@Override
@@ -3360,38 +3319,17 @@ public class Launcher extends Activity
}
}
- /**
- * @return whether or not the Launcher state changed.
- */
public boolean showWorkspace(boolean animated) {
- return showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, null);
+ return showWorkspace(animated, null);
}
- /**
- * @return whether or not the Launcher state changed.
- */
public boolean showWorkspace(boolean animated, Runnable onCompleteRunnable) {
- return showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated,
- onCompleteRunnable);
- }
-
- /**
- * @return whether or not the Launcher state changed.
- */
- protected boolean showWorkspace(int snapToPage, boolean animated) {
- return showWorkspace(snapToPage, animated, null);
- }
-
- /**
- * @return whether or not the Launcher state changed.
- */
- boolean showWorkspace(int snapToPage, boolean animated, Runnable onCompleteRunnable) {
boolean changed = mState != State.WORKSPACE ||
mWorkspace.getState() != Workspace.State.NORMAL;
if (changed) {
mWorkspace.setVisibility(View.VISIBLE);
mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
- Workspace.State.NORMAL, snapToPage, animated, onCompleteRunnable);
+ Workspace.State.NORMAL, animated, onCompleteRunnable);
// Set focus to the AppsCustomize button
if (mAllAppsButton != null) {
@@ -3440,9 +3378,7 @@ public class Launcher extends Activity
}
mWorkspace.setVisibility(View.VISIBLE);
mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
- Workspace.State.OVERVIEW,
- WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated,
- postAnimRunnable);
+ Workspace.State.OVERVIEW, animated, postAnimRunnable);
mState = State.WORKSPACE;
}
@@ -3519,31 +3455,48 @@ public class Launcher extends Activity
* Updates the workspace and interaction state on state change, and return the animation to this
* new state.
*/
- public Animator startWorkspaceStateChangeAnimation(Workspace.State toState, int toPage,
+ public Animator startWorkspaceStateChangeAnimation(Workspace.State toState,
boolean animated, HashMap<View, Integer> layerViews) {
Workspace.State fromState = mWorkspace.getState();
- Animator anim = mWorkspace.setStateWithAnimation(toState, toPage, animated, layerViews);
+ Animator anim = mWorkspace.setStateWithAnimation(toState, animated, layerViews);
updateInteraction(fromState, toState);
return anim;
}
+ public void onLauncherClingShown() {
+ // When a launcher cling appears, it should cover the underlying layers, so their focus
+ // should be blocked.
+ if (mDragLayer.getDescendantFocusability() != ViewGroup.FOCUS_BLOCK_DESCENDANTS) {
+ mDragLayer.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+ }
+ }
+
+ public void onLauncherClingDismissed() {
+ mDragLayer.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+ }
+
public void enterSpringLoadedDragMode() {
if (LOGD) Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s", mState.name()));
- if (mState == State.WORKSPACE || mState == State.APPS_SPRING_LOADED ||
- mState == State.WIDGETS_SPRING_LOADED) {
+ if (isStateSpringLoaded()) {
return;
}
mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
- Workspace.State.SPRING_LOADED,
- WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true /* animated */,
+ Workspace.State.SPRING_LOADED, true /* animated */,
null /* onCompleteRunnable */);
- mState = isAppsViewVisible() ? State.APPS_SPRING_LOADED : State.WIDGETS_SPRING_LOADED;
+
+ if (isAppsViewVisible()) {
+ mState = State.APPS_SPRING_LOADED;
+ } else if (isWidgetsViewVisible()) {
+ mState = State.WIDGETS_SPRING_LOADED;
+ } else {
+ mState = State.WORKSPACE_SPRING_LOADED;
+ }
}
public void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay,
final Runnable onCompleteRunnable) {
- if (mState != State.APPS_SPRING_LOADED && mState != State.WIDGETS_SPRING_LOADED) return;
+ if (!isStateSpringLoaded()) return;
mHandler.postDelayed(new Runnable() {
@Override
@@ -3563,12 +3516,19 @@ public class Launcher extends Activity
}, delay);
}
+ private boolean isStateSpringLoaded() {
+ return mState == State.WORKSPACE_SPRING_LOADED || mState == State.APPS_SPRING_LOADED
+ || mState == State.WIDGETS_SPRING_LOADED;
+ }
+
void exitSpringLoadedDragMode() {
if (mState == State.APPS_SPRING_LOADED) {
showAppsView(true /* animated */, false /* resetListToTop */,
false /* updatePredictedApps */, false /* focusSearchBar */);
} else if (mState == State.WIDGETS_SPRING_LOADED) {
showWidgetsView(true, false);
+ } else if (mState == State.WORKSPACE_SPRING_LOADED) {
+ showWorkspace(true);
}
}
@@ -3789,7 +3749,20 @@ public class Launcher extends Activity
if (mWorkspace != null) {
return mWorkspace.getCurrentPage();
} else {
- return SCREEN_COUNT / 2;
+ return 0;
+ }
+ }
+
+ /**
+ * Clear any pending bind callbacks. This is called when is loader is planning to
+ * perform a full rebind from scratch.
+ */
+ @Override
+ public void clearPendingBinds() {
+ mBindOnResumeCallbacks.clear();
+ if (mPendingExecutor != null) {
+ mPendingExecutor.markCompleted();
+ mPendingExecutor = null;
}
}
@@ -3801,11 +3774,6 @@ public class Launcher extends Activity
public void startBinding() {
setWorkspaceLoading(true);
- // If we're starting binding all over again, clear any bind calls we'd postponed in
- // the past (see waitUntilResume) -- we don't need them since we're starting binding
- // from scratch again
- mBindOnResumeCallbacks.clear();
-
// Clear the workspace because it's going to be rebound
mWorkspace.clearDropTargets();
mWorkspace.removeAllWorkspaceScreens();
@@ -3927,7 +3895,7 @@ public class Launcher extends Activity
Object tag = v.getTag();
String desc = "Collision while binding workspace item: " + item
+ ". Collides with " + tag;
- if (LauncherAppState.isDogfoodBuild()) {
+ if (ProviderConfig.IS_DOGFOOD_BUILD) {
throw (new RuntimeException(desc));
} else {
Log.d(TAG, desc);
@@ -4148,6 +4116,21 @@ public class Launcher extends Activity
mSynchronouslyBoundPages.add(page);
}
+ @Override
+ public void executeOnNextDraw(ViewOnDrawExecutor executor) {
+ if (mPendingExecutor != null) {
+ mPendingExecutor.markCompleted();
+ }
+ mPendingExecutor = executor;
+ executor.attachTo(this);
+ }
+
+ public void clearPendingExecutor(ViewOnDrawExecutor executor) {
+ if (mPendingExecutor == executor) {
+ mPendingExecutor = null;
+ }
+ }
+
/**
* Callback saying that there aren't any more items to bind.
*
@@ -4224,10 +4207,7 @@ public class Launcher extends Activity
}
private ValueAnimator createNewAppBounceAnimation(View v, int i) {
- ValueAnimator bounceAnim = LauncherAnimUtils.ofPropertyValuesHolder(v,
- PropertyValuesHolder.ofFloat("alpha", 1f),
- PropertyValuesHolder.ofFloat("scaleX", 1f),
- PropertyValuesHolder.ofFloat("scaleY", 1f));
+ ValueAnimator bounceAnim = LauncherAnimUtils.ofViewAlphaAndScale(v, 1, 1, 1);
bounceAnim.setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION);
bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY);
bounceAnim.setInterpolator(new OvershootInterpolator(BOUNCE_ANIMATION_TENSION));
@@ -4494,7 +4474,7 @@ public class Launcher extends Activity
public void run() {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
- }, mRestoreScreenOrientationDelay);
+ }, RESTORE_SCREEN_ORIENTATION_DELAY);
}
}
}
@@ -4762,7 +4742,6 @@ public class Launcher extends Activity
Log.d(TAG, "mWorkspaceLoading=" + mWorkspaceLoading);
Log.d(TAG, "mRestoring=" + mRestoring);
Log.d(TAG, "mWaitingForResult=" + mWaitingForResult);
- Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState);
Log.d(TAG, "sFolders.size=" + sFolders.size());
mModel.dumpState();
// TODO(hyunyoungs): add mWidgetsView.dumpState(); or mWidgetsModel.dumpState();
@@ -4773,51 +4752,47 @@ public class Launcher extends Activity
@Override
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
super.dump(prefix, fd, writer, args);
- synchronized (sDumpLogs) {
- writer.println(" ");
- writer.println("Debug logs: ");
- for (int i = 0; i < sDumpLogs.size(); i++) {
- writer.println(" " + sDumpLogs.get(i));
+ // Dump workspace
+ writer.println(prefix + "Workspace Items");
+ for (int i = mWorkspace.numCustomPages(); i < mWorkspace.getPageCount(); i++) {
+ writer.println(prefix + " Homescreen " + i);
+
+ ViewGroup layout = ((CellLayout) mWorkspace.getPageAt(i)).getShortcutsAndWidgets();
+ for (int j = 0; j < layout.getChildCount(); j++) {
+ Object tag = layout.getChildAt(j).getTag();
+ if (tag != null) {
+ writer.println(prefix + " " + tag.toString());
+ }
}
}
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.dump(prefix, fd, writer, args);
+
+ writer.println(prefix + " Hotseat");
+ ViewGroup layout = mHotseat.getLayout().getShortcutsAndWidgets();
+ for (int j = 0; j < layout.getChildCount(); j++) {
+ Object tag = layout.getChildAt(j).getTag();
+ if (tag != null) {
+ writer.println(prefix + " " + tag.toString());
+ }
}
- }
- public static void dumpDebugLogsToConsole() {
- if (DEBUG_DUMP_LOG) {
- synchronized (sDumpLogs) {
- Log.d(TAG, "");
- Log.d(TAG, "*********************");
- Log.d(TAG, "Launcher debug logs: ");
- for (int i = 0; i < sDumpLogs.size(); i++) {
- Log.d(TAG, " " + sDumpLogs.get(i));
- }
- Log.d(TAG, "*********************");
- Log.d(TAG, "");
+ synchronized (sDumpLogs) {
+ writer.println();
+ writer.println(prefix + "Debug logs");
+ for (String log : sDumpLogs) {
+ writer.println(prefix + " " + log);
}
}
- }
- public static void addDumpLog(String tag, String log, boolean debugLog) {
- addDumpLog(tag, log, null, debugLog);
+ if (mLauncherCallbacks != null) {
+ mLauncherCallbacks.dump(prefix, fd, writer, args);
+ }
}
- public static void addDumpLog(String tag, String log, Exception e, boolean debugLog) {
- if (debugLog) {
- if (e != null) {
- Log.d(tag, log, e);
- } else {
- Log.d(tag, log);
- }
- }
- if (DEBUG_DUMP_LOG) {
+ public static void addDumpLog(String tag, String log) {
+ Log.d(tag, log);
+ synchronized(sDumpLogs) {
sDateStamp.setTime(System.currentTimeMillis());
- synchronized (sDumpLogs) {
- sDumpLogs.add(sDateFormat.format(sDateStamp) + ": " + tag + ", " + log
- + (e == null ? "" : (", Exception: " + e)));
- }
+ sDumpLogs.add(sDateFormat.format(sDateStamp) + ": " + tag + ", " + log);
}
}
@@ -4828,53 +4803,6 @@ public class Launcher extends Activity
public static HashMap<String, CustomAppWidget> getCustomAppWidgets() {
return sCustomAppWidgets;
}
-
- public void dumpLogsToLocalData() {
- if (DEBUG_DUMP_LOG) {
- new AsyncTask<Void, Void, Void>() {
- public Void doInBackground(Void ... args) {
- boolean success = false;
- sDateStamp.setTime(sRunStart);
- String FILENAME = sDateStamp.getMonth() + "-"
- + sDateStamp.getDay() + "_"
- + sDateStamp.getHours() + "-"
- + sDateStamp.getMinutes() + "_"
- + sDateStamp.getSeconds() + ".txt";
-
- FileOutputStream fos = null;
- File outFile = null;
- try {
- outFile = new File(getFilesDir(), FILENAME);
- outFile.createNewFile();
- fos = new FileOutputStream(outFile);
- } catch (Exception e) {
- e.printStackTrace();
- }
- if (fos != null) {
- PrintWriter writer = new PrintWriter(fos);
-
- writer.println(" ");
- writer.println("Debug logs: ");
- synchronized (sDumpLogs) {
- for (int i = 0; i < sDumpLogs.size(); i++) {
- writer.println(" " + sDumpLogs.get(i));
- }
- }
- writer.close();
- }
- try {
- if (fos != null) {
- fos.close();
- success = true;
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- return null;
- }
- }.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
- }
- }
}
interface DebugIntents {
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index fa20f0360..01e73d4a1 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -21,13 +21,10 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
-import android.os.Build;
+import android.util.Property;
import android.view.View;
import android.view.ViewTreeObserver;
-import com.android.launcher3.util.UiThreadCircularReveal;
-
import java.util.HashSet;
import java.util.WeakHashMap;
@@ -102,42 +99,32 @@ public class LauncherAnimUtils {
return anim;
}
- public static ObjectAnimator ofFloat(View target, String propertyName, float... values) {
- ObjectAnimator anim = new ObjectAnimator();
- anim.setTarget(target);
- anim.setPropertyName(propertyName);
- anim.setFloatValues(values);
+ public static ObjectAnimator ofFloat(View target, Property<View, Float> property,
+ float... values) {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(target, property, values);
cancelOnDestroyActivity(anim);
new FirstFrameAnimatorHelper(anim, target);
return anim;
}
+ public static ObjectAnimator ofViewAlphaAndScale(View target,
+ float alpha, float scaleX, float scaleY) {
+ return ofPropertyValuesHolder(target,
+ PropertyValuesHolder.ofFloat(View.ALPHA, alpha),
+ PropertyValuesHolder.ofFloat(View.SCALE_X, scaleX),
+ PropertyValuesHolder.ofFloat(View.SCALE_Y, scaleY));
+ }
+
public static ObjectAnimator ofPropertyValuesHolder(View target,
PropertyValuesHolder... values) {
- ObjectAnimator anim = new ObjectAnimator();
- anim.setTarget(target);
- anim.setValues(values);
- cancelOnDestroyActivity(anim);
- new FirstFrameAnimatorHelper(anim, target);
- return anim;
+ return ofPropertyValuesHolder(target, target, values);
}
public static ObjectAnimator ofPropertyValuesHolder(Object target,
View view, PropertyValuesHolder... values) {
- ObjectAnimator anim = new ObjectAnimator();
- anim.setTarget(target);
- anim.setValues(values);
+ ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(target, values);
cancelOnDestroyActivity(anim);
new FirstFrameAnimatorHelper(anim, view);
return anim;
}
-
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- public static ValueAnimator createCircularReveal(View view, int centerX,
- int centerY, float startRadius, float endRadius) {
- ValueAnimator anim = UiThreadCircularReveal.createCircularReveal(view, centerX,
- centerY, startRadius, endRadius);
- new FirstFrameAnimatorHelper(anim, view);
- return anim;
- }
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 34b838252..6ce7c5fdd 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -17,10 +17,10 @@
package com.android.launcher3;
import android.app.SearchManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.os.UserManager;
import android.util.Log;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
@@ -35,12 +35,11 @@ import java.lang.ref.WeakReference;
public class LauncherAppState {
private final AppFilter mAppFilter;
- private final BuildInfo mBuildInfo;
@Thunk final LauncherModel mModel;
private final IconCache mIconCache;
private final WidgetPreviewLoader mWidgetCache;
- private boolean mWallpaperChangedSinceLastCheck;
+ @Thunk boolean mWallpaperChangedSinceLastCheck;
private static WeakReference<LauncherProvider> sLauncherProvider;
private static Context sContext;
@@ -89,7 +88,6 @@ public class LauncherAppState {
mWidgetCache = new WidgetPreviewLoader(sContext, mIconCache);
mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class));
- mBuildInfo = BuildInfo.loadByName(sContext.getString(R.string.build_info_class));
mModel = new LauncherModel(this, mIconCache, mAppFilter);
LauncherAppsCompat.getInstance(sContext).addOnAppsChangedCallback(mModel);
@@ -104,6 +102,16 @@ public class LauncherAppState {
sContext.registerReceiver(mModel, filter);
UserManagerCompat.getInstance(sContext).enableAndResetCache();
+
+ if (!Utilities.ATLEAST_KITKAT) {
+ sContext.registerReceiver(new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mWallpaperChangedSinceLastCheck = true;
+ }
+ }, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
+ }
}
/**
@@ -126,7 +134,7 @@ public class LauncherAppState {
}
LauncherModel setLauncher(Launcher launcher) {
- getLauncherProvider().setLauncherProviderChangeListener(launcher);
+ sLauncherProvider.get().setLauncherProviderChangeListener(launcher);
mModel.initialize(launcher);
mAccessibilityDelegate = ((launcher != null) && Utilities.ATLEAST_LOLLIPOP) ?
new LauncherAccessibilityDelegate(launcher) : null;
@@ -149,18 +157,10 @@ public class LauncherAppState {
sLauncherProvider = new WeakReference<LauncherProvider>(provider);
}
- public static LauncherProvider getLauncherProvider() {
- return sLauncherProvider.get();
- }
-
public WidgetPreviewLoader getWidgetCache() {
return mWidgetCache;
}
- public void onWallpaperChanged() {
- mWallpaperChangedSinceLastCheck = true;
- }
-
public boolean hasWallpaperChangedSinceLastCheck() {
boolean result = mWallpaperChangedSinceLastCheck;
mWallpaperChangedSinceLastCheck = false;
@@ -170,8 +170,4 @@ public class LauncherAppState {
public InvariantDeviceProfile getInvariantDeviceProfile() {
return mInvariantDeviceProfile;
}
-
- public static boolean isDogfoodBuild() {
- return getInstance().mBuildInfo.isDogfoodBuild();
- }
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index c49d43fb1..277f49532 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -28,7 +28,8 @@ import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.RemoteViews;
-import com.android.launcher3.DragLayer.TouchCompleteListener;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener;
import java.util.ArrayList;
@@ -53,7 +54,7 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc
super(context);
mContext = context;
mLongPressHelper = new CheckLongPressHelper(this);
- mStylusEventHelper = new StylusEventHelper(this);
+ mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mDragLayer = ((Launcher) context).getDragLayer();
setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
@@ -99,7 +100,7 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc
// Watch for longpress or stylus button press events at this level to
// make sure users can always pick up this widget
- if (mStylusEventHelper.checkAndPerformStylusEvent(ev)) {
+ if (mStylusEventHelper.onMotionEvent(ev)) {
mLongPressHelper.cancelLongPress();
return true;
}
diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java
index 8eb4e6369..2a5f1974e 100644
--- a/src/com/android/launcher3/LauncherBackupAgentHelper.java
+++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java
@@ -73,7 +73,8 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper {
}
// Clear dB before restore
- LauncherAppState.getLauncherProvider().createEmptyDB();
+ LauncherSettings.Settings.call(getContentResolver(),
+ LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
boolean hasData;
try {
@@ -90,12 +91,14 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper {
}
if (hasData && mHelper.restoreSuccessful) {
- LauncherAppState.getLauncherProvider().clearFlagEmptyDbCreated();
+ LauncherSettings.Settings.call(getContentResolver(),
+ LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
LauncherClings.synchonouslyMarkFirstRunClingDismissed(this);
// Rank was added in v4.
if (mHelper.restoredBackupVersion <= 3) {
- LauncherAppState.getLauncherProvider().updateFolderItemsRank();
+ LauncherSettings.Settings.call(getContentResolver(),
+ LauncherSettings.Settings.METHOD_UPDATE_FOLDER_ITEMS_RANK);
}
if (MigrateFromRestoreTask.ENABLED && mHelper.shouldAttemptWorkspaceMigration()) {
@@ -105,10 +108,12 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper {
mHelper.widgetSizes);
}
- LauncherAppState.getLauncherProvider().convertShortcutsToLauncherActivities();
+ LauncherSettings.Settings.call(getContentResolver(),
+ LauncherSettings.Settings.METHOD_CONVERT_SHORTCUTS_TO_ACTIVITIES);
} else {
if (VERBOSE) Log.v(TAG, "Nothing was restored, clearing DB");
- LauncherAppState.getLauncherProvider().createEmptyDB();
+ LauncherSettings.Settings.call(getContentResolver(),
+ LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
}
}
}
diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java
index 8f719e539..cffcbf27f 100644
--- a/src/com/android/launcher3/LauncherClings.java
+++ b/src/com/android/launcher3/LauncherClings.java
@@ -27,9 +27,11 @@ import android.os.Build;
import android.os.Bundle;
import android.os.UserManager;
import android.provider.Settings;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
+import android.view.View.OnKeyListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
@@ -37,7 +39,7 @@ import android.view.accessibility.AccessibilityManager;
import com.android.launcher3.util.Thunk;
-class LauncherClings implements OnClickListener {
+class LauncherClings implements OnClickListener, OnKeyListener {
private static final String MIGRATION_CLING_DISMISSED_KEY = "cling_gel.migration.dismissed";
private static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
@@ -83,6 +85,20 @@ class LauncherClings implements OnClickListener {
}
}
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (event.isPrintingKey()) {
+ // Should ignore all printing keys, otherwise they come to the search box.
+ return true;
+ }
+ if (keyCode == KeyEvent.KEYCODE_MENU) {
+ // Menu key goes to the overview mode similar to longpress, therefore it needs to
+ // dismiss the clings.
+ dismissLongPressCling();
+ }
+ return false;
+ }
+
/**
* Shows the migration cling.
*
@@ -90,6 +106,7 @@ class LauncherClings implements OnClickListener {
* package was not preinstalled and there exists a db to migrate from.
*/
public void showMigrationCling() {
+ mLauncher.onLauncherClingShown();
mIsVisible = true;
mLauncher.hideWorkspaceSearchAndHotseat();
@@ -134,7 +151,9 @@ class LauncherClings implements OnClickListener {
final ViewGroup content = (ViewGroup) cling.findViewById(R.id.cling_content);
mInflater.inflate(showWelcome ? R.layout.longpress_cling_welcome_content
: R.layout.longpress_cling_content, content);
- content.findViewById(R.id.cling_dismiss_longpress_info).setOnClickListener(this);
+ final View button = content.findViewById(R.id.cling_dismiss_longpress_info);
+ button.setOnClickListener(this);
+ button.setOnKeyListener(this);
if (TAG_CROP_TOP_AND_SIDES.equals(content.getTag())) {
Drawable bg = new BorderCropDrawable(mLauncher.getResources().getDrawable(R.drawable.cling_bg),
@@ -142,6 +161,7 @@ class LauncherClings implements OnClickListener {
content.setBackground(bg);
}
+ mLauncher.onLauncherClingShown();
root.addView(cling);
if (showWelcome) {
@@ -159,12 +179,12 @@ class LauncherClings implements OnClickListener {
ObjectAnimator anim;
if (TAG_CROP_TOP_AND_SIDES.equals(content.getTag())) {
content.setTranslationY(-content.getMeasuredHeight());
- anim = LauncherAnimUtils.ofFloat(content, "translationY", 0);
+ anim = LauncherAnimUtils.ofFloat(content, View.TRANSLATION_Y, 0);
} else {
content.setScaleX(0);
content.setScaleY(0);
- PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1);
- PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1);
+ PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 1);
+ PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1);
anim = LauncherAnimUtils.ofPropertyValuesHolder(content, scaleX, scaleY);
}
@@ -178,7 +198,12 @@ class LauncherClings implements OnClickListener {
@Thunk void dismissLongPressCling() {
Runnable dismissCb = new Runnable() {
public void run() {
- dismissCling(mLauncher.findViewById(R.id.longpress_cling), null,
+ Runnable cb = new Runnable() {
+ public void run() {
+ mLauncher.onLauncherClingDismissed();
+ }
+ };
+ dismissCling(mLauncher.findViewById(R.id.longpress_cling), cb,
WORKSPACE_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
}
};
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index a0e287ae2..19d851d11 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -56,6 +56,7 @@ import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.model.MigrateFromRestoreTask;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.util.ComponentKey;
@@ -63,6 +64,7 @@ import com.android.launcher3.util.CursorIconInfo;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.ManagedProfileHeuristic;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.ViewOnDrawExecutor;
import java.lang.ref.WeakReference;
import java.net.URISyntaxException;
@@ -78,6 +80,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.concurrent.Executor;
/**
* Maintains in-memory state of the Launcher. It is expected that there should be only one
@@ -123,12 +126,6 @@ public class LauncherModel extends BroadcastReceiver
@Thunk boolean mWorkspaceLoaded;
@Thunk boolean mAllAppsLoaded;
- // When we are loading pages synchronously, we can't just post the binding of items on the side
- // pages as this delays the rotation process. Instead, we wait for a callback from the first
- // draw (in Workspace) to initiate the binding of the remaining side pages. Any time we start
- // a normal load, we also clear this set of Runnables.
- static final ArrayList<Runnable> mDeferredBindRunnables = new ArrayList<Runnable>();
-
/**
* Set of runnables to be called on the background thread after the workspace binding
* is complete.
@@ -187,6 +184,7 @@ public class LauncherModel extends BroadcastReceiver
public interface Callbacks {
public boolean setLoadOnResume();
public int getCurrentWorkspaceScreen();
+ public void clearPendingBinds();
public void startBinding();
public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end,
boolean forceAnimateIcons);
@@ -211,7 +209,7 @@ public class LauncherModel extends BroadcastReceiver
public void bindSearchProviderChanged();
public boolean isAllAppsButtonRank(int rank);
public void onPageBoundSynchronously(int page);
- public void dumpLogsToLocalData();
+ public void executeOnNextDraw(ViewOnDrawExecutor executor);
}
public interface ItemInfoFilter {
@@ -483,7 +481,9 @@ public class LauncherModel extends BroadcastReceiver
if (!found) {
// Still no position found. Add a new screen to the end.
- screenId = LauncherAppState.getLauncherProvider().generateNewScreenId();
+ screenId = LauncherSettings.Settings.call(context.getContentResolver(),
+ LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
+ .getLong(LauncherSettings.Settings.EXTRA_VALUE);
// Save the screen id for binding in the workspace
workspaceScreens.add(screenId);
@@ -528,8 +528,7 @@ public class LauncherModel extends BroadcastReceiver
// Find appropriate space for the item.
Pair<Long, int[]> coords = findSpaceForItem(context,
- workspaceScreens, addedWorkspaceScreensFinal,
- 1, 1);
+ workspaceScreens, addedWorkspaceScreensFinal, 1, 1);
long screenId = coords.first;
int[] cordinates = coords.second;
@@ -589,11 +588,6 @@ public class LauncherModel extends BroadcastReceiver
"main thread");
}
- // Clear any deferred bind runnables
- synchronized (mDeferredBindRunnables) {
- mDeferredBindRunnables.clear();
- }
-
// Remove any queued UI runnables
mHandler.cancelAll();
// Unbind all the workspace items
@@ -652,12 +646,7 @@ public class LauncherModel extends BroadcastReceiver
modelShortcut.cellX == shortcut.cellX &&
modelShortcut.cellY == shortcut.cellY &&
modelShortcut.spanX == shortcut.spanX &&
- modelShortcut.spanY == shortcut.spanY &&
- ((modelShortcut.dropPos == null && shortcut.dropPos == null) ||
- (modelShortcut.dropPos != null &&
- shortcut.dropPos != null &&
- modelShortcut.dropPos[0] == shortcut.dropPos[0] &&
- modelShortcut.dropPos[1] == shortcut.dropPos[1]))) {
+ modelShortcut.spanY == shortcut.spanY) {
// For all intents and purposes, this is the same object
return;
}
@@ -884,7 +873,7 @@ public class LauncherModel extends BroadcastReceiver
}
private void assertWorkspaceLoaded() {
- if (LauncherAppState.isDogfoodBuild()) {
+ if (ProviderConfig.IS_DOGFOOD_BUILD) {
synchronized (mLock) {
if (!mHasLoaderCompletedOnce ||
(mLoaderTask != null && mLoaderTask.mIsLoadingAndBindingWorkspace)) {
@@ -936,51 +925,6 @@ public class LauncherModel extends BroadcastReceiver
}
/**
- * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
- */
- FolderInfo getFolderById(Context context, LongArrayMap<FolderInfo> folderList, long id) {
- final ContentResolver cr = context.getContentResolver();
- Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
- "_id=? and (itemType=? or itemType=?)",
- new String[] { String.valueOf(id),
- String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)}, null);
-
- try {
- if (c.moveToFirst()) {
- final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
- final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
- final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
- final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
- final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
- final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
- final int optionsIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.OPTIONS);
-
- FolderInfo folderInfo = null;
- switch (c.getInt(itemTypeIndex)) {
- case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
- folderInfo = findOrMakeFolder(folderList, id);
- break;
- }
-
- // Do not trim the folder label, as is was set by the user.
- folderInfo.title = c.getString(titleIndex);
- folderInfo.id = id;
- folderInfo.container = c.getInt(containerIndex);
- folderInfo.screenId = c.getInt(screenIndex);
- folderInfo.cellX = c.getInt(cellXIndex);
- folderInfo.cellY = c.getInt(cellYIndex);
- folderInfo.options = c.getInt(optionsIndex);
-
- return folderInfo;
- }
- } finally {
- c.close();
- }
-
- return null;
- }
-
- /**
* Add an item to the database in a specified container. Sets the container, screen, cellX and
* cellY fields of the item. Also assigns an ID to the item.
*/
@@ -1002,7 +946,9 @@ public class LauncherModel extends BroadcastReceiver
final ContentResolver cr = context.getContentResolver();
item.onAddToDatabase(context, values);
- item.id = LauncherAppState.getLauncherProvider().generateNewItemId();
+ item.id = LauncherSettings.Settings.call(cr, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
+ .getLong(LauncherSettings.Settings.EXTRA_VALUE);
+
values.put(LauncherSettings.Favorites._ID, item.id);
final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
@@ -1042,15 +988,6 @@ public class LauncherModel extends BroadcastReceiver
runOnWorkerThread(r);
}
- /**
- * Creates a new unique child id, for a given cell span across all layouts.
- */
- static int getCellLayoutChildId(
- long container, long screen, int localCellX, int localCellY, int spanX, int spanY) {
- return (((int) container & 0xFF) << 24)
- | ((int) screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF);
- }
-
private static ArrayList<ItemInfo> getItemsByPackageName(
final String pn, final UserHandleCompat user) {
ItemInfoFilter filter = new ItemInfoFilter() {
@@ -1346,14 +1283,16 @@ public class LauncherModel extends BroadcastReceiver
// Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
InstallShortcutReceiver.enableInstallQueue();
synchronized (mLock) {
- // Clear any deferred bind-runnables from the synchronized load process
- // We must do this before any loading/binding is scheduled below.
- synchronized (mDeferredBindRunnables) {
- mDeferredBindRunnables.clear();
- }
-
// Don't bother to start the thread if we know it's not going to do anything
if (mCallbacks != null && mCallbacks.get() != null) {
+ final Callbacks oldCallbacks = mCallbacks.get();
+ // Clear any pending bind-runnables from the synchronized load process.
+ runOnMainThread(new Runnable() {
+ public void run() {
+ oldCallbacks.clearPendingBinds();
+ }
+ });
+
// If there is already one running, tell it to stop.
stopLoaderLocked();
mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags);
@@ -1368,21 +1307,6 @@ public class LauncherModel extends BroadcastReceiver
}
}
- void bindRemainingSynchronousPages() {
- // Post the remaining side pages to be loaded
- if (!mDeferredBindRunnables.isEmpty()) {
- Runnable[] deferredBindRunnables = null;
- synchronized (mDeferredBindRunnables) {
- deferredBindRunnables = mDeferredBindRunnables.toArray(
- new Runnable[mDeferredBindRunnables.size()]);
- mDeferredBindRunnables.clear();
- }
- for (final Runnable r : deferredBindRunnables) {
- mHandler.post(r);
- }
- }
- }
-
public void stopLoader() {
synchronized (mLock) {
if (mLoaderTask != null) {
@@ -1408,8 +1332,7 @@ public class LauncherModel extends BroadcastReceiver
try {
screenIds.add(sc.getLong(idIndex));
} catch (Exception e) {
- Launcher.addDumpLog(TAG, "Desktop items loading interrupted"
- + " - invalid screens: " + e, true);
+ addDumpLog("Invalid screen id: " + e);
}
}
} finally {
@@ -1418,10 +1341,6 @@ public class LauncherModel extends BroadcastReceiver
return screenIds;
}
- public boolean isAllAppsLoaded() {
- return mAllAppsLoaded;
- }
-
/**
* Runnable for the thread that loads the contents of the launcher:
* - workspace icons
@@ -1725,8 +1644,7 @@ public class LauncherModel extends BroadcastReceiver
final PackageManager manager = context.getPackageManager();
final boolean isSafeMode = manager.isSafeMode();
final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
- final boolean isSdCardReady = context.registerReceiver(null,
- new IntentFilter(StartupReceiver.SYSTEM_READY)) != null;
+ final boolean isSdCardReady = Utilities.isBootCompleted();
LauncherAppState app = LauncherAppState.getInstance();
InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
@@ -1753,18 +1671,21 @@ public class LauncherModel extends BroadcastReceiver
}
if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
- Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true);
- LauncherAppState.getLauncherProvider().deleteDatabase();
+ Log.d(TAG, "loadWorkspace: resetting launcher database");
+ LauncherSettings.Settings.call(contentResolver,
+ LauncherSettings.Settings.METHOD_DELETE_DB);
}
if ((mFlags & LOADER_FLAG_MIGRATE_SHORTCUTS) != 0) {
// append the user's Launcher2 shortcuts
- Launcher.addDumpLog(TAG, "loadWorkspace: migrating from launcher2", true);
- LauncherAppState.getLauncherProvider().migrateLauncher2Shortcuts();
+ Log.d(TAG, "loadWorkspace: migrating from launcher2");
+ LauncherSettings.Settings.call(contentResolver,
+ LauncherSettings.Settings.METHOD_MIGRATE_LAUNCHER2_SHORTCUTS);
} else {
// Make sure the default workspace is loaded
- Launcher.addDumpLog(TAG, "loadWorkspace: loading default favorites", false);
- LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary();
+ Log.d(TAG, "loadWorkspace: loading default favorites");
+ LauncherSettings.Settings.call(contentResolver,
+ LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
}
synchronized (sBgLock) {
@@ -1887,8 +1808,7 @@ public class LauncherModel extends BroadcastReceiver
if (intent == null) {
// The app is installed but the component is no
// longer available.
- Launcher.addDumpLog(TAG,
- "Invalid component removed: " + cn, true);
+ addDumpLog("Invalid component removed: " + cn);
itemsToRemove.add(id);
continue;
} else {
@@ -1899,8 +1819,7 @@ public class LauncherModel extends BroadcastReceiver
} else if (restored) {
// Package is not yet available but might be
// installed later.
- Launcher.addDumpLog(TAG,
- "package not yet restored: " + cn, true);
+ addDumpLog("package not yet restored: " + cn);
if ((promiseType & ShortcutInfo.FLAG_RESTORE_STARTED) != 0) {
// Restore has started once.
@@ -1926,14 +1845,12 @@ public class LauncherModel extends BroadcastReceiver
itemReplaced = true;
} else if (REMOVE_UNRESTORED_ICONS) {
- Launcher.addDumpLog(TAG,
- "Unrestored package removed: " + cn, true);
+ addDumpLog("Unrestored package removed: " + cn);
itemsToRemove.add(id);
continue;
}
} else if (REMOVE_UNRESTORED_ICONS) {
- Launcher.addDumpLog(TAG,
- "Unrestored package removed: " + cn, true);
+ addDumpLog("Unrestored package removed: " + cn);
itemsToRemove.add(id);
continue;
}
@@ -1946,8 +1863,7 @@ public class LauncherModel extends BroadcastReceiver
} else if (!isSdCardReady) {
// SdCard is not ready yet. Package might get available,
// once it is ready.
- Launcher.addDumpLog(TAG, "Invalid package: " + cn
- + " (check again later)", true);
+ Log.d(TAG, "Invalid package: " + cn + " (check again later)");
HashSet<String> pkgs = sPendingPackages.get(user);
if (pkgs == null) {
pkgs = new HashSet<String>();
@@ -1960,8 +1876,7 @@ public class LauncherModel extends BroadcastReceiver
} else {
// Do not wait for external media load anymore.
// Log the invalid package, and remove it
- Launcher.addDumpLog(TAG,
- "Invalid package removed: " + cn, true);
+ addDumpLog("Invalid package removed: " + cn);
itemsToRemove.add(id);
continue;
}
@@ -1971,8 +1886,7 @@ public class LauncherModel extends BroadcastReceiver
restored = false;
}
} catch (URISyntaxException e) {
- Launcher.addDumpLog(TAG,
- "Invalid uri: " + intentDescription, true);
+ addDumpLog("Invalid uri: " + intentDescription);
itemsToRemove.add(id);
continue;
}
@@ -1992,9 +1906,6 @@ public class LauncherModel extends BroadcastReceiver
}
} else if (restored) {
if (user.equals(UserHandleCompat.myUserHandle())) {
- Launcher.addDumpLog(TAG,
- "constructing info for partially restored package",
- true);
info = getRestoredItemInfo(c, titleIndex, intent,
promiseType, itemType, cursorIconInfo, context);
intent = getRestoredItemIntent(c, context, intent);
@@ -2149,11 +2060,8 @@ public class LauncherModel extends BroadcastReceiver
final boolean isProviderReady = isValidProvider(provider);
if (!isSafeMode && !customWidget &&
wasProviderReady && !isProviderReady) {
- String log = "Deleting widget that isn't installed anymore: "
- + "id=" + id + " appWidgetId=" + appWidgetId;
-
- Log.e(TAG, log);
- Launcher.addDumpLog(TAG, log, false);
+ addDumpLog("Deleting widget that isn't installed anymore: "
+ + provider);
itemsToRemove.add(id);
} else {
if (isProviderReady) {
@@ -2194,8 +2102,7 @@ public class LauncherModel extends BroadcastReceiver
appWidgetInfo.restoreStatus |=
LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
} else if (REMOVE_UNRESTORED_ICONS && !isSafeMode) {
- Launcher.addDumpLog(TAG,
- "Unrestored widget removed: " + component, true);
+ addDumpLog("Unrestored widget removed: " + component);
itemsToRemove.add(id);
continue;
}
@@ -2247,7 +2154,7 @@ public class LauncherModel extends BroadcastReceiver
break;
}
} catch (Exception e) {
- Launcher.addDumpLog(TAG, "Desktop items loading interrupted", e, true);
+ Log.e(TAG, "Desktop items loading interrupted", e);
}
}
} finally {
@@ -2273,8 +2180,11 @@ public class LauncherModel extends BroadcastReceiver
}
// Remove any empty folder
- for (long folderId : LauncherAppState.getLauncherProvider()
- .deleteEmptyFolders()) {
+ ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings
+ .call(contentResolver,
+ LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
+ .getSerializable(LauncherSettings.Settings.EXTRA_VALUE);
+ for (long folderId : deletedFolderIds) {
sBgWorkspaceItems.remove(sBgFolders.get(folderId));
sBgFolders.remove(folderId);
sBgItemsIdMap.remove(folderId);
@@ -2307,7 +2217,7 @@ public class LauncherModel extends BroadcastReceiver
if (!isSdCardReady && !sPendingPackages.isEmpty()) {
context.registerReceiver(new AppsAvailabilityCheck(),
- new IntentFilter(StartupReceiver.SYSTEM_READY),
+ new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
null, sWorker);
}
@@ -2457,19 +2367,36 @@ public class LauncherModel extends BroadcastReceiver
private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
final LauncherAppState app = LauncherAppState.getInstance();
final InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
- // XXX: review this
+ final int screenCols = profile.numColumns;
+ final int screenCellCount = profile.numColumns * profile.numRows;
Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
@Override
public int compare(ItemInfo lhs, ItemInfo rhs) {
- int cellCountX = (int) profile.numColumns;
- int cellCountY = (int) profile.numRows;
- int screenOffset = cellCountX * cellCountY;
- int containerOffset = screenOffset * (Launcher.SCREEN_COUNT + 1); // +1 hotseat
- long lr = (lhs.container * containerOffset + lhs.screenId * screenOffset +
- lhs.cellY * cellCountX + lhs.cellX);
- long rr = (rhs.container * containerOffset + rhs.screenId * screenOffset +
- rhs.cellY * cellCountX + rhs.cellX);
- return Utilities.longCompare(lr, rr);
+ if (lhs.container == rhs.container) {
+ // Within containers, order by their spatial position in that container
+ switch ((int) lhs.container) {
+ case LauncherSettings.Favorites.CONTAINER_DESKTOP: {
+ long lr = (lhs.screenId * screenCellCount +
+ lhs.cellY * screenCols + lhs.cellX);
+ long rr = (rhs.screenId * screenCellCount +
+ rhs.cellY * screenCols + rhs.cellX);
+ return Utilities.longCompare(lr, rr);
+ }
+ case LauncherSettings.Favorites.CONTAINER_HOTSEAT: {
+ // We currently use the screen id as the rank
+ return Utilities.longCompare(lhs.screenId, rhs.screenId);
+ }
+ default:
+ if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ throw new RuntimeException("Unexpected container type when " +
+ "sorting workspace items.");
+ }
+ return 0;
+ }
+ } else {
+ // Between containers, order by hotseat, desktop
+ return Utilities.longCompare(lhs.container, rhs.container);
+ }
}
});
}
@@ -2492,9 +2419,7 @@ public class LauncherModel extends BroadcastReceiver
final ArrayList<ItemInfo> workspaceItems,
final ArrayList<LauncherAppWidgetInfo> appWidgets,
final LongArrayMap<FolderInfo> folders,
- ArrayList<Runnable> deferredBindRunnables) {
-
- final boolean postOnMainThread = (deferredBindRunnables != null);
+ final Executor executor) {
// Bind the workspace items
int N = workspaceItems.size();
@@ -2511,13 +2436,7 @@ public class LauncherModel extends BroadcastReceiver
}
}
};
- if (postOnMainThread) {
- synchronized (deferredBindRunnables) {
- deferredBindRunnables.add(r);
- }
- } else {
- runOnMainThread(r);
- }
+ executor.execute(r);
}
// Bind the folders
@@ -2530,13 +2449,7 @@ public class LauncherModel extends BroadcastReceiver
}
}
};
- if (postOnMainThread) {
- synchronized (deferredBindRunnables) {
- deferredBindRunnables.add(r);
- }
- } else {
- runOnMainThread(r);
- }
+ executor.execute(r);
}
// Bind the widgets, one at a time
@@ -2551,11 +2464,7 @@ public class LauncherModel extends BroadcastReceiver
}
}
};
- if (postOnMainThread) {
- deferredBindRunnables.add(r);
- } else {
- runOnMainThread(r);
- }
+ executor.execute(r);
}
}
@@ -2633,6 +2542,7 @@ public class LauncherModel extends BroadcastReceiver
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
+ callbacks.clearPendingBinds();
callbacks.startBinding();
}
}
@@ -2641,28 +2551,20 @@ public class LauncherModel extends BroadcastReceiver
bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
- // Load items on the current page
+ Executor mainExecutor = new DeferredMainThreadExecutor();
+ // Load items on the current page.
bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
- currentFolders, null);
- if (isLoadingSynchronously) {
- r = new Runnable() {
- public void run() {
- Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null && currentScreen != PagedView.INVALID_RESTORE_PAGE) {
- callbacks.onPageBoundSynchronously(currentScreen);
- }
- }
- };
- runOnMainThread(r);
- }
+ currentFolders, mainExecutor);
- // Load all the remaining pages (if we are loading synchronously, we want to defer this
- // work until after the first render)
- synchronized (mDeferredBindRunnables) {
- mDeferredBindRunnables.clear();
- }
- bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
- (isLoadingSynchronously ? mDeferredBindRunnables : null));
+ // In case of isLoadingSynchronously, only bind the first screen, and defer binding the
+ // remaining screens after first onDraw is called. This ensures that the first screen
+ // is immediately visible (eg. during rotation)
+ // In case of !isLoadingSynchronously, bind all pages one after other.
+ final Executor deferredExecutor = isLoadingSynchronously ?
+ new ViewOnDrawExecutor(mHandler) : mainExecutor;
+
+ bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets,
+ otherFolders, deferredExecutor);
// Tell the workspace that we're done binding items
r = new Runnable() {
@@ -2692,11 +2594,23 @@ public class LauncherModel extends BroadcastReceiver
}
};
+ deferredExecutor.execute(r);
+
if (isLoadingSynchronously) {
- synchronized (mDeferredBindRunnables) {
- mDeferredBindRunnables.add(r);
- }
- } else {
+ r = new Runnable() {
+ public void run() {
+ Callbacks callbacks = tryGetCallbacks(oldCallbacks);
+ if (callbacks != null) {
+ // We are loading synchronously, which means, some of the pages will be
+ // bound after first draw. Inform the callbacks that page binding is
+ // not complete, and schedule the remaining pages.
+ if (currentScreen != PagedView.INVALID_RESTORE_PAGE) {
+ callbacks.onPageBoundSynchronously(currentScreen);
+ }
+ callbacks.executeOnNextDraw((ViewOnDrawExecutor) deferredExecutor);
+ }
+ }
+ };
runOnMainThread(r);
}
}
@@ -2961,10 +2875,8 @@ public class LauncherModel extends BroadcastReceiver
boolean packageOnSdcard = launcherApps.isAppEnabled(
manager, pkg, PackageManager.GET_UNINSTALLED_PACKAGES);
if (packageOnSdcard) {
- Launcher.addDumpLog(TAG, "Package found on sd-card: " + pkg, true);
packagesUnavailable.add(pkg);
} else {
- Launcher.addDumpLog(TAG, "Package not found: " + pkg, true);
packagesRemoved.add(pkg);
}
}
@@ -3311,7 +3223,7 @@ public class LauncherModel extends BroadcastReceiver
.setPackage(pkg), 0);
needToRefresh |= widgets != null && !widgets.isEmpty();
} catch (RuntimeException e) {
- if (LauncherAppState.isDogfoodBuild()) {
+ if (ProviderConfig.IS_DOGFOOD_BUILD) {
throw e;
}
// Ignore the crash. We can live with a state widget list.
@@ -3322,16 +3234,6 @@ public class LauncherModel extends BroadcastReceiver
loadAndBindWidgetsAndShortcuts(callbacks, needToRefresh);
}
-
- // Write all the logs to disk
- mHandler.post(new Runnable() {
- public void run() {
- Callbacks cb = getCallback();
- if (callbacks == cb && cb != null) {
- callbacks.dumpLogsToLocalData();
- }
- }
- });
}
}
@@ -3368,7 +3270,7 @@ public class LauncherModel extends BroadcastReceiver
return results;
}
} catch (Exception e) {
- if (!LauncherAppState.isDogfoodBuild() &&
+ if (!ProviderConfig.IS_DOGFOOD_BUILD &&
(e.getCause() instanceof TransactionTooLargeException ||
e.getCause() instanceof DeadObjectException)) {
// the returned value may be incomplete and will not be refreshed until the next
@@ -3438,7 +3340,7 @@ public class LauncherModel extends BroadcastReceiver
List<ResolveInfo> providers = packageManager.queryIntentActivities(shortcutsIntent, 0);
sBgShortcutProviders = providers;
} catch (RuntimeException e) {
- if (!LauncherAppState.isDogfoodBuild() &&
+ if (!ProviderConfig.IS_DOGFOOD_BUILD &&
(e.getCause() instanceof TransactionTooLargeException ||
e.getCause() instanceof DeadObjectException)) {
/**
@@ -3754,10 +3656,22 @@ public class LauncherModel extends BroadcastReceiver
}
}
+ @Thunk class DeferredMainThreadExecutor implements Executor {
+
+ @Override
+ public void execute(Runnable command) {
+ runOnMainThread(command);
+ }
+ }
+
/**
* @return the looper for the worker thread which can be used to start background tasks.
*/
public static Looper getWorkerLooper() {
return sWorkerThread.getLooper();
}
+
+ @Thunk static final void addDumpLog(String log) {
+ Launcher.addDumpLog(TAG, log);
+ }
}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index f095a054e..6598644a8 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -62,7 +62,6 @@ import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
-import java.util.List;
public class LauncherProvider extends ContentProvider {
private static final String TAG = "LauncherProvider";
@@ -95,10 +94,6 @@ public class LauncherProvider extends ContentProvider {
return true;
}
- public boolean wasNewDbCreated() {
- return mOpenHelper.wasNewDbCreated();
- }
-
public void setLauncherProviderChangeListener(LauncherProviderChangeListener listener) {
mListener = listener;
mOpenHelper.mListener = mListener;
@@ -275,6 +270,50 @@ public class LauncherProvider extends ContentProvider {
result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, value);
return result;
}
+ case LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG: {
+ clearFlagEmptyDbCreated();
+ return null;
+ }
+ case LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS: {
+ Bundle result = new Bundle();
+ result.putSerializable(LauncherSettings.Settings.EXTRA_VALUE, deleteEmptyFolders());
+ return result;
+ }
+ case LauncherSettings.Settings.METHOD_NEW_ITEM_ID: {
+ Bundle result = new Bundle();
+ result.putLong(LauncherSettings.Settings.EXTRA_VALUE, mOpenHelper.generateNewItemId());
+ return result;
+ }
+ case LauncherSettings.Settings.METHOD_NEW_SCREEN_ID: {
+ Bundle result = new Bundle();
+ result.putLong(LauncherSettings.Settings.EXTRA_VALUE, mOpenHelper.generateNewScreenId());
+ return result;
+ }
+ case LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB: {
+ createEmptyDB();
+ return null;
+ }
+ case LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES: {
+ loadDefaultFavoritesIfNecessary();
+ return null;
+ }
+ case LauncherSettings.Settings.METHOD_MIGRATE_LAUNCHER2_SHORTCUTS: {
+ mOpenHelper.migrateLauncher2Shortcuts(mOpenHelper.getWritableDatabase(),
+ Uri.parse(getContext().getString(R.string.old_launcher_provider_uri)));
+ return null;
+ }
+ case LauncherSettings.Settings.METHOD_UPDATE_FOLDER_ITEMS_RANK: {
+ mOpenHelper.updateFolderItemsRank(mOpenHelper.getWritableDatabase(), false);
+ return null;
+ }
+ case LauncherSettings.Settings.METHOD_CONVERT_SHORTCUTS_TO_ACTIVITIES: {
+ mOpenHelper.convertShortcutsToLauncherActivities(mOpenHelper.getWritableDatabase());
+ return null;
+ }
+ case LauncherSettings.Settings.METHOD_DELETE_DB: {
+ deleteDatabase();
+ return null;
+ }
}
return null;
}
@@ -283,8 +322,8 @@ public class LauncherProvider extends ContentProvider {
* Deletes any empty folder from the DB.
* @return Ids of deleted folders.
*/
- public List<Long> deleteEmptyFolders() {
- ArrayList<Long> folderIds = new ArrayList<Long>();
+ private ArrayList<Long> deleteEmptyFolders() {
+ ArrayList<Long> folderIds = new ArrayList<>();
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
db.beginTransaction();
try {
@@ -301,7 +340,7 @@ public class LauncherProvider extends ContentProvider {
folderIds.add(c.getLong(0));
}
c.close();
- if (folderIds.size() > 0) {
+ if (!folderIds.isEmpty()) {
db.delete(TABLE_FAVORITES, Utilities.createDbSelectionQuery(
LauncherSettings.Favorites._ID, folderIds), null);
}
@@ -327,22 +366,14 @@ public class LauncherProvider extends ContentProvider {
values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis());
}
- public long generateNewItemId() {
- return mOpenHelper.generateNewItemId();
- }
-
- public long generateNewScreenId() {
- return mOpenHelper.generateNewScreenId();
- }
-
/**
* Clears all the data for a fresh start.
*/
- synchronized public void createEmptyDB() {
+ synchronized private void createEmptyDB() {
mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
}
- public void clearFlagEmptyDbCreated() {
+ private void clearFlagEmptyDbCreated() {
Utilities.getPrefs(getContext()).edit().remove(EMPTY_DATABASE_CREATED).commit();
}
@@ -353,7 +384,7 @@ public class LauncherProvider extends ContentProvider {
* 3) From a partner configuration APK, already in the system image
* 4) The default configuration for the particular device
*/
- synchronized public void loadDefaultFavoritesIfNecessary() {
+ synchronized private void loadDefaultFavoritesIfNecessary() {
SharedPreferences sp = Utilities.getPrefs(getContext());
if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
@@ -438,21 +469,7 @@ public class LauncherProvider extends ContentProvider {
mOpenHelper, getContext().getResources(), defaultLayout);
}
- public void migrateLauncher2Shortcuts() {
- mOpenHelper.migrateLauncher2Shortcuts(mOpenHelper.getWritableDatabase(),
- Uri.parse(getContext().getString(R.string.old_launcher_provider_uri)));
- }
-
- public void updateFolderItemsRank() {
- mOpenHelper.updateFolderItemsRank(mOpenHelper.getWritableDatabase(), false);
- }
-
- public void convertShortcutsToLauncherActivities() {
- mOpenHelper.convertShortcutsToLauncherActivities(mOpenHelper.getWritableDatabase());
- }
-
-
- public void deleteDatabase() {
+ private void deleteDatabase() {
// Are you sure? (y/n)
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final File dbFile = new File(db.getPath());
@@ -1124,10 +1141,6 @@ public class LauncherProvider extends ContentProvider {
= c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
final int cellYIndex
= c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
- final int uriIndex
- = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
- final int displayModeIndex
- = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
final int profileIndex
= c.getColumnIndex(LauncherSettings.Favorites.PROFILE_ID);
@@ -1175,17 +1188,10 @@ public class LauncherProvider extends ContentProvider {
}
if (userHandle == null) {
- Launcher.addDumpLog(TAG, "skipping deleted user", true);
+ Log.d(TAG, "skipping deleted user");
continue;
}
- Launcher.addDumpLog(TAG, "migrating \""
- + c.getString(titleIndex) + "\" ("
- + cellX + "," + cellY + "@"
- + LauncherSettings.Favorites.containerToString(container)
- + "/" + screen
- + "): " + intentStr, true);
-
if (itemType != Favorites.ITEM_TYPE_FOLDER) {
final Intent intent;
@@ -1194,22 +1200,20 @@ public class LauncherProvider extends ContentProvider {
intent = Intent.parseUri(intentStr, 0);
} catch (URISyntaxException e) {
// bogus intent?
- Launcher.addDumpLog(TAG,
- "skipping invalid intent uri", true);
+ Log.d(TAG, "skipping invalid intent uri");
continue;
}
cn = intent.getComponent();
if (TextUtils.isEmpty(intentStr)) {
// no intent? no icon
- Launcher.addDumpLog(TAG, "skipping empty intent", true);
+ Log.d(TAG, "skipping empty intent");
continue;
} else if (cn != null &&
!LauncherModel.isValidPackageActivity(mContext, cn,
userHandle)) {
// component no longer exists.
- Launcher.addDumpLog(TAG, "skipping item whose component " +
- "no longer exists.", true);
+ Log.d(TAG, "skipping item whose component no longer exists.");
continue;
} else if (container ==
LauncherSettings.Favorites.CONTAINER_DESKTOP) {
@@ -1225,7 +1229,7 @@ public class LauncherProvider extends ContentProvider {
final String key = intent.toUri(0);
intent.setFlags(flags);
if (seenIntents.contains(key)) {
- Launcher.addDumpLog(TAG, "skipping duplicate", true);
+ Log.d(TAG, "skipping duplicate");
continue;
} else {
seenIntents.add(key);
@@ -1246,9 +1250,6 @@ public class LauncherProvider extends ContentProvider {
c.getString(iconResourceIndex));
values.put(LauncherSettings.Favorites.ITEM_TYPE, itemType);
values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
- values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
- values.put(LauncherSettings.Favorites.DISPLAY_MODE,
- c.getInt(displayModeIndex));
values.put(LauncherSettings.Favorites.PROFILE_ID, userSerialNumber);
if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
@@ -1368,8 +1369,8 @@ public class LauncherProvider extends ContentProvider {
}
}
- Launcher.addDumpLog(TAG, "migrated " + count + " icons from Launcher2 into "
- + (curScreen+1) + " screens", true);
+ Log.d(TAG, "migrated " + count + " icons from Launcher2 into "
+ + (curScreen+1) + " screens");
// ensure that new screens are created to hold these icons
setFlagJustLoadedOldDb();
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 8a5804f34..9ee6a2199 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -16,7 +16,9 @@
package com.android.launcher3;
+import android.content.ContentResolver;
import android.net.Uri;
+import android.os.Bundle;
import android.provider.BaseColumns;
import com.android.launcher3.config.ProviderConfig;
@@ -321,8 +323,27 @@ public class LauncherSettings {
public static final String METHOD_GET_BOOLEAN = "get_boolean_setting";
public static final String METHOD_SET_BOOLEAN = "set_boolean_setting";
+ public static final String METHOD_CLEAR_EMPTY_DB_FLAG = "clear_empty_db_flag";
+
+ public static final String METHOD_DELETE_EMPTY_FOLDERS = "delete_empty_folders";
+ public static final String METHOD_UPDATE_FOLDER_ITEMS_RANK = "update_folder_items_rank";
+ public static final String METHOD_CONVERT_SHORTCUTS_TO_ACTIVITIES =
+ "convert_shortcuts_to_launcher_activities";
+
+ public static final String METHOD_NEW_ITEM_ID = "generate_new_item_id";
+ public static final String METHOD_NEW_SCREEN_ID = "generate_new_screen_id";
+
+ public static final String METHOD_CREATE_EMPTY_DB = "create_empty_db";
+ public static final String METHOD_DELETE_DB = "delete_db";
+
+ public static final String METHOD_LOAD_DEFAULT_FAVORITES = "load_default_favorites";
+ public static final String METHOD_MIGRATE_LAUNCHER2_SHORTCUTS = "migrate_l2_shortcuts";
public static final String EXTRA_VALUE = "value";
public static final String EXTRA_DEFAULT_VALUE = "default_value";
+
+ public static Bundle call(ContentResolver cr, String method) {
+ return cr.call(CONTENT_URI, method, null, null);
+ }
}
}
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 83b12a99d..668e021c6 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -24,15 +24,17 @@ import android.animation.PropertyValuesHolder;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
import android.content.res.Resources;
+import android.os.Build;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.util.UiThreadCircularReveal;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.UiThreadCircularReveal;
import com.android.launcher3.widget.WidgetsContainerView;
import java.util.HashMap;
@@ -163,7 +165,6 @@ public class LauncherStateTransitionAnimation {
final boolean animated) {
final WidgetsContainerView toView = mLauncher.getWidgetsView();
final View buttonView = mLauncher.getWidgetsButton();
-
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@Override
public float getMaterialRevealViewFinalAlpha(View revealView) {
@@ -176,11 +177,11 @@ public class LauncherStateTransitionAnimation {
}
/**
- * Starts and animation to the workspace from the current overlay view.
+ * Starts an animation to the workspace from the current overlay view.
*/
public void startAnimationToWorkspace(final Launcher.State fromState,
final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
- final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) {
+ final boolean animated, final Runnable onCompleteRunnable) {
if (toWorkspaceState != Workspace.State.NORMAL &&
toWorkspaceState != Workspace.State.SPRING_LOADED &&
toWorkspaceState != Workspace.State.OVERVIEW) {
@@ -188,10 +189,14 @@ public class LauncherStateTransitionAnimation {
}
if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) {
- startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState, toWorkspacePage,
+ startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState,
+ animated, onCompleteRunnable);
+ } else if (fromState == Launcher.State.WIDGETS ||
+ fromState == Launcher.State.WIDGETS_SPRING_LOADED) {
+ startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState,
animated, onCompleteRunnable);
} else {
- startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState, toWorkspacePage,
+ startAnimationToNewWorkspaceState(fromWorkspaceState, toWorkspaceState,
animated, onCompleteRunnable);
}
}
@@ -221,16 +226,8 @@ public class LauncherStateTransitionAnimation {
// Cancel the current animation
cancelAnimation();
- // Create the workspace animation.
- // NOTE: this call apparently also sets the state for the workspace if !animated
- Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, -1,
- animated, layerViews);
-
- // Animate the search bar
- startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState,
- animated ? revealDuration : 0, overlaySearchBarView);
-
- Animator updateTransitionStepAnim = dispatchOnLauncherTransitionStepAnim(fromView, toView);
+ playCommonTransitionAnimations(fromWorkspaceState, toWorkspaceState, fromView, toView,
+ overlaySearchBarView, animated, initialized, animation, revealDuration, layerViews);
if (animated && initialized) {
// Setup the reveal view animation
@@ -260,11 +257,11 @@ public class LauncherStateTransitionAnimation {
// Create the animators
PropertyValuesHolder panelAlpha =
- PropertyValuesHolder.ofFloat("alpha", revealViewToAlpha, 1f);
+ PropertyValuesHolder.ofFloat(View.ALPHA, revealViewToAlpha, 1f);
PropertyValuesHolder panelDriftY =
- PropertyValuesHolder.ofFloat("translationY", revealViewToYDrift, 0);
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, revealViewToYDrift, 0);
PropertyValuesHolder panelDriftX =
- PropertyValuesHolder.ofFloat("translationX", revealViewToXDrift, 0);
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_X, revealViewToXDrift, 0);
ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView,
panelAlpha, panelDriftY, panelDriftX);
panelAlphaAndDrift.setDuration(revealDuration);
@@ -340,13 +337,6 @@ public class LauncherStateTransitionAnimation {
});
- // Play the workspace animation
- if (workspaceAnim != null) {
- animation.play(workspaceAnim);
- }
-
- animation.play(updateTransitionStepAnim);
-
// Dispatch the prepare transition signal
dispatchOnLauncherTransitionPrepare(fromView, animated, false);
dispatchOnLauncherTransitionPrepare(toView, animated, false);
@@ -366,7 +356,7 @@ public class LauncherStateTransitionAnimation {
if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
- if (Utilities.ATLEAST_LOLLIPOP && Utilities.isViewAttachedToWindow(v)) {
+ if (Utilities.ATLEAST_LOLLIPOP && v.isAttachedToWindow()) {
v.buildLayer();
}
}
@@ -406,6 +396,32 @@ public class LauncherStateTransitionAnimation {
}
/**
+ * Plays animations used by various transitions.
+ */
+ private void playCommonTransitionAnimations(Workspace.State fromWorkspaceState,
+ Workspace.State toWorkspaceState, View fromView, View toView, View overlaySearchBarView,
+ boolean animated, boolean initialized, AnimatorSet animation, int revealDuration,
+ HashMap<View, Integer> layerViews) {
+ // Create the workspace animation.
+ // NOTE: this call apparently also sets the state for the workspace if !animated
+ Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
+ animated, layerViews);
+
+ // Animate the search bar
+ startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState,
+ animated ? revealDuration : 0, overlaySearchBarView);
+
+ if (animated && initialized) {
+ // Play the workspace animation
+ if (workspaceAnim != null) {
+ animation.play(workspaceAnim);
+ }
+ // Dispatch onLauncherTransitionStep() as the animation interpolates.
+ animation.play(dispatchOnLauncherTransitionStepAnim(fromView, toView));
+ }
+ }
+
+ /**
* Returns an Animator that calls {@link #dispatchOnLauncherTransitionStep(View, float)} on
* {@param fromView} and {@param toView} as the animation interpolates.
*
@@ -424,11 +440,11 @@ public class LauncherStateTransitionAnimation {
}
/**
- * Starts and animation to the workspace from the apps view.
+ * Starts an animation to the workspace from the apps view.
*/
private void startAnimationToWorkspaceFromAllApps(final Workspace.State fromWorkspaceState,
- final Workspace.State toWorkspaceState, final int toWorkspacePage,
- final boolean animated, final Runnable onCompleteRunnable) {
+ final Workspace.State toWorkspaceState, final boolean animated,
+ final Runnable onCompleteRunnable) {
AllAppsContainerView appsView = mLauncher.getAppsView();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@Override
@@ -463,17 +479,17 @@ public class LauncherStateTransitionAnimation {
};
// Only animate the search bar if animating to spring loaded mode from all apps
mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
- toWorkspacePage, mLauncher.getAllAppsButton(), appsView, appsView.getContentView(),
+ mLauncher.getAllAppsButton(), appsView, appsView.getContentView(),
appsView.getRevealView(), appsView.getSearchBarView(), animated,
onCompleteRunnable, cb);
}
/**
- * Starts and animation to the workspace from the widgets view.
+ * Starts an animation to the workspace from the widgets view.
*/
private void startAnimationToWorkspaceFromWidgets(final Workspace.State fromWorkspaceState,
- final Workspace.State toWorkspaceState, final int toWorkspacePage,
- final boolean animated, final Runnable onCompleteRunnable) {
+ final Workspace.State toWorkspaceState, final boolean animated,
+ final Runnable onCompleteRunnable) {
final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@Override
@@ -492,16 +508,97 @@ public class LauncherStateTransitionAnimation {
}
};
mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState,
- toWorkspaceState, toWorkspacePage, mLauncher.getWidgetsButton(), widgetsView,
+ toWorkspaceState, mLauncher.getWidgetsButton(), widgetsView,
widgetsView.getContentView(), widgetsView.getRevealView(), null, animated,
onCompleteRunnable, cb);
}
/**
+ * Starts an animation to the workspace from another workspace state, e.g. normal to overview.
+ */
+ private void startAnimationToNewWorkspaceState(final Workspace.State fromWorkspaceState,
+ final Workspace.State toWorkspaceState, final boolean animated,
+ final Runnable onCompleteRunnable) {
+ final View fromWorkspace = mLauncher.getWorkspace();
+ final HashMap<View, Integer> layerViews = new HashMap<>();
+ final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
+ final int revealDuration = mLauncher.getResources()
+ .getInteger(R.integer.config_overlayRevealTime);
+
+ // Cancel the current animation
+ cancelAnimation();
+
+ playCommonTransitionAnimations(fromWorkspaceState, toWorkspaceState, fromWorkspace, null,
+ null, animated, animated, animation, revealDuration, layerViews);
+
+ if (animated) {
+ dispatchOnLauncherTransitionPrepare(fromWorkspace, animated, true);
+
+ final AnimatorSet stateAnimation = animation;
+ final Runnable startAnimRunnable = new Runnable() {
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public void run() {
+ // Check that mCurrentAnimation hasn't changed while
+ // we waited for a layout/draw pass
+ if (mCurrentAnimation != stateAnimation)
+ return;
+
+ dispatchOnLauncherTransitionStart(fromWorkspace, animated, true);
+
+ // Enable all necessary layers
+ for (View v : layerViews.keySet()) {
+ if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
+ v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ }
+ if (Utilities.ATLEAST_LOLLIPOP && v.isAttachedToWindow()) {
+ v.buildLayer();
+ }
+ }
+ stateAnimation.start();
+ }
+ };
+ animation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ dispatchOnLauncherTransitionEnd(fromWorkspace, animated, true);
+
+ // Run any queued runnables
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
+
+ // Disable all necessary layers
+ for (View v : layerViews.keySet()) {
+ if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
+ v.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ }
+
+ // This can hold unnecessary references to views.
+ cleanupAnimation();
+ }
+ });
+ fromWorkspace.post(startAnimRunnable);
+ mCurrentAnimation = animation;
+ } else /* if (!animated) */ {
+ dispatchOnLauncherTransitionPrepare(fromWorkspace, animated, true);
+ dispatchOnLauncherTransitionStart(fromWorkspace, animated, true);
+ dispatchOnLauncherTransitionEnd(fromWorkspace, animated, true);
+
+ // Run any queued runnables
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
+
+ mCurrentAnimation = null;
+ }
+ }
+
+ /**
* Creates and starts a new animation to the workspace.
*/
private AnimatorSet startAnimationToWorkspaceFromOverlay(final Workspace.State fromWorkspaceState,
- final Workspace.State toWorkspaceState, final int toWorkspacePage, final View buttonView,
+ final Workspace.State toWorkspaceState, final View buttonView,
final View fromView, final View contentView, final View revealView,
final View overlaySearchBarView, final boolean animated, final Runnable onCompleteRunnable,
final PrivateTransitionCallbacks pCb) {
@@ -522,25 +619,10 @@ public class LauncherStateTransitionAnimation {
// Cancel the current animation
cancelAnimation();
- // Create the workspace animation.
- // NOTE: this call apparently also sets the state for the workspace if !animated
- Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
- toWorkspacePage, animated, layerViews);
-
- // Animate the search bar
- startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState,
- animated ? revealDuration : 0, overlaySearchBarView);
-
- Animator updateTransitionStepAnim = dispatchOnLauncherTransitionStepAnim(fromView, toView);
+ playCommonTransitionAnimations(fromWorkspaceState, toWorkspaceState, fromView, toView,
+ overlaySearchBarView, animated, initialized, animation, revealDuration, layerViews);
if (animated && initialized) {
- // Play the workspace animation
- if (workspaceAnim != null) {
- animation.play(workspaceAnim);
- }
-
- animation.play(updateTransitionStepAnim);
-
// hideAppsCustomizeHelper is called in some cases when it is already hidden
// don't perform all these no-op animations. In particularly, this was causing
// the all-apps button to pop in and out.
@@ -683,6 +765,7 @@ public class LauncherStateTransitionAnimation {
final AnimatorSet stateAnimation = animation;
final Runnable startAnimRunnable = new Runnable() {
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void run() {
// Check that mCurrentAnimation hasn't changed while
// we waited for a layout/draw pass
@@ -697,7 +780,7 @@ public class LauncherStateTransitionAnimation {
if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
- if (Utilities.ATLEAST_LOLLIPOP && Utilities.isViewAttachedToWindow(v)) {
+ if (Utilities.ATLEAST_LOLLIPOP && v.isAttachedToWindow()) {
v.buildLayer();
}
}
@@ -707,7 +790,7 @@ public class LauncherStateTransitionAnimation {
fromView.post(startAnimRunnable);
return animation;
- } else {
+ } else /* if (!(animated && initialized)) */ {
fromView.setVisibility(View.GONE);
dispatchOnLauncherTransitionPrepare(fromView, animated, true);
dispatchOnLauncherTransitionStart(fromView, animated, true);
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index b6d3cc492..869d42c1b 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -18,10 +18,10 @@ package com.android.launcher3;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
+import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
@@ -47,10 +47,8 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Interpolator;
-
import com.android.launcher3.util.LauncherEdgeEffect;
import com.android.launcher3.util.Thunk;
-
import java.util.ArrayList;
/**
@@ -65,9 +63,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
// the min drag distance for a fling to register, to prevent random page shifts
private static final int MIN_LENGTH_FOR_FLING = 25;
- protected static final int PAGE_SNAP_ANIMATION_DURATION = 750;
+ public static final int PAGE_SNAP_ANIMATION_DURATION = 750;
protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
- protected static final float NANOTIME_DIV = 1000000000.0f;
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.
@@ -87,25 +84,19 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
private int mFreeScrollMinScrollX = -1;
private int mFreeScrollMaxScrollX = -1;
- static final int AUTOMATIC_PAGE_SPACING = -1;
-
protected int mFlingThresholdVelocity;
protected int mMinFlingVelocity;
protected int mMinSnapVelocity;
- protected float mDensity;
- protected float mSmoothingTime;
- protected float mTouchX;
-
protected boolean mFirstLayout = true;
private int mNormalChildHeight;
protected int mCurrentPage;
protected int mRestorePage = INVALID_RESTORE_PAGE;
- protected int mChildCountOnLastLayout;
+ private int mChildCountOnLastLayout;
protected int mNextPage = INVALID_PAGE;
- protected int mMaxScrollX;
+ private int mMaxScrollX;
protected LauncherScroller mScroller;
private Interpolator mDefaultInterpolator;
private VelocityTracker mVelocityTracker;
@@ -117,10 +108,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
private float mDownMotionY;
private float mDownScrollX;
private float mDragViewBaselineLeft;
- protected float mLastMotionX;
- protected float mLastMotionXRemainder;
- protected float mLastMotionY;
- protected float mTotalMotionX;
+ private float mLastMotionX;
+ private float mLastMotionXRemainder;
+ private float mLastMotionY;
+ private float mTotalMotionX;
private int mLastScreenCenter = -1;
private boolean mCancelTap;
@@ -133,23 +124,17 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
protected final static int TOUCH_STATE_NEXT_PAGE = 3;
protected final static int TOUCH_STATE_REORDERING = 4;
- protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f;
-
protected int mTouchState = TOUCH_STATE_REST;
- protected boolean mForceScreenScrolled = false;
+ private boolean mForceScreenScrolled = false;
protected OnLongClickListener mLongClickListener;
protected int mTouchSlop;
private int mMaximumVelocity;
- protected int mPageLayoutWidthGap;
- protected int mPageLayoutHeightGap;
protected int mCellCountX = 0;
protected int mCellCountY = 0;
- protected boolean mCenterPagesVertically;
protected boolean mAllowOverScroll = true;
protected int[] mTempVisiblePagesRange = new int[2];
- protected boolean mForceDrawAllChildrenNextFrame;
protected static final int INVALID_POINTER = -1;
@@ -180,7 +165,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
private float mMinScale = 1f;
private boolean mUseMinScale = false;
- protected View mDragView;
+ @Thunk View mDragView;
private Runnable mSidePageHoverRunnable;
@Thunk int mSidePageHoverIndex = -1;
// This variable's scope is only for the duration of startReordering() and endReordering()
@@ -189,7 +174,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
// animation after endReordering()
private boolean mIsReordering;
// The runnable that settles the page after snapToPage and animateDragViewToOriginalPosition
- private int NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT = 2;
+ private static final int NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT = 2;
private int mPostReorderingPreZoomInRemainingAnimationCount;
private Runnable mPostReorderingPreZoomInRunnable;
@@ -223,11 +208,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.PagedView, defStyle, 0);
-
- mPageLayoutWidthGap = a.getDimensionPixelSize(
- R.styleable.PagedView_pageLayoutWidthGap, 0);
- mPageLayoutHeightGap = a.getDimensionPixelSize(
- R.styleable.PagedView_pageLayoutHeightGap, 0);
mPageIndicatorViewId = a.getResourceId(R.styleable.PagedView_pageIndicator, -1);
a.recycle();
@@ -243,16 +223,15 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
mScroller = new LauncherScroller(getContext());
setDefaultInterpolator(new ScrollInterpolator());
mCurrentPage = 0;
- mCenterPagesVertically = true;
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledPagingTouchSlop();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
- mDensity = getResources().getDisplayMetrics().density;
- mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
- mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * mDensity);
- mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * mDensity);
+ float density = getResources().getDisplayMetrics().density;
+ mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * density);
+ mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * density);
+ mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density);
setOnHierarchyChangeListener(this);
setWillNotDraw(false);
}
@@ -393,7 +372,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
/**
- * Returns the index of the currently displayed page.
+ * Returns the index of the currently displayed page. When in free scroll mode, this is the page
+ * that the user was on before entering free scroll mode (e.g. the home screen page they
+ * long-pressed on to enter the overview). Try using {@link #getPageNearestToCenterOfScreen()}
+ * to get the page the user is currently scrolling over.
*/
public int getCurrentPage() {
return mCurrentPage;
@@ -402,7 +384,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
/**
* Returns the index of page to be shown immediately afterwards.
*/
- int getNextPage() {
+ public int getNextPage() {
return (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
}
@@ -459,7 +441,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
Math.min(newPage, mTempVisiblePagesRange[1]));
}
// Ensure that it is clamped by the actual set of children in all cases
- validatedPage = Math.max(0, Math.min(validatedPage, getPageCount() - 1));
+ validatedPage = Utilities.boundInRange(validatedPage, 0, getPageCount() - 1);
return validatedPage;
}
@@ -606,9 +588,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
super.scrollTo(x, y);
}
- mTouchX = x;
- mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
-
// Update the last motion events when scrolling
if (isReordering(true)) {
float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY);
@@ -836,6 +815,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
setMeasuredDimension(scaledWidthSize, scaledHeightSize);
}
+ @SuppressLint("DrawAllocation")
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (getChildCount() == 0) {
@@ -874,9 +854,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
childTop = offsetY;
} else {
childTop = offsetY + getPaddingTop() + mInsets.top;
- if (mCenterPagesVertically) {
- childTop += (getViewportHeight() - mInsets.top - mInsets.bottom - verticalPadding - child.getMeasuredHeight()) / 2;
- }
+ childTop += (getViewportHeight() - mInsets.top - mInsets.bottom - verticalPadding - child.getMeasuredHeight()) / 2;
}
final int childWidth = child.getMeasuredWidth();
@@ -1064,11 +1042,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
if (pageCount > 0) {
int viewportWidth = getViewportWidth();
- int curScreen = 0;
+ int lastVisiblePageIndex = 0;
- int count = getChildCount();
- for (int i = 0; i < count; i++) {
- View currPage = getPageAt(i);
+ for (int currPageIndex = 0; currPageIndex < pageCount; currPageIndex++) {
+ View currPage = getPageAt(currPageIndex);
sTmpIntPoint[0] = 0;
Utilities.getDescendantCoordRelativeToParent(currPage, this, sTmpIntPoint, false);
@@ -1089,13 +1066,13 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
break;
}
}
- curScreen = i;
if (range[0] < 0) {
- range[0] = curScreen;
+ range[0] = currPageIndex;
}
+ lastVisiblePageIndex = currPageIndex;
}
- range[1] = curScreen;
+ range[1] = lastVisiblePageIndex;
} else {
range[0] = -1;
range[1] = -1;
@@ -1136,8 +1113,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
for (int i = pageCount - 1; i >= 0; i--) {
final View v = getPageAt(i);
if (v == mDragView) continue;
- if (mForceDrawAllChildrenNextFrame ||
- (leftScreen <= i && i <= rightScreen && shouldDrawChild(v))) {
+ if (leftScreen <= i && i <= rightScreen && shouldDrawChild(v)) {
drawChild(canvas, v, drawingTime);
}
}
@@ -1146,7 +1122,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
drawChild(canvas, mDragView, drawingTime);
}
- mForceDrawAllChildrenNextFrame = false;
canvas.restore();
}
}
@@ -1461,8 +1436,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
mTotalMotionX += Math.abs(mLastMotionX - x);
mLastMotionX = x;
mLastMotionXRemainder = 0;
- mTouchX = getViewportOffsetX() + getScrollX();
- mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
onScrollInteractionBegin();
pageBeginMoving();
}
@@ -1661,8 +1634,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
// keep the remainder because we are actually testing if we've moved from the last
// scrolled position (which is discrete).
if (Math.abs(deltaX) >= 1.0f) {
- mTouchX += deltaX;
- mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
scrollBy((int) deltaX, 0);
mLastMotionX = x;
mLastMotionXRemainder = deltaX - (int) deltaX;
@@ -1722,16 +1693,14 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
// Animate the view translation from its old position to its new
// position
- AnimatorSet anim = (AnimatorSet) v.getTag(ANIM_TAG_KEY);
+ ObjectAnimator anim = (ObjectAnimator) v.getTag();
if (anim != null) {
anim.cancel();
}
v.setTranslationX(oldX - newX);
- anim = new AnimatorSet();
+ anim = LauncherAnimUtils.ofFloat(v, View.TRANSLATION_X, 0);
anim.setDuration(REORDERING_REORDER_REPOSITION_DURATION);
- anim.playTogether(
- ObjectAnimator.ofFloat(v, "translationX", 0f));
anim.start();
v.setTag(anim);
}
@@ -2176,13 +2145,12 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
// Animate the drag view back to the original position
private void animateDragViewToOriginalPosition() {
if (mDragView != null) {
- AnimatorSet anim = new AnimatorSet();
- anim.setDuration(REORDERING_DROP_REPOSITION_DURATION);
- anim.playTogether(
- ObjectAnimator.ofFloat(mDragView, "translationX", 0f),
- ObjectAnimator.ofFloat(mDragView, "translationY", 0f),
- ObjectAnimator.ofFloat(mDragView, "scaleX", 1f),
- ObjectAnimator.ofFloat(mDragView, "scaleY", 1f));
+ Animator anim = new LauncherViewPropertyAnimator(mDragView)
+ .translationX(0)
+ .translationY(0)
+ .scaleX(1)
+ .scaleY(1)
+ .setDuration(REORDERING_DROP_REPOSITION_DURATION);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -2279,9 +2247,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
animateDragViewToOriginalPosition();
}
- private static final int ANIM_TAG_KEY = 100;
-
/* Accessibility */
+ @SuppressWarnings("deprecation")
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
@@ -2341,7 +2308,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
protected String getCurrentPageDescription() {
- return String.format(getContext().getString(R.string.default_scroll_format),
+ return getContext().getString(R.string.default_scroll_format,
getNextPage() + 1, getChildCount());
}
diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java
index 772a334b9..9bda8b8f8 100644
--- a/src/com/android/launcher3/SearchDropTargetBar.java
+++ b/src/com/android/launcher3/SearchDropTargetBar.java
@@ -23,16 +23,15 @@ import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
-import android.view.animation.AccelerateInterpolator;
-import android.widget.FrameLayout;
+import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.util.Thunk;
/*
- * Ths bar will manage the transition between the QSB search bar and the delete drop
- * targets so that each of the individual IconDropTargets don't have to.
+ * This bar will manage the transition between the QSB search bar and the delete/uninstall drop
+ * targets so that each of the individual ButtonDropTargets don't have to.
*/
-public class SearchDropTargetBar extends FrameLayout implements DragController.DragListener {
+public class SearchDropTargetBar extends BaseDropTargetBar {
/** The different states that the search bar space can be in. */
public enum State {
@@ -57,21 +56,13 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
}
}
- private static int DEFAULT_DRAG_FADE_DURATION = 175;
- private LauncherViewPropertyAnimator mDropTargetBarAnimator;
private LauncherViewPropertyAnimator mQSBSearchBarAnimator;
- private static final AccelerateInterpolator sAccelerateInterpolator =
- new AccelerateInterpolator();
private State mState = State.SEARCH_BAR;
@Thunk View mQSB;
- @Thunk View mDropTargetBar;
- private boolean mDeferOnDragEnd = false;
- @Thunk boolean mAccessibilityEnabled = false;
// Drop targets
- private ButtonDropTarget mInfoDropTarget;
private ButtonDropTarget mDeleteDropTarget;
private ButtonDropTarget mUninstallDropTarget;
@@ -83,61 +74,48 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
super(context, attrs, defStyle);
}
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ // Get the individual components
+ mDeleteDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.delete_target_text);
+ mUninstallDropTarget = (ButtonDropTarget) mDropTargetBar
+ .findViewById(R.id.uninstall_target_text);
+
+ mDeleteDropTarget.setDropTargetBar(this);
+ mUninstallDropTarget.setDropTargetBar(this);
+ }
+
+ @Override
public void setup(Launcher launcher, DragController dragController) {
dragController.addDragListener(this);
dragController.setFlingToDeleteDropTarget(mDeleteDropTarget);
- dragController.addDragListener(mInfoDropTarget);
dragController.addDragListener(mDeleteDropTarget);
dragController.addDragListener(mUninstallDropTarget);
- dragController.addDropTarget(mInfoDropTarget);
dragController.addDropTarget(mDeleteDropTarget);
dragController.addDropTarget(mUninstallDropTarget);
- mInfoDropTarget.setLauncher(launcher);
mDeleteDropTarget.setLauncher(launcher);
mUninstallDropTarget.setLauncher(launcher);
}
@Override
- protected void onFinishInflate() {
- super.onFinishInflate();
+ public void showDropTargets() {
+ animateToState(State.DROP_TARGET, DEFAULT_DRAG_FADE_DURATION);
+ }
- // Get the individual components
- mDropTargetBar = findViewById(R.id.drag_target_bar);
- mInfoDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.info_target_text);
- mDeleteDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.delete_target_text);
- mUninstallDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.uninstall_target_text);
-
- mInfoDropTarget.setSearchDropTargetBar(this);
- mDeleteDropTarget.setSearchDropTargetBar(this);
- mUninstallDropTarget.setSearchDropTargetBar(this);
-
- // Create the various fade animations
- mDropTargetBar.setAlpha(0f);
- mDropTargetBarAnimator = new LauncherViewPropertyAnimator(mDropTargetBar);
- mDropTargetBarAnimator.setInterpolator(sAccelerateInterpolator);
- mDropTargetBarAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- // Ensure that the view is visible for the animation
- mDropTargetBar.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mDropTargetBar != null) {
- AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
- }
- }
- });
+ @Override
+ public void hideDropTargets() {
+ animateToState(State.SEARCH_BAR, DEFAULT_DRAG_FADE_DURATION);
}
public void setQsbSearchBar(View qsb) {
mQSB = qsb;
if (mQSB != null) {
- // Update the search ber animation
+ // Update the search bar animation
mQSBSearchBarAnimator = new LauncherViewPropertyAnimator(mQSB);
mQSBSearchBarAnimator.setInterpolator(sAccelerateInterpolator);
mQSBSearchBarAnimator.addListener(new AnimatorListenerAdapter() {
@@ -182,51 +160,6 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
}
/**
- * Convenience method to animate the alpha of a view using hardware layers.
- */
- private void animateViewAlpha(LauncherViewPropertyAnimator animator, View v, float alpha,
- int duration) {
- if (v == null) {
- return;
- }
-
- animator.cancel();
- if (Float.compare(v.getAlpha(), alpha) != 0) {
- if (duration > 0) {
- animator.alpha(alpha).withLayer().setDuration(duration).start();
- } else {
- v.setAlpha(alpha);
- AlphaUpdateListener.updateVisibility(v, mAccessibilityEnabled);
- }
- }
- }
-
- /*
- * DragController.DragListener implementation
- */
- @Override
- public void onDragStart(DragSource source, Object info, int dragAction) {
- animateToState(State.DROP_TARGET, DEFAULT_DRAG_FADE_DURATION);
- }
-
- /**
- * This is called to defer hiding the delete drop target until the drop animation has completed,
- * instead of hiding immediately when the drag has ended.
- */
- public void deferOnDragEnd() {
- mDeferOnDragEnd = true;
- }
-
- @Override
- public void onDragEnd() {
- if (!mDeferOnDragEnd) {
- animateToState(State.SEARCH_BAR, DEFAULT_DRAG_FADE_DURATION);
- } else {
- mDeferOnDragEnd = false;
- }
- }
-
- /**
* @return the bounds of the QSB search bar.
*/
public Rect getSearchBarBounds() {
@@ -245,11 +178,11 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
}
}
+ @Override
public void enableAccessibleDrag(boolean enable) {
if (mQSB != null) {
mQSB.setVisibility(enable ? View.GONE : View.VISIBLE);
}
- mInfoDropTarget.enableAccessibleDrag(enable);
mDeleteDropTarget.enableAccessibleDrag(enable);
mUninstallDropTarget.enableAccessibleDrag(enable);
}
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 56282fe97..20c27735e 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -18,8 +18,6 @@ package com.android.launcher3;
import android.app.WallpaperManager;
import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
@@ -42,7 +40,6 @@ public class ShortcutAndWidgetContainer extends ViewGroup {
private int mHeightGap;
private int mCountX;
- private int mCountY;
private Launcher mLauncher;
@@ -61,7 +58,6 @@ public class ShortcutAndWidgetContainer extends ViewGroup {
mWidthGap = widthGap;
mHeightGap = heightGap;
mCountX = countX;
- mCountY = countY;
}
public View getChildAt(int x, int y) {
@@ -79,24 +75,6 @@ public class ShortcutAndWidgetContainer extends ViewGroup {
}
@Override
- protected void dispatchDraw(Canvas canvas) {
- @SuppressWarnings("all") // suppress dead code warning
- final boolean debug = false;
- if (debug) {
- // Debug drawing for hit space
- Paint p = new Paint();
- p.setColor(0x6600FF00);
- for (int i = getChildCount() - 1; i >= 0; i--) {
- final View child = getChildAt(i);
- final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
-
- canvas.drawRect(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height, p);
- }
- }
- super.dispatchDraw(canvas);
- }
-
- @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
@@ -237,7 +215,6 @@ public class ShortcutAndWidgetContainer extends ViewGroup {
}
}
- @Override
protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
super.setChildrenDrawnWithCacheEnabled(enabled);
}
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 6bdcb4bf1..baa3cd02c 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -29,7 +29,6 @@ import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
import java.util.ArrayList;
-import java.util.Arrays;
/**
* Represents a launchable icon on the workspaces and in folders.
@@ -71,7 +70,7 @@ public class ShortcutInfo extends ItemInfo {
/**
* The intent used to start the application.
*/
- Intent intent;
+ public Intent intent;
/**
* Indicates whether the icon comes from an application's resource (if false)
@@ -238,16 +237,7 @@ public class ShortcutInfo extends ItemInfo {
return "ShortcutInfo(title=" + title + "intent=" + intent + "id=" + this.id
+ " type=" + this.itemType + " container=" + this.container + " screen=" + screenId
+ " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX + " spanY=" + spanY
- + " dropPos=" + Arrays.toString(dropPos) + " user=" + user + ")";
- }
-
- public static void dumpShortcutInfoList(String tag, String label,
- ArrayList<ShortcutInfo> list) {
- Log.d(tag, label + " size=" + list.size());
- for (ShortcutInfo info: list) {
- Log.d(tag, " title=\"" + info.title + " icon=" + info.mIcon
- + " customIcon=" + info.customIcon);
- }
+ + " user=" + user + ")";
}
public ComponentName getTargetComponent() {
diff --git a/src/com/android/launcher3/SimpleOnStylusPressListener.java b/src/com/android/launcher3/SimpleOnStylusPressListener.java
new file mode 100644
index 000000000..6b97dcee6
--- /dev/null
+++ b/src/com/android/launcher3/SimpleOnStylusPressListener.java
@@ -0,0 +1,25 @@
+package com.android.launcher3;
+
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.launcher3.StylusEventHelper.StylusButtonListener;
+
+/**
+ * Simple listener that performs a long click on the view after a stylus button press.
+ */
+public class SimpleOnStylusPressListener implements StylusButtonListener {
+ private View mView;
+
+ public SimpleOnStylusPressListener(View view) {
+ mView = view;
+ }
+
+ public boolean onPressed(MotionEvent event) {
+ return mView.isLongClickable() && mView.performLongClick();
+ }
+
+ public boolean onReleased(MotionEvent event) {
+ return false;
+ }
+} \ No newline at end of file
diff --git a/src/com/android/launcher3/StartupReceiver.java b/src/com/android/launcher3/StartupReceiver.java
deleted file mode 100644
index 65f913fdf..000000000
--- a/src/com/android/launcher3/StartupReceiver.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.android.launcher3;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-public class StartupReceiver extends BroadcastReceiver {
-
- static final String SYSTEM_READY = "com.android.launcher3.SYSTEM_READY";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- context.sendStickyBroadcast(new Intent(SYSTEM_READY));
- }
-}
diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java
index 83bca855a..59feeb7e2 100644
--- a/src/com/android/launcher3/Stats.java
+++ b/src/com/android/launcher3/Stats.java
@@ -25,6 +25,8 @@ import android.util.Log;
import android.view.View;
import android.view.ViewParent;
+import com.android.launcher3.config.ProviderConfig;
+
public class Stats {
/**
@@ -71,7 +73,7 @@ public class Stats {
if (provider != null) {
provider.fillInLaunchSourceData(v, sourceData);
- } else if (LauncherAppState.isDogfoodBuild()) {
+ } else if (ProviderConfig.IS_DOGFOOD_BUILD) {
throw new RuntimeException("Expected LaunchSourceProvider");
}
}
diff --git a/src/com/android/launcher3/StylusEventHelper.java b/src/com/android/launcher3/StylusEventHelper.java
index e03273a6e..d5fc0fad4 100644
--- a/src/com/android/launcher3/StylusEventHelper.java
+++ b/src/com/android/launcher3/StylusEventHelper.java
@@ -6,55 +6,82 @@ import android.view.ViewConfiguration;
/**
* Helper for identifying when a stylus touches a view while the primary stylus button is pressed.
- * This can occur in {@value MotionEvent#ACTION_DOWN} or {@value MotionEvent#ACTION_MOVE}. On a
- * stylus button press this performs the view's {@link View#performLongClick()} method, if the view
- * is long clickable.
+ * This can occur in {@value MotionEvent#ACTION_DOWN} or {@value MotionEvent#ACTION_MOVE}.
*/
public class StylusEventHelper {
- private boolean mIsButtonPressed;
- private View mView;
- public StylusEventHelper(View view) {
- mView = view;
+ /**
+ * Implement this interface to receive callbacks for a stylus button press and release.
+ */
+ public interface StylusButtonListener {
+ /**
+ * Called when the stylus button is pressed.
+ *
+ * @param event The MotionEvent that the button press occurred for.
+ * @return Whether the event was handled.
+ */
+ public boolean onPressed(MotionEvent event);
+
+ /**
+ * Called when the stylus button is released after a button press. This is also called if
+ * the event is canceled or the stylus is lifted off the screen.
+ *
+ * @param event The MotionEvent the button release occurred for.
+ * @return Whether the event was handled.
+ */
+ public boolean onReleased(MotionEvent event);
}
+ private boolean mIsButtonPressed;
+ private View mView;
+ private StylusButtonListener mListener;
+ private final float mSlop;
+
/**
- * Call this in onTouchEvent method of a view to identify a stylus button press and perform a
- * long click (if the view is long clickable).
+ * Constructs a helper for listening to stylus button presses and releases. Ensure that {
+ * {@link #onMotionEvent(MotionEvent)} and {@link #onGenericMotionEvent(MotionEvent)} are called on
+ * the helper to correctly identify stylus events.
*
- * @param event The event to check for a stylus button press.
- * @return Whether a stylus event occurred and was handled.
+ * @param listener The listener to call for stylus events.
+ * @param view Optional view associated with the touch events.
*/
- public boolean checkAndPerformStylusEvent(MotionEvent event) {
- final float slop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop();
-
- if (!mView.isLongClickable()) {
- // We don't do anything unless the view is long clickable.
- return false;
+ public StylusEventHelper(StylusButtonListener listener, View view) {
+ mListener = listener;
+ mView = view;
+ if (mView != null) {
+ mSlop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop();
+ } else {
+ mSlop = ViewConfiguration.getTouchSlop();
}
+ }
+ public boolean onMotionEvent(MotionEvent event) {
final boolean stylusButtonPressed = isStylusButtonPressed(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
- mIsButtonPressed = false;
- if (stylusButtonPressed && mView.performLongClick()) {
- mIsButtonPressed = true;
- return true;
+ mIsButtonPressed = stylusButtonPressed;
+ if (mIsButtonPressed) {
+ return mListener.onPressed(event);
}
break;
case MotionEvent.ACTION_MOVE:
- if (Utilities.pointInView(mView, event.getX(), event.getY(), slop)) {
- if (!mIsButtonPressed && stylusButtonPressed && mView.performLongClick()) {
- mIsButtonPressed = true;
- return true;
- } else if (mIsButtonPressed && !stylusButtonPressed) {
- mIsButtonPressed = false;
- }
+ if (!Utilities.pointInView(mView, event.getX(), event.getY(), mSlop)) {
+ return false;
+ }
+ if (!mIsButtonPressed && stylusButtonPressed) {
+ mIsButtonPressed = true;
+ return mListener.onPressed(event);
+ } else if (mIsButtonPressed && !stylusButtonPressed) {
+ mIsButtonPressed = false;
+ return mListener.onReleased(event);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- mIsButtonPressed = false;
+ if (mIsButtonPressed) {
+ mIsButtonPressed = false;
+ return mListener.onReleased(event);
+ }
break;
}
return false;
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index 955d4013c..73881610b 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -8,7 +8,7 @@ import android.os.Bundle;
import android.os.UserManager;
import android.util.AttributeSet;
import android.util.Pair;
-import com.android.launcher3.R;
+
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.util.Thunk;
@@ -32,7 +32,7 @@ public class UninstallDropTarget extends ButtonDropTarget {
}
@Override
- protected boolean supportsDrop(DragSource source, Object info) {
+ protected boolean supportsDrop(DragSource source, ItemInfo info) {
return supportsDrop(getContext(), info);
}
@@ -81,15 +81,18 @@ public class UninstallDropTarget extends ButtonDropTarget {
@Override
void completeDrop(final DragObject d) {
final Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(d.dragInfo);
- final UserHandleCompat user = ((ItemInfo) d.dragInfo).user;
- if (startUninstallActivity(mLauncher, d.dragInfo)) {
+ final UserHandleCompat user = d.dragInfo.user;
+ if (startActivityWithUninstallAffordance(d)) {
final Runnable checkIfUninstallWasSuccess = new Runnable() {
@Override
public void run() {
- String packageName = componentInfo.first.getPackageName();
- boolean uninstallSuccessful = !AllAppsList.packageHasActivities(
- getContext(), packageName, user);
+ boolean uninstallSuccessful = false;
+ if (componentInfo != null) {
+ String packageName = componentInfo.first.getPackageName();
+ uninstallSuccessful = !AllAppsList.packageHasActivities(
+ getContext(), packageName, user);
+ }
sendUninstallResult(d.dragSource, uninstallSuccessful);
}
};
@@ -99,9 +102,13 @@ public class UninstallDropTarget extends ButtonDropTarget {
}
}
- public static boolean startUninstallActivity(Launcher launcher, Object info) {
+ protected boolean startActivityWithUninstallAffordance(DragObject d) {
+ return startUninstallActivity(mLauncher, d.dragInfo);
+ }
+
+ public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) {
final Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(info);
- final UserHandleCompat user = ((ItemInfo) info).user;
+ final UserHandleCompat user = info.user;
return launcher.startApplicationUninstallActivity(
componentInfo.first, componentInfo.second, user);
}
@@ -115,7 +122,7 @@ public class UninstallDropTarget extends ButtonDropTarget {
/**
* Interface defining an object that can provide uninstallable drag objects.
*/
- public static interface UninstallSource {
+ public interface UninstallSource {
/**
* A pending uninstall operation was complete.
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 0a7028603..a102203e8 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -61,10 +61,12 @@ import android.view.View;
import android.widget.Toast;
import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.util.IconNormalizer;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Set;
@@ -139,11 +141,9 @@ public final class Utilities {
return Log.isLoggable(propertyName, Log.VERBOSE);
}
- public static boolean isAllowRotationPrefEnabled(Context context, boolean multiProcess) {
- SharedPreferences sharedPrefs = context.getSharedPreferences(
- LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE | (multiProcess ?
- Context.MODE_MULTI_PROCESS : 0));
- boolean allowRotationPref = sharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false);
+ public static boolean isAllowRotationPrefEnabled(Context context) {
+ boolean allowRotationPref = getPrefs(context)
+ .getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false);
return sForceEnableRotation || allowRotationPref;
}
@@ -151,6 +151,18 @@ public final class Utilities {
return sForceEnableRotation || context.getResources().getBoolean(R.bool.allow_rotation);
}
+ public static boolean isNycOrAbove() {
+ // TODO(vadimt): Replace using reflection with looking at the API version once
+ // Build.VERSION.SDK_INT gets bumped to 24. b/22942492.
+ try {
+ View.class.getDeclaredField("DRAG_FLAG_OPAQUE");
+ // View.DRAG_FLAG_OPAQUE doesn't exist in M-release, so it's an indication of N+.
+ return true;
+ } catch (NoSuchFieldException e) {
+ return false;
+ }
+ }
+
public static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) {
byte[] data = c.getBlob(iconIndex);
try {
@@ -204,7 +216,7 @@ public final class Utilities {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static Bitmap createBadgedIconBitmap(
Drawable icon, UserHandleCompat user, Context context) {
- float scale = LauncherAppState.isDogfoodBuild() ?
+ float scale = ProviderConfig.IS_DOGFOOD_BUILD ?
IconNormalizer.getInstance().getScale(icon) : 1;
Bitmap bitmap = createIconBitmap(icon, context, scale);
if (Utilities.ATLEAST_LOLLIPOP && user != null
@@ -398,15 +410,6 @@ public final class Utilities {
localY < (v.getHeight() + slop);
}
- public static void scaleRect(Rect r, float scale) {
- if (scale != 1.0f) {
- r.left = (int) (r.left * scale + 0.5f);
- r.top = (int) (r.top * scale + 0.5f);
- r.right = (int) (r.right * scale + 0.5f);
- r.bottom = (int) (r.bottom * scale + 0.5f);
- }
- }
-
public static int[] getCenterDeltaInScreenSpace(View v0, View v1, int[] delta) {
v0.getLocationInWindow(sLoc0);
v1.getLocationInWindow(sLoc1);
@@ -427,11 +430,18 @@ public final class Utilities {
}
public static void scaleRectAboutCenter(Rect r, float scale) {
- int cx = r.centerX();
- int cy = r.centerY();
- r.offset(-cx, -cy);
- Utilities.scaleRect(r, scale);
- r.offset(cx, cy);
+ if (scale != 1.0f) {
+ int cx = r.centerX();
+ int cy = r.centerY();
+ r.offset(-cx, -cy);
+
+ r.left = (int) (r.left * scale + 0.5f);
+ r.top = (int) (r.top * scale + 0.5f);
+ r.right = (int) (r.right * scale + 0.5f);
+ r.bottom = (int) (r.bottom * scale + 0.5f);
+
+ r.offset(cx, cy);
+ }
}
public static void startActivityForResultSafely(
@@ -574,16 +584,6 @@ public final class Utilities {
return null;
}
- @TargetApi(Build.VERSION_CODES.KITKAT)
- public static boolean isViewAttachedToWindow(View v) {
- if (ATLEAST_KITKAT) {
- return v.isAttachedToWindow();
- } else {
- // A proxy call which returns null, if the view is not attached to the window.
- return v.getKeyDispatcherState() != null;
- }
- }
-
/**
* Returns a widget with category {@link AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX}
* provided by the same package which is set to be global search activity.
@@ -719,7 +719,7 @@ public final class Utilities {
}
public static void assertWorkerThread() {
- if (LauncherAppState.isDogfoodBuild() &&
+ if (ProviderConfig.IS_DOGFOOD_BUILD &&
(LauncherModel.sWorkerThread.getThreadId() != Process.myTid())) {
throw new IllegalStateException();
}
@@ -769,6 +769,28 @@ public final class Utilities {
return String.format(Locale.ENGLISH, "%s IN (%s)", columnName, TextUtils.join(", ", values));
}
+ public static boolean isBootCompleted() {
+ try {
+ Class clazz = Class.forName("android.os.SystemProperties");
+ Method getter = clazz.getDeclaredMethod("get", String.class);
+ String value = (String) getter.invoke(null, "sys.boot_completed");
+ return "1".equals(value);
+ } catch (Exception e) {
+ Log.d(TAG, "Unable to read system properties");
+ // Assume that boot has completed
+ return true;
+ }
+ }
+
+ /**
+ * Ensures that a value is within given bounds. Specifically:
+ * If value is less than lowerBound, return lowerBound; else if value is greater than upperBound,
+ * return upperBound; else return value unchanged.
+ */
+ public static int boundInRange(int value, int lowerBound, int upperBound) {
+ return Math.max(lowerBound, Math.min(value, upperBound));
+ }
+
/**
* Wraps a message with a TTS span, so that a different message is spoken than
* what is getting displayed.
diff --git a/src/com/android/launcher3/WallpaperChangedReceiver.java b/src/com/android/launcher3/WallpaperChangedReceiver.java
deleted file mode 100644
index c24fbffc6..000000000
--- a/src/com/android/launcher3/WallpaperChangedReceiver.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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.launcher3;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-public class WallpaperChangedReceiver extends BroadcastReceiver {
- public void onReceive(Context context, Intent data) {
- LauncherAppState.getInstance().onWallpaperChanged();
- }
-}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 94cbc7a12..a73a13111 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -28,12 +28,9 @@ import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
-import android.content.SharedPreferences;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PointF;
@@ -50,7 +47,6 @@ import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.Choreographer;
-import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -62,15 +58,21 @@ import android.widget.TextView;
import com.android.launcher3.FolderIcon.FolderRingAnimator;
import com.android.launcher3.Launcher.CustomContentCallbacks;
import com.android.launcher3.Launcher.LauncherOverlay;
-import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.UninstallDropTarget.UninstallSource;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragScroller;
+import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.dragndrop.SpringLoadedDragController;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.WallpaperOffsetInterpolator;
import com.android.launcher3.util.WallpaperUtils;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -93,13 +95,17 @@ public class Workspace extends PagedView
private static boolean ENFORCE_DRAG_EVENT_ORDER = false;
- protected static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
- protected static final int FADE_EMPTY_SCREEN_DURATION = 150;
+ private static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
+ private static final int FADE_EMPTY_SCREEN_DURATION = 150;
private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
- static final boolean MAP_NO_RECURSE = false;
- static final boolean MAP_RECURSE = true;
+ private static final boolean MAP_NO_RECURSE = false;
+ private static final boolean MAP_RECURSE = true;
+
+ // The screen id used for the empty screen always present to the right.
+ public final static long EXTRA_EMPTY_SCREEN_ID = -201;
+ private final static long CUSTOM_CONTENT_SCREEN_ID = -301;
private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200;
private long mTouchDownTime = -1;
@@ -107,17 +113,9 @@ public class Workspace extends PagedView
private LayoutTransition mLayoutTransition;
@Thunk final WallpaperManager mWallpaperManager;
- @Thunk IBinder mWindowToken;
-
- private int mOriginalDefaultPage;
- private int mDefaultPage;
private ShortcutAndWidgetContainer mDragSourceInternal;
- // The screen id used for the empty screen always present to the right.
- final static long EXTRA_EMPTY_SCREEN_ID = -201;
- private final static long CUSTOM_CONTENT_SCREEN_ID = -301;
-
@Thunk LongArrayMap<CellLayout> mWorkspaceScreens = new LongArrayMap<>();
@Thunk ArrayList<Long> mScreenOrder = new ArrayList<Long>();
@@ -137,9 +135,6 @@ public class Workspace extends PagedView
private int mDragOverX = -1;
private int mDragOverY = -1;
- static Rect mLandscapeCellLayoutMetrics = null;
- static Rect mPortraitCellLayoutMetrics = null;
-
CustomContentCallbacks mCustomContentCallbacks;
boolean mCustomContentShowing;
private float mLastCustomContentScrollProgress = -1f;
@@ -165,12 +160,11 @@ public class Workspace extends PagedView
// These are temporary variables to prevent having to allocate a new object just to
// return an (x, y) value from helper functions. Do NOT use them to maintain other state.
- private int[] mTempCell = new int[2];
- private int[] mTempPt = new int[2];
- private int[] mTempEstimate = new int[2];
+ private static final Rect sTempRect = new Rect();
+ private final int[] mTempXY = new int[2];
@Thunk float[] mDragViewVisualCenter = new float[2];
private float[] mTempCellLayoutCenterCoordinates = new float[2];
- private Matrix mTempInverseMatrix = new Matrix();
+ private int[] mTempVisiblePagesRange = new int[2];
private SpringLoadedDragController mSpringLoadedDragController;
private float mSpringLoadedShrinkFactor;
@@ -201,7 +195,6 @@ public class Workspace extends PagedView
private boolean mIsSwitchingState = false;
boolean mAnimatingViewIntoPlace = false;
- boolean mIsDragOccuring = false;
boolean mChildrenLayersEnabled = true;
private boolean mStripScreensOnPageStopMoving = false;
@@ -211,20 +204,13 @@ public class Workspace extends PagedView
private HolographicOutlineHelper mOutlineHelper;
@Thunk Bitmap mDragOutline = null;
- private static final Rect sTempRect = new Rect();
- private final int[] mTempXY = new int[2];
- private int[] mTempVisiblePagesRange = new int[2];
public static final int DRAG_BITMAP_PADDING = 2;
private boolean mWorkspaceFadeInAdjacentScreens;
- WallpaperOffsetInterpolator mWallpaperOffset;
- @Thunk boolean mWallpaperIsLiveWallpaper;
- @Thunk int mNumPagesForWallpaperParallax;
- @Thunk float mLastSetWallpaperOffsetSteps = 0;
+ final WallpaperOffsetInterpolator mWallpaperOffset;
@Thunk Runnable mDelayedResizeRunnable;
private Runnable mDelayedSnapToPageRunnable;
- private Point mDisplaySize = new Point();
// Variables relating to the creation of user folders by hovering shortcuts over shortcuts
private static final int FOLDER_CREATION_TIMEOUT = 0;
@@ -278,19 +264,13 @@ public class Workspace extends PagedView
boolean mStartedSendingScrollEvents;
boolean mShouldSendPageSettled;
int mLastOverlaySroll = 0;
+ private boolean mForceDrawAdjacentPages = false;
// Handles workspace state transitions
private WorkspaceStateTransitionAnimation mStateTransitionAnimation;
private AccessibilityDelegate mPagesAccessibilityDelegate;
- private final Runnable mBindPages = new Runnable() {
- @Override
- public void run() {
- mLauncher.getModel().bindRemainingSynchronousPages();
- }
- };
-
/**
* Used to inflate the Workspace from XML.
*
@@ -321,14 +301,12 @@ public class Workspace extends PagedView
mFadeInAdjacentScreens = false;
mWallpaperManager = WallpaperManager.getInstance(context);
- TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.Workspace, defStyle, 0);
+ mWallpaperOffset = new WallpaperOffsetInterpolator(this);
+
mSpringLoadedShrinkFactor =
res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
mOverviewModeShrinkFactor =
res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100f;
- mOriginalDefaultPage = mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
- a.recycle();
setOnHierarchyChangeListener(this);
setHapticFeedbackEnabled(false);
@@ -382,12 +360,11 @@ public class Workspace extends PagedView
}
@Override
- public void onDragStart(final DragSource source, Object info, int dragAction) {
+ public void onDragStart(final DragSource source, ItemInfo info, int dragAction) {
if (ENFORCE_DRAG_EVENT_ORDER) {
enfoceDragParity("onDragStart", 0, 0);
}
- mIsDragOccuring = true;
updateChildrenLayersEnabled(false);
mLauncher.lockScreenOrientation();
mLauncher.onInteractionBegin();
@@ -418,7 +395,6 @@ public class Workspace extends PagedView
removeExtraEmptyScreen(true, mDragSourceInternal != null);
}
- mIsDragOccuring = false;
updateChildrenLayersEnabled(false);
mLauncher.unlockScreenOrientation(false);
@@ -429,11 +405,15 @@ public class Workspace extends PagedView
mLauncher.onInteractionEnd();
}
+ public float getSpringLoadedShrinkFactor() {
+ return mSpringLoadedShrinkFactor;
+ }
+
/**
* Initializes various states for this workspace.
*/
protected void initWorkspace() {
- mCurrentPage = mDefaultPage;
+ mCurrentPage = getDefaultPage();
LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = mLauncher.getDeviceProfile();
mIconCache = app.getIconCache();
@@ -445,10 +425,6 @@ public class Workspace extends PagedView
setMinScale(mOverviewModeShrinkFactor);
setupLayoutTransition();
- mWallpaperOffset = new WallpaperOffsetInterpolator();
- Display display = mLauncher.getWindowManager().getDefaultDisplay();
- display.getSize(mDisplaySize);
-
mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx);
// Set the wallpaper dimensions when Launcher starts up
@@ -457,6 +433,10 @@ public class Workspace extends PagedView
setEdgeGlowColor(getResources().getColor(R.color.workspace_edge_effect_color));
}
+ private int getDefaultPage() {
+ return numCustomPages();
+ }
+
private void setupLayoutTransition() {
// We want to show layout transitions when pages are deleted, to close the gap.
mLayoutTransition = new LayoutTransition();
@@ -589,9 +569,6 @@ public class Workspace extends PagedView
addFullScreenPage(customScreen);
- // Ensure that the current page and default page are maintained.
- mDefaultPage = mOriginalDefaultPage + 1;
-
// Update the custom content hint
if (mRestorePage != INVALID_RESTORE_PAGE) {
mRestorePage = mRestorePage + 1;
@@ -617,9 +594,6 @@ public class Workspace extends PagedView
mCustomContentCallbacks = null;
- // Ensure that the current page and default page are maintained.
- mDefaultPage = mOriginalDefaultPage - 1;
-
// Update the custom content hint
if (mRestorePage != INVALID_RESTORE_PAGE) {
mRestorePage = mRestorePage - 1;
@@ -698,7 +672,6 @@ public class Workspace extends PagedView
private void convertFinalScreenToEmptyScreenIfNecessary() {
if (mLauncher.isWorkspaceLoading()) {
// Invalid and dangerous operation if workspace is loading
- Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
return;
}
@@ -731,7 +704,6 @@ public class Workspace extends PagedView
final int delay, final boolean stripEmptyScreens) {
if (mLauncher.isWorkspaceLoading()) {
// Don't strip empty screens if the workspace is still loading
- Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
return;
}
@@ -817,7 +789,6 @@ public class Workspace extends PagedView
public long commitExtraEmptyScreen() {
if (mLauncher.isWorkspaceLoading()) {
// Invalid and dangerous operation if workspace is loading
- Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
return -1;
}
@@ -826,7 +797,9 @@ public class Workspace extends PagedView
mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
- long newId = LauncherAppState.getLauncherProvider().generateNewScreenId();
+ long newId = LauncherSettings.Settings.call(getContext().getContentResolver(),
+ LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
+ .getLong(LauncherSettings.Settings.EXTRA_VALUE);
mWorkspaceScreens.put(newId, cl);
mScreenOrder.add(newId);
@@ -842,8 +815,7 @@ public class Workspace extends PagedView
}
public CellLayout getScreenWithId(long screenId) {
- CellLayout layout = mWorkspaceScreens.get(screenId);
- return layout;
+ return mWorkspaceScreens.get(screenId);
}
public long getIdForScreen(CellLayout layout) {
@@ -873,7 +845,6 @@ public class Workspace extends PagedView
if (mLauncher.isWorkspaceLoading()) {
// Don't strip empty screens if the workspace is still loading.
// This is dangerous and can result in data loss.
- Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
return;
}
@@ -1033,7 +1004,7 @@ public class Workspace extends PagedView
// TODO: This branch occurs when the workspace is adding views
// outside of the defined grid
// maybe we should be deleting these items from the LauncherModel?
- Launcher.addDumpLog(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout", true);
+ Log.e(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
}
if (!(child instanceof Folder)) {
@@ -1221,7 +1192,7 @@ public class Workspace extends PagedView
}
}
- if (mDelayedResizeRunnable != null) {
+ if (mDelayedResizeRunnable != null && !mIsSwitchingState) {
mDelayedResizeRunnable.run();
mDelayedResizeRunnable = null;
}
@@ -1324,12 +1295,12 @@ public class Workspace extends PagedView
protected void setWallpaperDimension() {
new AsyncTask<Void, Void, Void>() {
public Void doInBackground(Void ... args) {
- String spKey = LauncherFiles.WALLPAPER_CROP_PREFERENCES_KEY;
- SharedPreferences sp =
- mLauncher.getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
- WallpaperUtils.suggestWallpaperDimension(mLauncher.getResources(),
- sp, mLauncher.getWindowManager(), mWallpaperManager,
- mLauncher.overrideWallpaperDimensions());
+ if (Utilities.ATLEAST_KITKAT) {
+ WallpaperUtils.suggestWallpaperDimension(mLauncher);
+ } else {
+ WallpaperUtils.suggestWallpaperDimensionPreK(mLauncher,
+ mLauncher.overrideWallpaperDimensions());
+ }
return null;
}
}.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
@@ -1355,191 +1326,6 @@ public class Workspace extends PagedView
snapToPage(getPageIndexForScreenId(screenId), r);
}
- class WallpaperOffsetInterpolator implements Choreographer.FrameCallback {
- float mFinalOffset = 0.0f;
- float mCurrentOffset = 0.5f; // to force an initial update
- boolean mWaitingForUpdate;
- Choreographer mChoreographer;
- Interpolator mInterpolator;
- boolean mAnimating;
- long mAnimationStartTime;
- float mAnimationStartOffset;
- private final int ANIMATION_DURATION = 250;
- // Don't use all the wallpaper for parallax until you have at least this many pages
- private final int MIN_PARALLAX_PAGE_SPAN = 3;
- int mNumScreens;
-
- public WallpaperOffsetInterpolator() {
- mChoreographer = Choreographer.getInstance();
- mInterpolator = new DecelerateInterpolator(1.5f);
- }
-
- @Override
- public void doFrame(long frameTimeNanos) {
- updateOffset(false);
- }
-
- private void updateOffset(boolean force) {
- if (mWaitingForUpdate || force) {
- mWaitingForUpdate = false;
- if (computeScrollOffset() && mWindowToken != null) {
- try {
- mWallpaperManager.setWallpaperOffsets(mWindowToken,
- mWallpaperOffset.getCurrX(), 0.5f);
- setWallpaperOffsetSteps();
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Error updating wallpaper offset: " + e);
- }
- }
- }
- }
-
- public boolean computeScrollOffset() {
- final float oldOffset = mCurrentOffset;
- if (mAnimating) {
- long durationSinceAnimation = System.currentTimeMillis() - mAnimationStartTime;
- float t0 = durationSinceAnimation / (float) ANIMATION_DURATION;
- float t1 = mInterpolator.getInterpolation(t0);
- mCurrentOffset = mAnimationStartOffset +
- (mFinalOffset - mAnimationStartOffset) * t1;
- mAnimating = durationSinceAnimation < ANIMATION_DURATION;
- } else {
- mCurrentOffset = mFinalOffset;
- }
-
- if (Math.abs(mCurrentOffset - mFinalOffset) > 0.0000001f) {
- scheduleUpdate();
- }
- if (Math.abs(oldOffset - mCurrentOffset) > 0.0000001f) {
- return true;
- }
- return false;
- }
-
- public float wallpaperOffsetForScroll(int scroll) {
- // TODO: do different behavior if it's a live wallpaper?
- // Don't use up all the wallpaper parallax until you have at least
- // MIN_PARALLAX_PAGE_SPAN pages
- int numScrollingPages = getNumScreensExcludingEmptyAndCustom();
- int parallaxPageSpan;
- if (mWallpaperIsLiveWallpaper) {
- parallaxPageSpan = numScrollingPages - 1;
- } else {
- parallaxPageSpan = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages - 1);
- }
- mNumPagesForWallpaperParallax = parallaxPageSpan;
-
- if (getChildCount() <= 1) {
- if (mIsRtl) {
- return 1 - 1.0f/mNumPagesForWallpaperParallax;
- }
- return 0;
- }
-
- // Exclude the leftmost page
- int emptyExtraPages = numEmptyScreensToIgnore();
- int firstIndex = numCustomPages();
- // Exclude the last extra empty screen (if we have > MIN_PARALLAX_PAGE_SPAN pages)
- int lastIndex = getChildCount() - 1 - emptyExtraPages;
- if (mIsRtl) {
- int temp = firstIndex;
- firstIndex = lastIndex;
- lastIndex = temp;
- }
-
- int firstPageScrollX = getScrollForPage(firstIndex);
- int scrollRange = getScrollForPage(lastIndex) - firstPageScrollX;
- if (scrollRange == 0) {
- return 0;
- } else {
- // Sometimes the left parameter of the pages is animated during a layout transition;
- // this parameter offsets it to keep the wallpaper from animating as well
- int adjustedScroll =
- scroll - firstPageScrollX - getLayoutTransitionOffsetForPage(0);
- float offset = Math.min(1, adjustedScroll / (float) scrollRange);
- offset = Math.max(0, offset);
-
- // On RTL devices, push the wallpaper offset to the right if we don't have enough
- // pages (ie if numScrollingPages < MIN_PARALLAX_PAGE_SPAN)
- if (!mWallpaperIsLiveWallpaper && numScrollingPages < MIN_PARALLAX_PAGE_SPAN
- && mIsRtl) {
- return offset * (parallaxPageSpan - numScrollingPages + 1) / parallaxPageSpan;
- }
- return offset * (numScrollingPages - 1) / parallaxPageSpan;
- }
- }
-
- private float wallpaperOffsetForCurrentScroll() {
- return wallpaperOffsetForScroll(getScrollX());
- }
-
- private int numEmptyScreensToIgnore() {
- int numScrollingPages = getChildCount() - numCustomPages();
- if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && hasExtraEmptyScreen()) {
- return 1;
- } else {
- return 0;
- }
- }
-
- private int getNumScreensExcludingEmptyAndCustom() {
- int numScrollingPages = getChildCount() - numEmptyScreensToIgnore() - numCustomPages();
- return numScrollingPages;
- }
-
- public void syncWithScroll() {
- float offset = wallpaperOffsetForCurrentScroll();
- mWallpaperOffset.setFinalX(offset);
- updateOffset(true);
- }
-
- public float getCurrX() {
- return mCurrentOffset;
- }
-
- public float getFinalX() {
- return mFinalOffset;
- }
-
- private void animateToFinal() {
- mAnimating = true;
- mAnimationStartOffset = mCurrentOffset;
- mAnimationStartTime = System.currentTimeMillis();
- }
-
- private void setWallpaperOffsetSteps() {
- // Set wallpaper offset steps (1 / (number of screens - 1))
- float xOffset = 1.0f / mNumPagesForWallpaperParallax;
- if (xOffset != mLastSetWallpaperOffsetSteps) {
- mWallpaperManager.setWallpaperOffsetSteps(xOffset, 1.0f);
- mLastSetWallpaperOffsetSteps = xOffset;
- }
- }
-
- public void setFinalX(float x) {
- scheduleUpdate();
- mFinalOffset = Math.max(0f, Math.min(x, 1.0f));
- if (getNumScreensExcludingEmptyAndCustom() != mNumScreens) {
- if (mNumScreens > 0) {
- // Don't animate if we're going from 0 screens
- animateToFinal();
- }
- mNumScreens = getNumScreensExcludingEmptyAndCustom();
- }
- }
-
- private void scheduleUpdate() {
- if (!mWaitingForUpdate) {
- mChoreographer.postFrameCallback(this);
- mWaitingForUpdate = true;
- }
- }
-
- public void jumpToFinal() {
- mCurrentOffset = mFinalOffset;
- }
- }
-
@Override
public void computeScroll() {
super.computeScroll();
@@ -1567,18 +1353,6 @@ public class Workspace extends PagedView
}
}
- float backgroundAlphaInterpolator(float r) {
- float pivotA = 0.1f;
- float pivotB = 0.4f;
- if (r < pivotA) {
- return 0;
- } else if (r > pivotB) {
- return 1.0f;
- } else {
- return (r - pivotA)/(pivotB - pivotA);
- }
- }
-
private void updatePageAlphaValues(int screenCenter) {
if (mWorkspaceFadeInAdjacentScreens &&
!workspaceInModalState() &&
@@ -1610,6 +1384,7 @@ public class Workspace extends PagedView
setOnClickListener(mLauncher);
}
mLauncher.getSearchDropTargetBar().enableAccessibleDrag(enable);
+ mLauncher.getAppInfoDropTargetBar().enableAccessibleDrag(enable);
mLauncher.getHotseat().getLayout()
.enableAccessibleDrag(enable, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
}
@@ -1681,13 +1456,12 @@ public class Workspace extends PagedView
if (!am.isTouchExplorationEnabled()) {
return null;
}
- OnClickListener listener = new OnClickListener() {
+ return new OnClickListener() {
@Override
public void onClick(View arg0) {
mLauncher.showOverviewMode(true);
}
};
- return listener;
}
@Override
@@ -1699,14 +1473,15 @@ public class Workspace extends PagedView
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mWindowToken = getWindowToken();
+ IBinder windowToken = getWindowToken();
+ mWallpaperOffset.setWindowToken(windowToken);
computeScroll();
- mDragController.setWindowToken(mWindowToken);
+ mDragController.setWindowToken(windowToken);
}
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- mWindowToken = null;
+ mWallpaperOffset.setWindowToken(null);
}
protected void onResume() {
@@ -1724,10 +1499,7 @@ public class Workspace extends PagedView
if (LauncherAppState.getInstance().hasWallpaperChangedSinceLastCheck()) {
setWallpaperDimension();
}
- mWallpaperIsLiveWallpaper = mWallpaperManager.getWallpaperInfo() != null;
- // Force the wallpaper offset steps to be set again, because another app might have changed
- // them
- mLastSetWallpaperOffsetSteps = 0f;
+ mWallpaperOffset.onResume();
}
@Override
@@ -1740,14 +1512,6 @@ public class Workspace extends PagedView
}
@Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- // Call back to LauncherModel to finish binding after the first draw
- post(mBindPages);
- }
-
- @Override
protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
if (!mLauncher.isAppsViewVisible()) {
final Folder openFolder = getOpenFolder();
@@ -1873,8 +1637,18 @@ public class Workspace extends PagedView
updateChildrenLayersEnabled(false);
}
+ @Override
+ protected void getVisiblePages(int[] range) {
+ super.getVisiblePages(range);
+ if (mForceDrawAdjacentPages) {
+ // In overview mode, make sure that the two side pages are visible.
+ range[0] = Utilities.boundInRange(getCurrentPage() - 1, numCustomPages(), range[1]);
+ range[1] = Utilities.boundInRange(getCurrentPage() + 1, range[0], getPageCount() - 1);
+ }
+ }
+
protected void onWallpaperTap(MotionEvent ev) {
- final int[] position = mTempCell;
+ final int[] position = mTempXY;
getLocationOnScreen(position);
int pointerIndex = ev.getActionIndex();
@@ -1938,6 +1712,18 @@ public class Workspace extends PagedView
}
public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, boolean clipAlpha) {
+ // Find a page that has enough space to place this widget (after rearranging/resizing).
+ // Start at the current page and search right (on LTR) until finding a page with enough
+ // space. Since an empty screen is the furthest right, a page must be found.
+ int currentPageInOverview = getPageNearestToCenterOfScreen();
+ for (int pageIndex = currentPageInOverview; pageIndex < getPageCount(); pageIndex++) {
+ CellLayout page = (CellLayout) getPageAt(pageIndex);
+ if (page.hasReorderSolution(info)) {
+ setCurrentPage(pageIndex);
+ break;
+ }
+ }
+
int[] size = estimateItemSize(info, false);
// The outline is used to visualize where the item will land if dropped
@@ -1993,6 +1779,10 @@ public class Workspace extends PagedView
return mState == State.OVERVIEW;
}
+ public void snapToPageFromOverView(int whichPage) {
+ mStateTransitionAnimation.snapToPageFromOverView(whichPage);
+ }
+
int getOverviewModeTranslationY() {
DeviceProfile grid = mLauncher.getDeviceProfile();
Rect workspacePadding = grid.getWorkspacePadding(Utilities.isRtl(getResources()));
@@ -2008,19 +1798,28 @@ public class Workspace extends PagedView
return -workspaceOffsetTopEdge + overviewOffsetTopEdge;
}
+ int getSpringLoadedTranslationY() {
+ return getOverviewModeTranslationY();
+ }
+
/**
* Sets the current workspace {@link State}, returning an animation transitioning the workspace
* to that new state.
*/
- public Animator setStateWithAnimation(State toState, int toPage, boolean animated,
+ public Animator setStateWithAnimation(State toState, boolean animated,
HashMap<View, Integer> layerViews) {
// Create the animation to the new state
Animator workspaceAnim = mStateTransitionAnimation.getAnimationToState(mState,
- toState, toPage, animated, layerViews);
+ toState, animated, layerViews);
// Update the current state
mState = toState;
updateAccessibilityFlags();
+ if (mState == State.OVERVIEW || mState == State.SPRING_LOADED) {
+ // Redraw pages, as we might want to draw pages which were not visible.
+ mForceDrawAdjacentPages = true;
+ invalidate(); // This will call dispatchDraw(), which calls getVisiblePages().
+ }
return workspaceAnim;
}
@@ -2094,6 +1893,7 @@ public class Workspace extends PagedView
mIsSwitchingState = false;
updateChildrenLayersEnabled(false);
showCustomContentIfNecessary();
+ mForceDrawAdjacentPages = false;
}
void updateCustomContentVisibility() {
@@ -2140,19 +1940,17 @@ public class Workspace extends PagedView
* @param padding the horizontal and vertical padding to use when drawing
*/
private static void drawDragView(View v, Canvas destCanvas, int padding) {
- final Rect clipRect = sTempRect;
- v.getDrawingRect(clipRect);
-
- boolean textVisible = false;
-
destCanvas.save();
if (v instanceof TextView) {
Drawable d = getTextViewIcon((TextView) v);
Rect bounds = getDrawableBounds(d);
- clipRect.set(0, 0, bounds.width() + padding, bounds.height() + padding);
destCanvas.translate(padding / 2 - bounds.left, padding / 2 - bounds.top);
d.draw(destCanvas);
} else {
+ final Rect clipRect = sTempRect;
+ v.getDrawingRect(clipRect);
+
+ boolean textVisible = false;
if (v instanceof FolderIcon) {
// For FolderIcons the text can bleed into the icon area, and so we need to
// hide the text completely (which can't be achieved by clipping).
@@ -2330,7 +2128,8 @@ public class Workspace extends PagedView
icon.clearPressedBackground();
}
- if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
+ Object dragObject = child.getTag();
+ if (!(dragObject instanceof ItemInfo)) {
String msg = "Drag started with a view that has no tag set. This "
+ "will cause a crash (issue 11627249) down the line. "
+ "View: " + child + " tag: " + child.getTag();
@@ -2341,11 +2140,14 @@ public class Workspace extends PagedView
mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
}
- DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
- DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale, accessible);
+ DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source,
+ (ItemInfo) dragObject, DragController.DRAG_ACTION_MOVE, dragVisualizeOffset,
+ dragRect, scale, accessible);
dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
b.recycle();
+
+ mLauncher.enterSpringLoadedDragMode();
}
public void beginExternalDragShared(View child, DragSource source) {
@@ -2378,7 +2180,8 @@ public class Workspace extends PagedView
Point dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2);
Rect dragRect = new Rect(0, 0, iconSize, iconSize);
- if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
+ Object dragObject = child.getTag();
+ if (!(dragObject instanceof ItemInfo)) {
String msg = "Drag started with a view that has no tag set. This "
+ "will cause a crash (issue 11627249) down the line. "
+ "View: " + child + " tag: " + child.getTag();
@@ -2386,12 +2189,15 @@ public class Workspace extends PagedView
}
// Start the drag
- DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
- DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale, false);
+ DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source,
+ (ItemInfo) dragObject, DragController.DRAG_ACTION_MOVE, dragVisualizeOffset,
+ dragRect, scale, false);
dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
// Recycle temporary bitmaps
tmpB.recycle();
+
+ mLauncher.enterSpringLoadedDragMode();
}
public boolean transitionStateShouldAllowDrop() {
@@ -2418,7 +2224,7 @@ public class Workspace extends PagedView
if (mLauncher.isHotseatLayout(dropTargetLayout)) {
mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
} else {
- mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
+ mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter);
}
int spanX = 1;
@@ -2428,9 +2234,8 @@ public class Workspace extends PagedView
spanX = dragCellInfo.spanX;
spanY = dragCellInfo.spanY;
} else {
- final ItemInfo dragInfo = (ItemInfo) d.dragInfo;
- spanX = dragInfo.spanX;
- spanY = dragInfo.spanY;
+ spanX = d.dragInfo.spanX;
+ spanY = d.dragInfo.spanY;
}
int minSpanX = spanX;
@@ -2445,12 +2250,12 @@ public class Workspace extends PagedView
mTargetCell);
float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
mDragViewVisualCenter[1], mTargetCell);
- if (mCreateUserFolderOnDrop && willCreateUserFolder((ItemInfo) d.dragInfo,
+ if (mCreateUserFolderOnDrop && willCreateUserFolder(d.dragInfo,
dropTargetLayout, mTargetCell, distance, true)) {
return true;
}
- if (mAddToExistingFolderOnDrop && willAddToExistingUserFolder((ItemInfo) d.dragInfo,
+ if (mAddToExistingFolderOnDrop && willAddToExistingUserFolder(d.dragInfo,
dropTargetLayout, mTargetCell, distance)) {
return true;
}
@@ -2519,14 +2324,14 @@ public class Workspace extends PagedView
return (aboveShortcut && willBecomeShortcut);
}
- boolean willAddToExistingUserFolder(Object dragInfo, CellLayout target, int[] targetCell,
+ boolean willAddToExistingUserFolder(ItemInfo dragInfo, CellLayout target, int[] targetCell,
float distance) {
if (distance > mMaxDistanceForFolderCreation) return false;
View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
return willAddToExistingUserFolder(dragInfo, dropOverView);
}
- boolean willAddToExistingUserFolder(Object dragInfo, View dropOverView) {
+ boolean willAddToExistingUserFolder(ItemInfo dragInfo, View dropOverView) {
if (dropOverView != null) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) {
@@ -2631,11 +2436,11 @@ public class Workspace extends PagedView
if (mLauncher.isHotseatLayout(dropTargetLayout)) {
mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
} else {
- mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
+ mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter);
}
}
- int snapScreen = WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE;
+ int snapScreen = -1;
boolean resizeOnDrop = false;
if (d.dragSource != this) {
final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
@@ -2644,7 +2449,6 @@ public class Workspace extends PagedView
} else if (mDragInfo != null) {
final View cell = mDragInfo.cell;
- Runnable resizeRunnable = null;
if (dropTargetLayout != null && !d.cancelled) {
// Move internally
boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);
@@ -2678,7 +2482,7 @@ public class Workspace extends PagedView
// Aside from the special case where we're dropping a shortcut onto a shortcut,
// we need to find the nearest cell location that is vacant
- ItemInfo item = (ItemInfo) d.dragInfo;
+ ItemInfo item = d.dragInfo;
int minSpanX = item.spanX;
int minSpanY = item.spanY;
if (item.minSpanX > 0 && item.minSpanY > 0) {
@@ -2716,7 +2520,7 @@ public class Workspace extends PagedView
CellLayout parentCell = getParentCellLayoutForView(cell);
if (parentCell != null) {
parentCell.removeView(cell);
- } else if (LauncherAppState.isDogfoodBuild()) {
+ } else if (ProviderConfig.IS_DOGFOOD_BUILD) {
throw new NullPointerException("mDragInfo.cell has null parent");
}
addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
@@ -2741,21 +2545,14 @@ public class Workspace extends PagedView
AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo();
if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE
&& !d.accessibleDrag) {
- final Runnable addResizeFrame = new Runnable() {
- public void run() {
- DragLayer dragLayer = mLauncher.getDragLayer();
- dragLayer.addResizeFrame(info, hostView, cellLayout);
- }
- };
- resizeRunnable = (new Runnable() {
+ mDelayedResizeRunnable = new Runnable() {
public void run() {
- if (!isPageMoving()) {
- addResizeFrame.run();
- } else {
- mDelayedResizeRunnable = addResizeFrame;
+ if (!isPageMoving() && !mIsSwitchingState) {
+ DragLayer dragLayer = mLauncher.getDragLayer();
+ dragLayer.addResizeFrame(info, hostView, cellLayout);
}
}
- });
+ };
}
}
@@ -2772,7 +2569,6 @@ public class Workspace extends PagedView
}
final CellLayout parent = (CellLayout) cell.getParent().getParent();
- final Runnable finalResizeRunnable = resizeRunnable;
// Prepare it to be animated into its new position
// This must be called after the view has been re-parented
final Runnable onCompleteRunnable = new Runnable() {
@@ -2780,9 +2576,6 @@ public class Workspace extends PagedView
public void run() {
mAnimatingViewIntoPlace = false;
updateChildrenLayersEnabled(false);
- if (finalResizeRunnable != null) {
- finalResizeRunnable.run();
- }
}
};
mAnimatingViewIntoPlace = true;
@@ -2796,9 +2589,7 @@ public class Workspace extends PagedView
animateWidgetDrop(info, parent, d.dragView,
onCompleteRunnable, animationType, cell, false);
} else {
- int duration = snapScreen < 0 ?
- WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE :
- ADJACENT_SCREEN_DROP_DURATION;
+ int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
onCompleteRunnable, this);
}
@@ -2832,19 +2623,6 @@ public class Workspace extends PagedView
(int) (mTempXY[1] + scale * boundingLayout.getMeasuredHeight()));
}
- public void getViewLocationRelativeToSelf(View v, int[] location) {
- getLocationInWindow(location);
- int x = location[0];
- int y = location[1];
-
- v.getLocationInWindow(location);
- int vX = location[0];
- int vY = location[1];
-
- location[0] = vX - x;
- location[1] = vY - y;
- }
-
@Override
public void onDragEnter(DragObject d) {
if (ENFORCE_DRAG_EVENT_ORDER) {
@@ -2858,49 +2636,6 @@ public class Workspace extends PagedView
CellLayout layout = getCurrentDropLayout();
setCurrentDropLayout(layout);
setCurrentDragOverlappingLayout(layout);
-
- if (!workspaceInModalState()) {
- mLauncher.getDragLayer().showPageHints();
- }
- }
-
- /** Return a rect that has the cellWidth/cellHeight (left, top), and
- * widthGap/heightGap (right, bottom) */
- static Rect getCellLayoutMetrics(Launcher launcher, int orientation) {
- LauncherAppState app = LauncherAppState.getInstance();
- InvariantDeviceProfile inv = app.getInvariantDeviceProfile();
-
- Display display = launcher.getWindowManager().getDefaultDisplay();
- Point smallestSize = new Point();
- Point largestSize = new Point();
- display.getCurrentSizeRange(smallestSize, largestSize);
- int countX = (int) inv.numColumns;
- int countY = (int) inv.numRows;
- boolean isLayoutRtl = Utilities.isRtl(launcher.getResources());
- if (orientation == CellLayout.LANDSCAPE) {
- if (mLandscapeCellLayoutMetrics == null) {
- Rect padding = inv.landscapeProfile.getWorkspacePadding(isLayoutRtl);
- int width = largestSize.x - padding.left - padding.right;
- int height = smallestSize.y - padding.top - padding.bottom;
- mLandscapeCellLayoutMetrics = new Rect();
- mLandscapeCellLayoutMetrics.set(
- DeviceProfile.calculateCellWidth(width, countX),
- DeviceProfile.calculateCellHeight(height, countY), 0, 0);
- }
- return mLandscapeCellLayoutMetrics;
- } else if (orientation == CellLayout.PORTRAIT) {
- if (mPortraitCellLayoutMetrics == null) {
- Rect padding = inv.portraitProfile.getWorkspacePadding(isLayoutRtl);
- int width = smallestSize.x - padding.left - padding.right;
- int height = largestSize.y - padding.top - padding.bottom;
- mPortraitCellLayoutMetrics = new Rect();
- mPortraitCellLayoutMetrics.set(
- DeviceProfile.calculateCellWidth(width, countX),
- DeviceProfile.calculateCellHeight(height, countY), 0, 0);
- }
- return mPortraitCellLayoutMetrics;
- }
- return null;
}
@Override
@@ -2935,8 +2670,6 @@ public class Workspace extends PagedView
setCurrentDragOverlappingLayout(null);
mSpringLoadedDragController.cancel();
-
- mLauncher.getDragLayer().hidePageHints();
}
private void enfoceDragParity(String event, int update, int expectedValue) {
@@ -3041,41 +2774,27 @@ public class Workspace extends PagedView
*
* Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
* coordinate space. The argument xy is modified with the return result.
- *
- * if cachedInverseMatrix is not null, this method will just use that matrix instead of
- * computing it itself; we use this to avoid redundant matrix inversions in
- * findMatchingPageForDragOver
- *
*/
- void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) {
+ void mapPointFromSelfToChild(View v, float[] xy) {
xy[0] = xy[0] - v.getLeft();
xy[1] = xy[1] - v.getTop();
}
- boolean isPointInSelfOverHotseat(int x, int y, Rect r) {
- if (r == null) {
- r = new Rect();
- }
- mTempPt[0] = x;
- mTempPt[1] = y;
- mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
-
- DeviceProfile grid = mLauncher.getDeviceProfile();
- r = grid.getHotseatRect();
- if (r.contains(mTempPt[0], mTempPt[1])) {
- return true;
- }
- return false;
+ boolean isPointInSelfOverHotseat(int x, int y) {
+ mTempXY[0] = x;
+ mTempXY[1] = y;
+ mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY, true);
+ return mLauncher.getDeviceProfile().isInHotseatRect(mTempXY[0], mTempXY[1]);
}
void mapPointFromSelfToHotseatLayout(Hotseat hotseat, float[] xy) {
- mTempPt[0] = (int) xy[0];
- mTempPt[1] = (int) xy[1];
- mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
- mLauncher.getDragLayer().mapCoordInSelfToDescendent(hotseat.getLayout(), mTempPt);
+ mTempXY[0] = (int) xy[0];
+ mTempXY[1] = (int) xy[1];
+ mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY, true);
+ mLauncher.getDragLayer().mapCoordInSelfToDescendent(hotseat.getLayout(), mTempXY);
- xy[0] = mTempPt[0];
- xy[1] = mTempPt[1];
+ xy[0] = mTempXY[0];
+ xy[1] = mTempXY[1];
}
/*
@@ -3121,10 +2840,7 @@ public class Workspace extends PagedView
CellLayout cl = (CellLayout) getChildAt(i);
final float[] touchXy = {originX, originY};
- // Transform the touch coordinates to the CellLayout's local coordinates
- // If the touch point is within the bounds of the cell layout, we can return immediately
- cl.getMatrix().invert(mTempInverseMatrix);
- mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix);
+ mapPointFromSelfToChild(cl, touchXy);
if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
@@ -3166,11 +2882,10 @@ public class Workspace extends PagedView
// Skip drag over events while we are dragging over side pages
if (mInScrollArea || !transitionStateShouldAllowDrop()) return;
- Rect r = new Rect();
CellLayout layout = null;
- ItemInfo item = (ItemInfo) d.dragInfo;
+ ItemInfo item = d.dragInfo;
if (item == null) {
- if (LauncherAppState.isDogfoodBuild()) {
+ if (ProviderConfig.IS_DOGFOOD_BUILD) {
throw new NullPointerException("DragObject has null info");
}
return;
@@ -3184,7 +2899,7 @@ public class Workspace extends PagedView
// Identify whether we have dragged over a side page
if (workspaceInModalState()) {
if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) {
- if (isPointInSelfOverHotseat(d.x, d.y, r)) {
+ if (isPointInSelfOverHotseat(d.x, d.y)) {
layout = mLauncher.getHotseat().getLayout();
}
}
@@ -3207,7 +2922,7 @@ public class Workspace extends PagedView
} else {
// Test to see if we are over the hotseat otherwise just use the current page
if (mLauncher.getHotseat() != null && !isDragWidget(d)) {
- if (isPointInSelfOverHotseat(d.x, d.y, r)) {
+ if (isPointInSelfOverHotseat(d.x, d.y)) {
layout = mLauncher.getHotseat().getLayout();
}
}
@@ -3226,7 +2941,7 @@ public class Workspace extends PagedView
if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
} else {
- mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
+ mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter);
}
int minSpanX = item.spanX;
@@ -3288,7 +3003,7 @@ public class Workspace extends PagedView
if (distance > mMaxDistanceForFolderCreation) return;
final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0], mTargetCell[1]);
- ItemInfo info = (ItemInfo) dragObject.dragInfo;
+ ItemInfo info = dragObject.dragInfo;
boolean userFolderPending = willCreateUserFolder(info, dragOverView, false);
if (mDragMode == DRAG_MODE_NONE && userFolderPending &&
!mFolderCreationAlarm.alarmPending()) {
@@ -3410,24 +3125,6 @@ public class Workspace extends PagedView
}
/**
- * Add the item specified by dragInfo to the given layout.
- * @return true if successful
- */
- public boolean addExternalItemToScreen(ItemInfo dragInfo, CellLayout layout) {
- if (layout.findCellForSpan(mTempEstimate, dragInfo.spanX, dragInfo.spanY)) {
- onDropExternal(dragInfo.dropPos, (ItemInfo) dragInfo, (CellLayout) layout, false);
- return true;
- }
- mLauncher.showOutOfSpaceMessage(mLauncher.isHotseatLayout(layout));
- return false;
- }
-
- private void onDropExternal(int[] touchXY, Object dragInfo,
- CellLayout cellLayout, boolean insertAtFirst) {
- onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null);
- }
-
- /**
* Drop an item that didn't originate on one of the workspace screens.
* It may have come from Launcher (e.g. from all apps or customize), or it may have
* come from another app altogether.
@@ -3435,7 +3132,7 @@ public class Workspace extends PagedView
* NOTE: This can also be called when we are outside of a drag event, when we want
* to add an item to one of the workspace screens.
*/
- private void onDropExternal(final int[] touchXY, final Object dragInfo,
+ private void onDropExternal(final int[] touchXY, final ItemInfo dragInfo,
final CellLayout cellLayout, boolean insertAtFirst, DragObject d) {
final Runnable exitSpringLoadedRunnable = new Runnable() {
@Override
@@ -3445,7 +3142,7 @@ public class Workspace extends PagedView
}
};
- ItemInfo info = (ItemInfo) dragInfo;
+ ItemInfo info = dragInfo;
int spanX = info.spanX;
int spanY = info.spanY;
if (mDragInfo != null) {
@@ -3472,14 +3169,14 @@ public class Workspace extends PagedView
cellLayout, mTargetCell);
float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
mDragViewVisualCenter[1], mTargetCell);
- if (willCreateUserFolder((ItemInfo) d.dragInfo, cellLayout, mTargetCell,
- distance, true) || willAddToExistingUserFolder((ItemInfo) d.dragInfo,
- cellLayout, mTargetCell, distance)) {
+ if (willCreateUserFolder(d.dragInfo, cellLayout, mTargetCell, distance, true)
+ || willAddToExistingUserFolder(
+ d.dragInfo, cellLayout, mTargetCell, distance)) {
findNearestVacantCell = false;
}
}
- final ItemInfo item = (ItemInfo) d.dragInfo;
+ final ItemInfo item = d.dragInfo;
boolean updateWidgetSize = false;
if (findNearestVacantCell) {
int minSpanX = item.spanX;
@@ -3792,7 +3489,7 @@ public class Workspace extends PagedView
mDragInfo.container, mDragInfo.screenId);
if (cellLayout != null) {
cellLayout.onDropChild(mDragInfo.cell);
- } else if (LauncherAppState.isDogfoodBuild()) {
+ } else if (ProviderConfig.IS_DOGFOOD_BUILD) {
throw new RuntimeException("Invalid state: cellLayout == null in "
+ "Workspace#onDropCompleted. Please file a bug. ");
};
@@ -3803,6 +3500,13 @@ public class Workspace extends PagedView
}
mDragOutline = null;
mDragInfo = null;
+
+ if (!isFlingToDelete) {
+ // Fling to delete already exits spring loaded mode after the animation finishes.
+ mLauncher.exitSpringLoadedDragModeDelayed(success,
+ Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, mDelayedResizeRunnable);
+ mDelayedResizeRunnable = null;
+ }
}
/**
@@ -3812,7 +3516,7 @@ public class Workspace extends PagedView
CellLayout parentCell = getParentCellLayoutForView(v);
if (parentCell != null) {
parentCell.removeView(v);
- } else if (LauncherAppState.isDogfoodBuild()) {
+ } else if (ProviderConfig.IS_DOGFOOD_BUILD) {
// When an app is uninstalled using the drop target, we wait until resume to remove
// the icon. We also remove all the corresponding items from the workspace at
// {@link Launcher#bindComponentsRemoved}. That call can come before or after
@@ -3839,70 +3543,6 @@ public class Workspace extends PagedView
}
}
- void updateItemLocationsInDatabase(CellLayout cl) {
- int count = cl.getShortcutsAndWidgets().getChildCount();
-
- long screenId = getIdForScreen(cl);
- int container = Favorites.CONTAINER_DESKTOP;
-
- if (mLauncher.isHotseatLayout(cl)) {
- screenId = -1;
- container = Favorites.CONTAINER_HOTSEAT;
- }
-
- for (int i = 0; i < count; i++) {
- View v = cl.getShortcutsAndWidgets().getChildAt(i);
- ItemInfo info = (ItemInfo) v.getTag();
- // Null check required as the AllApps button doesn't have an item info
- if (info != null && info.requiresDbUpdate) {
- info.requiresDbUpdate = false;
- LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, info.cellX,
- info.cellY, info.spanX, info.spanY);
- }
- }
- }
-
- void saveWorkspaceToDb() {
- saveWorkspaceScreenToDb((CellLayout) mLauncher.getHotseat().getLayout());
- int count = getChildCount();
- for (int i = 0; i < count; i++) {
- CellLayout cl = (CellLayout) getChildAt(i);
- saveWorkspaceScreenToDb(cl);
- }
- }
-
- void saveWorkspaceScreenToDb(CellLayout cl) {
- int count = cl.getShortcutsAndWidgets().getChildCount();
-
- long screenId = getIdForScreen(cl);
- int container = Favorites.CONTAINER_DESKTOP;
-
- Hotseat hotseat = mLauncher.getHotseat();
- if (mLauncher.isHotseatLayout(cl)) {
- screenId = -1;
- container = Favorites.CONTAINER_HOTSEAT;
- }
-
- for (int i = 0; i < count; i++) {
- View v = cl.getShortcutsAndWidgets().getChildAt(i);
- ItemInfo info = (ItemInfo) v.getTag();
- // Null check required as the AllApps button doesn't have an item info
- if (info != null) {
- int cellX = info.cellX;
- int cellY = info.cellY;
- if (container == Favorites.CONTAINER_HOTSEAT) {
- cellX = hotseat.getCellXFromOrder((int) info.screenId);
- cellY = hotseat.getCellYFromOrder((int) info.screenId);
- }
- LauncherModel.addItemToDatabase(mLauncher, info, container, screenId, cellX, cellY);
- }
- if (v instanceof FolderIcon) {
- FolderIcon fi = (FolderIcon) v;
- fi.getFolder().addItemLocationsInDatabase();
- }
- }
- }
-
@Override
public float getIntrinsicIconScaleFactor() {
return 1f;
@@ -3915,7 +3555,7 @@ public class Workspace extends PagedView
@Override
public boolean supportsAppInfoDropTarget() {
- return false;
+ return true;
}
@Override
@@ -4440,7 +4080,7 @@ public class Workspace extends PagedView
}
void moveToDefaultScreen(boolean animate) {
- moveToScreen(mDefaultPage, animate);
+ moveToScreen(getDefaultPage(), animate);
}
void moveToCustomContentScreen(boolean animate) {
@@ -4496,13 +4136,8 @@ public class Workspace extends PagedView
}
nScreens--;
}
- return String.format(getContext().getString(R.string.workspace_scroll_format),
+ return getContext().getString(R.string.workspace_scroll_format,
page + 1 - delta, nScreens);
-
- }
-
- public void getLocationInDragLayer(int[] loc) {
- mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
}
@Override
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 54f63bbd8..d32ce7377 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -30,6 +30,7 @@ import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.DecelerateInterpolator;
+import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.util.Thunk;
import java.util.HashMap;
@@ -174,7 +175,6 @@ public class WorkspaceStateTransitionAnimation {
public static final String TAG = "WorkspaceStateTransitionAnimation";
- public static final int SCROLL_TO_CURRENT_PAGE = -1;
@Thunk static final int BACKGROUND_FADE_OUT_DURATION = 350;
final @Thunk Launcher mLauncher;
@@ -217,14 +217,18 @@ public class WorkspaceStateTransitionAnimation {
mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
}
+ public void snapToPageFromOverView(int whichPage) {
+ mWorkspace.snapToPage(whichPage, mOverviewTransitionTime, mZoomInInterpolator);
+ }
+
public AnimatorSet getAnimationToState(Workspace.State fromState, Workspace.State toState,
- int toPage, boolean animated, HashMap<View, Integer> layerViews) {
+ boolean animated, HashMap<View, Integer> layerViews) {
AccessibilityManager am = (AccessibilityManager)
mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE);
final boolean accessibilityEnabled = am.isEnabled();
TransitionStates states = new TransitionStates(fromState, toState);
int workspaceDuration = getAnimationDuration(states);
- animateWorkspace(states, toPage, animated, workspaceDuration, layerViews,
+ animateWorkspace(states, animated, workspaceDuration, layerViews,
accessibilityEnabled);
animateBackgroundGradient(states, animated, BACKGROUND_FADE_OUT_DURATION);
return mStateAnimator;
@@ -263,7 +267,7 @@ public class WorkspaceStateTransitionAnimation {
/**
* Starts a transition animation for the workspace.
*/
- private void animateWorkspace(final TransitionStates states, int toPage, final boolean animated,
+ private void animateWorkspace(final TransitionStates states, final boolean animated,
final int duration, final HashMap<View, Integer> layerViews,
final boolean accessibilityEnabled) {
// Reinitialize animation arrays for the current workspace state
@@ -281,8 +285,13 @@ public class WorkspaceStateTransitionAnimation {
float finalHotseatAndPageIndicatorAlpha = (states.stateIsNormal || states.stateIsSpringLoaded) ?
1f : 0f;
float finalOverviewPanelAlpha = states.stateIsOverview ? 1f : 0f;
- float finalWorkspaceTranslationY = states.stateIsOverview || states.stateIsOverviewHidden ?
- mWorkspace.getOverviewModeTranslationY() : 0;
+
+ float finalWorkspaceTranslationY = 0;
+ if (states.stateIsOverview || states.stateIsOverviewHidden) {
+ finalWorkspaceTranslationY = mWorkspace.getOverviewModeTranslationY();
+ } else if (states.stateIsSpringLoaded) {
+ finalWorkspaceTranslationY = mWorkspace.getSpringLoadedTranslationY();
+ }
final int childCount = mWorkspace.getChildCount();
final int customPageCount = mWorkspace.numCustomPages();
@@ -303,11 +312,7 @@ public class WorkspaceStateTransitionAnimation {
}
}
- if (toPage == SCROLL_TO_CURRENT_PAGE) {
- toPage = mWorkspace.getPageNearestToCenterOfScreen();
- }
- mWorkspace.snapToPage(toPage, duration, mZoomInInterpolator);
-
+ int toPage = mWorkspace.getPageNearestToCenterOfScreen();
for (int i = 0; i < childCount; i++) {
final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i);
boolean isCurrentPage = (i == toPage);
@@ -379,7 +384,6 @@ public class WorkspaceStateTransitionAnimation {
mNewBackgroundAlphas[i] != 0) {
ValueAnimator bgAnim = ObjectAnimator.ofFloat(cl, "backgroundAlpha",
mOldBackgroundAlphas[i], mNewBackgroundAlphas[i]);
- LauncherAnimUtils.ofFloat(cl, 0f, 1f);
bgAnim.setInterpolator(mZoomInInterpolator);
bgAnim.setDuration(duration);
mStateAnimator.play(bgAnim);
@@ -488,8 +492,7 @@ public class WorkspaceStateTransitionAnimation {
if (animated) {
// These properties refer to the background protection gradient used for AllApps
// and Widget tray.
- ValueAnimator bgFadeOutAnimation =
- LauncherAnimUtils.ofFloat(mWorkspace, startAlpha, finalAlpha);
+ ValueAnimator bgFadeOutAnimation = ValueAnimator.ofFloat(startAlpha, finalAlpha);
bgFadeOutAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 2306b776e..03731d175 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -20,7 +20,6 @@ import com.android.launcher3.AppInfo;
import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeleteDropTarget;
-import com.android.launcher3.DragController.DragListener;
import com.android.launcher3.DragSource;
import com.android.launcher3.Folder;
import com.android.launcher3.FolderInfo;
@@ -36,6 +35,7 @@ import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.UninstallDropTarget;
import com.android.launcher3.Workspace;
+import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -75,11 +75,11 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
mLauncher = launcher;
mActions.put(REMOVE, new AccessibilityAction(REMOVE,
- launcher.getText(R.string.delete_target_label)));
+ launcher.getText(R.string.remove_drop_target_label)));
mActions.put(INFO, new AccessibilityAction(INFO,
- launcher.getText(R.string.info_target_label)));
+ launcher.getText(R.string.app_info_drop_target_label)));
mActions.put(UNINSTALL, new AccessibilityAction(UNINSTALL,
- launcher.getText(R.string.delete_target_uninstall_label)));
+ launcher.getText(R.string.uninstall_drop_target_label)));
mActions.put(ADD_TO_WORKSPACE, new AccessibilityAction(ADD_TO_WORKSPACE,
launcher.getText(R.string.action_add_to_workspace)));
mActions.put(MOVE, new AccessibilityAction(MOVE,
@@ -96,7 +96,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
if (!(host.getTag() instanceof ItemInfo)) return;
ItemInfo item = (ItemInfo) host.getTag();
- if (DeleteDropTarget.supportsDrop(item)) {
+ if (DeleteDropTarget.supportsAccessibleDrop(item)) {
info.addAction(mActions.get(REMOVE));
}
if (UninstallDropTarget.supportsDrop(host.getContext(), item)) {
@@ -372,7 +372,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
@Override
- public void onDragStart(DragSource source, Object info, int dragAction) {
+ public void onDragStart(DragSource source, ItemInfo info, int dragAction) {
// No-op
}
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index f9bb13450..13f83a743 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -494,8 +494,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
// Start the drag
mLauncher.getWorkspace().beginDragShared(v, mIconLastTouchPos, this, false);
- // Enter spring loaded mode
- mLauncher.enterSpringLoadedDragMode();
return false;
}
@@ -549,7 +547,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
int currentScreen = mLauncher.getCurrentWorkspaceScreen();
Workspace workspace = (Workspace) target;
CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
- ItemInfo itemInfo = (ItemInfo) d.dragInfo;
+ ItemInfo itemInfo = d.dragInfo;
if (layout != null) {
showOutOfSpaceMessage =
!layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 38a3e3c3e..4803c6229 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -324,7 +324,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
}
}
- private Launcher mLauncher;
+ @Thunk Launcher mLauncher;
private LayoutInflater mLayoutInflater;
@Thunk AlphabeticalAppsList mApps;
private GridLayoutManager mGridLayoutMgr;
@@ -348,9 +348,9 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
// each time the search query changes.
private String mMarketSearchMessage;
// The intent to send off to the market app, updated each time the search query changes.
- private Intent mMarketSearchIntent;
+ @Thunk Intent mMarketSearchIntent;
// The last query that the user entered into the search field
- private String mLastSearchQuery;
+ @Thunk String mLastSearchQuery;
// Section drawing
@Thunk int mSectionNamesMargin;
@@ -420,11 +420,10 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
*/
public void setLastSearchQuery(String query) {
Resources res = mLauncher.getResources();
- String formatStr = res.getString(R.string.all_apps_no_search_results);
mLastSearchQuery = query;
- mEmptySearchMessage = String.format(formatStr, query);
+ mEmptySearchMessage = res.getString(R.string.all_apps_no_search_results, query);
if (mMarketAppName != null) {
- mMarketSearchMessage = String.format(res.getString(R.string.all_apps_search_market_message),
+ mMarketSearchMessage = res.getString(R.string.all_apps_search_market_message,
mMarketAppName);
mMarketSearchIntent = createMarketSearchIntent(query);
}
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
index 14e2a1863..09a7d59bf 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
@@ -17,17 +17,15 @@ package com.android.launcher3.allapps;
import android.content.Context;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.util.AttributeSet;
-import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
+
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
import com.android.launcher3.ClickShadowView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
/**
* A container for RecyclerView to allow for the click shadow view to be shown behind an icon that
diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
index 2b363c0cb..d853d5b8d 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.allapps;
-import android.content.ComponentName;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index dac0df12a..cb989e58b 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -16,13 +16,13 @@
package com.android.launcher3.allapps;
import android.content.Context;
-import android.support.v7.widget.RecyclerView;
import android.util.Log;
+
import com.android.launcher3.AppInfo;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.model.AppNameComparator;
import com.android.launcher3.util.ComponentKey;
@@ -186,7 +186,7 @@ public class AlphabeticalAppsList {
// The of ordered component names as a result of a search query
private ArrayList<ComponentKey> mSearchResults;
private HashMap<CharSequence, String> mCachedSectionNames = new HashMap<>();
- private RecyclerView.Adapter mAdapter;
+ private AllAppsGridAdapter mAdapter;
private AlphabeticIndexCompat mIndexer;
private AppNameComparator mAppNameComparator;
private MergeAlgorithm mMergeAlgorithm;
@@ -215,7 +215,7 @@ public class AlphabeticalAppsList {
/**
* Sets the adapter to notify when this dataset changes.
*/
- public void setAdapter(RecyclerView.Adapter adapter) {
+ public void setAdapter(AllAppsGridAdapter adapter) {
mAdapter = adapter;
}
@@ -419,7 +419,7 @@ public class AlphabeticalAppsList {
if (info != null) {
mPredictedApps.add(info);
} else {
- if (LauncherAppState.isDogfoodBuild()) {
+ if (ProviderConfig.IS_DOGFOOD_BUILD) {
Log.e(TAG, "Predicted app not found: " + ck.flattenToString(mLauncher));
}
}
diff --git a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
index ec1fb669f..463278ab4 100644
--- a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
+++ b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
@@ -1,6 +1,7 @@
package com.android.launcher3.compat;
import android.content.Context;
+
import com.android.launcher3.Utilities;
import java.lang.reflect.Constructor;
@@ -62,6 +63,7 @@ public class AlphabeticIndexCompat extends BaseAlphabeticIndex {
private boolean mHasValidAlphabeticIndex;
private String mDefaultMiscLabel;
+ @SuppressWarnings({"unchecked", "rawtypes"})
public AlphabeticIndexCompat(Context context) {
super();
try {
diff --git a/src/com/android/launcher3/config/ProviderConfig.java b/src/com/android/launcher3/config/ProviderConfig.java
index e8930d063..825b43422 100644
--- a/src/com/android/launcher3/config/ProviderConfig.java
+++ b/src/com/android/launcher3/config/ProviderConfig.java
@@ -19,4 +19,6 @@ package com.android.launcher3.config;
public class ProviderConfig {
public static final String AUTHORITY = "com.android.launcher3.settings".intern();
+
+ public static boolean IS_DOGFOOD_BUILD = false;
}
diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 204dddfdf..d61117c0d 100644
--- a/src/com/android/launcher3/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.launcher3;
+package com.android.launcher3.dragndrop;
import android.content.ComponentName;
import android.content.Context;
@@ -26,15 +26,23 @@ import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
+import android.view.DragEvent;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
-import android.view.accessibility.AccessibilityManager;
import android.view.inputmethod.InputMethodManager;
+import com.android.launcher3.DeleteDropTarget;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.PagedView;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.util.Thunk;
@@ -44,7 +52,7 @@ import java.util.HashSet;
/**
* Class for initiating a drag within a view or across multiple views.
*/
-public class DragController {
+public class DragController implements DragDriver.EventListener {
private static final String TAG = "Launcher.DragController";
/** Indicates the drag is a move. */
@@ -61,9 +69,9 @@ public class DragController {
private static final int SCROLL_OUTSIDE_ZONE = 0;
private static final int SCROLL_WAITING_IN_ZONE = 1;
- static final int SCROLL_NONE = -1;
- static final int SCROLL_LEFT = 0;
- static final int SCROLL_RIGHT = 1;
+ public static final int SCROLL_NONE = -1;
+ public static final int SCROLL_LEFT = 0;
+ public static final int SCROLL_RIGHT = 1;
private static final float MAX_FLING_DEGREES = 35f;
@@ -73,12 +81,14 @@ public class DragController {
// temporaries to avoid gc thrash
private Rect mRectTemp = new Rect();
private final int[] mCoordinatesTemp = new int[2];
- private final boolean mIsRtl;
- /** Whether or not we're dragging. */
- private boolean mDragging;
+ /**
+ * Drag driver for the current drag/drop operation, or null if there is no active DND operation.
+ * It's null during accessible drag operations.
+ */
+ private DragDriver mDragDriver = null;
- /** Whether or not this is an accessible drag operation */
+ /** Whether or not an accessible drag operation is in progress. */
private boolean mIsAccessibleDrag;
/** X coordinate of the down event. */
@@ -87,11 +97,6 @@ public class DragController {
/** Y coordinate of the down event. */
private int mMotionDownY;
- /** the area at the edge of the screen that makes the workspace go left
- * or right while you're dragging.
- */
- private int mScrollZone;
-
private DropTarget.DragObject mDragObject;
/** Who can receive drop events */
@@ -102,9 +107,6 @@ public class DragController {
/** The window token used as the parent for the DragView. */
private IBinder mWindowToken;
- /** The view that will be scrolled when dragging to the left and right edges of the screen. */
- private View mScrollView;
-
private View mMoveTarget;
@Thunk DragScroller mDragScroller;
@@ -117,12 +119,11 @@ public class DragController {
@Thunk int mLastTouch[] = new int[2];
@Thunk long mLastTouchUpTime = -1;
- @Thunk int mDistanceSinceScroll = 0;
private int mTmpPoint[] = new int[2];
private Rect mDragLayerRect = new Rect();
- protected int mFlingToDeleteThresholdVelocity;
+ protected final int mFlingToDeleteThresholdVelocity;
private VelocityTracker mVelocityTracker;
/**
@@ -137,14 +138,14 @@ public class DragController {
* @param dragAction The drag action: either {@link DragController#DRAG_ACTION_MOVE}
* or {@link DragController#DRAG_ACTION_COPY}
*/
- void onDragStart(DragSource source, Object info, int dragAction);
+ void onDragStart(DragSource source, ItemInfo info, int dragAction);
/**
* The drag has ended
*/
void onDragEnd();
}
-
+
/**
* Used to create a new DragLayer from XML.
*/
@@ -152,17 +153,10 @@ public class DragController {
Resources r = launcher.getResources();
mLauncher = launcher;
mHandler = new Handler();
- mScrollZone = r.getDimensionPixelSize(R.dimen.scroll_zone);
mVelocityTracker = VelocityTracker.obtain();
- float density = r.getDisplayMetrics().density;
mFlingToDeleteThresholdVelocity =
- (int) (r.getInteger(R.integer.config_flingToDeleteMinVelocity) * density);
- mIsRtl = Utilities.isRtl(r);
- }
-
- public boolean dragging() {
- return mDragging;
+ r.getDimensionPixelSize(R.dimen.drag_flingToDeleteMinVelocity);
}
/**
@@ -172,13 +166,11 @@ public class DragController {
* @param bmp The bitmap that represents the view being dragged
* @param source An object representing where the drag originated
* @param dragInfo The data associated with the object that is being dragged
+ * @param viewImageBounds the position of the image inside the view
* @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
* {@link #DRAG_ACTION_COPY}
- * @param viewImageBounds the position of the image inside the view
- * @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
- * Makes dragging feel more precise, e.g. you can clip out a transparent border
*/
- public void startDrag(View v, Bitmap bmp, DragSource source, Object dragInfo,
+ public void startDrag(View v, Bitmap bmp, DragSource source, ItemInfo dragInfo,
Rect viewImageBounds, int dragAction, float initialDragViewScale) {
int[] loc = mCoordinatesTemp;
mLauncher.getDragLayer().getLocationInDragLayer(v, loc);
@@ -211,7 +203,7 @@ public class DragController {
* @param accessible whether this drag should occur in accessibility mode
*/
public DragView startDrag(Bitmap b, int dragLayerX, int dragLayerY,
- DragSource source, Object dragInfo, int dragAction, Point dragOffset, Rect dragRegion,
+ DragSource source, ItemInfo dragInfo, int dragAction, Point dragOffset, Rect dragRegion,
float initialDragViewScale, boolean accessible) {
if (PROFILE_DRAWING_DURING_DRAG) {
android.os.Debug.startMethodTracing("Launcher");
@@ -234,13 +226,15 @@ public class DragController {
final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
- mDragging = true;
mIsAccessibleDrag = accessible;
+ mLastDropTarget = null;
mDragObject = new DropTarget.DragObject();
+ float finalDragViewScale = mLauncher.getWorkspace().getSpringLoadedShrinkFactor();
final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
- registrationY, 0, 0, b.getWidth(), b.getHeight(), initialDragViewScale);
+ registrationY, 0, 0, b.getWidth(), b.getHeight(),
+ initialDragViewScale, finalDragViewScale);
mDragObject.dragComplete = false;
if (mIsAccessibleDrag) {
@@ -252,6 +246,8 @@ public class DragController {
mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft);
mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop);
mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
+
+ mDragDriver = DragDriver.create(this, dragInfo, dragView);
}
mDragObject.dragSource = source;
@@ -271,44 +267,6 @@ public class DragController {
}
/**
- * Draw the view into a bitmap.
- */
- Bitmap getViewBitmap(View v) {
- v.clearFocus();
- v.setPressed(false);
-
- boolean willNotCache = v.willNotCacheDrawing();
- v.setWillNotCacheDrawing(false);
-
- // Reset the drawing cache background color to fully transparent
- // for the duration of this operation
- int color = v.getDrawingCacheBackgroundColor();
- v.setDrawingCacheBackgroundColor(0);
- float alpha = v.getAlpha();
- v.setAlpha(1.0f);
-
- if (color != 0) {
- v.destroyDrawingCache();
- }
- v.buildDrawingCache();
- Bitmap cacheBitmap = v.getDrawingCache();
- if (cacheBitmap == null) {
- Log.e(TAG, "failed getViewBitmap(" + v + ")", new RuntimeException());
- return null;
- }
-
- Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);
-
- // Restore the view
- v.destroyDrawingCache();
- v.setAlpha(alpha);
- v.setWillNotCacheDrawing(willNotCache);
- v.setDrawingCacheBackgroundColor(color);
-
- return bitmap;
- }
-
- /**
* Call this from a drag source view like this:
*
* <pre>
@@ -319,18 +277,18 @@ public class DragController {
* </pre>
*/
public boolean dispatchKeyEvent(KeyEvent event) {
- return mDragging;
+ return mDragDriver != null;
}
public boolean isDragging() {
- return mDragging;
+ return mDragDriver != null || mIsAccessibleDrag;
}
/**
* Stop dragging without dropping.
*/
public void cancelDrag() {
- if (mDragging) {
+ if (isDragging()) {
if (mLastDropTarget != null) {
mLastDropTarget.onDragExit(mDragObject);
}
@@ -341,6 +299,7 @@ public class DragController {
}
endDrag();
}
+
public void onAppsRemoved(final ArrayList<String> packageNames, HashSet<ComponentName> cns) {
// Cancel the current drag if we are removing an app that we are dragging
if (mDragObject != null) {
@@ -364,8 +323,8 @@ public class DragController {
}
private void endDrag() {
- if (mDragging) {
- mDragging = false;
+ if (isDragging()) {
+ mDragDriver = null;
mIsAccessibleDrag = false;
clearScrollRunnable();
boolean isDeferred = false;
@@ -416,18 +375,63 @@ public class DragController {
return mTmpPoint;
}
- long getLastGestureUpTime() {
- if (mDragging) {
+ public long getLastGestureUpTime() {
+ if (mDragDriver != null) {
return System.currentTimeMillis();
} else {
return mLastTouchUpTime;
}
}
- void resetLastGestureUpTime() {
+ public void resetLastGestureUpTime() {
mLastTouchUpTime = -1;
}
+ @Override
+ public void onDriverDragMove(float x, float y) {
+ final int[] dragLayerPos = getClampedDragLayerPos(x, y);
+
+ handleMoveEvent(dragLayerPos[0], dragLayerPos[1]);
+ }
+
+ @Override
+ public void onDriverDragExitWindow() {
+ if (mLastDropTarget != null) {
+ mLastDropTarget.onDragExit(mDragObject);
+ mLastDropTarget = null;
+ }
+ }
+
+ @Override
+ public void onDriverDragEnd(float x, float y, DropTarget dropTargetOverride) {
+ final int[] dragLayerPos = getClampedDragLayerPos(x, y);
+ final int dragLayerX = dragLayerPos[0];
+ final int dragLayerY = dragLayerPos[1];
+
+ DropTarget dropTarget;
+ PointF vec = null;
+
+ if (dropTargetOverride != null) {
+ dropTarget = dropTargetOverride;
+ } else {
+ vec = isFlingingToDelete(mDragObject.dragSource);
+ if (vec != null) {
+ dropTarget = mFlingToDeleteDropTarget;
+ } else {
+ dropTarget = findDropTarget((int) x, (int) y, mCoordinatesTemp);
+ }
+ }
+
+ drop(dropTarget, x, y, vec);
+
+ endDrag();
+ }
+
+ @Override
+ public void onDriverDragCancel() {
+ cancelDrag();
+ }
+
/**
* Call this from a drag source view.
*/
@@ -435,8 +439,8 @@ public class DragController {
@SuppressWarnings("all") // suppress dead code warning
final boolean debug = false;
if (debug) {
- Log.d(Launcher.TAG, "DragController.onInterceptTouchEvent " + ev + " mDragging="
- + mDragging);
+ Log.d(Launcher.TAG, "DragController.onInterceptTouchEvent " + ev + " Dragging="
+ + (mDragDriver != null));
}
if (mIsAccessibleDrag) {
@@ -452,41 +456,39 @@ public class DragController {
final int dragLayerY = dragLayerPos[1];
switch (action) {
- case MotionEvent.ACTION_MOVE:
- break;
case MotionEvent.ACTION_DOWN:
// Remember location of down touch
mMotionDownX = dragLayerX;
mMotionDownY = dragLayerY;
- mLastDropTarget = null;
break;
case MotionEvent.ACTION_UP:
mLastTouchUpTime = System.currentTimeMillis();
- if (mDragging) {
- PointF vec = isFlingingToDelete(mDragObject.dragSource);
- if (!DeleteDropTarget.supportsDrop(mDragObject.dragInfo)) {
- vec = null;
- }
- if (vec != null) {
- dropOnFlingToDeleteTarget(dragLayerX, dragLayerY, vec);
- } else {
- drop(dragLayerX, dragLayerY);
- }
- }
- endDrag();
- break;
- case MotionEvent.ACTION_CANCEL:
- cancelDrag();
break;
}
- return mDragging;
+ return mDragDriver != null && mDragDriver.onInterceptTouchEvent(ev);
+ }
+
+ /**
+ * Call this from a drag source view.
+ */
+ public boolean onDragEvent(DragEvent event) {
+ return mDragDriver != null && mDragDriver.onDragEvent(event);
+ }
+
+ /**
+ * Call this from a drag view.
+ */
+ public void onDragViewAnimationEnd() {
+ if (mDragDriver != null) {
+ mDragDriver.onDragViewAnimationEnd();
+ }
}
/**
* Sets the view that should handle move events.
*/
- void setMoveTarget(View view) {
+ public void setMoveTarget(View view) {
mMoveTarget = view;
}
@@ -500,7 +502,6 @@ public class DragController {
mScrollState = SCROLL_OUTSIDE_ZONE;
mScrollRunnable.setDirection(SCROLL_RIGHT);
mDragScroller.onExitScrollArea();
- mLauncher.getDragLayer().onExitScrollArea();
}
}
@@ -514,11 +515,8 @@ public class DragController {
mDragObject.y = coordinates[1];
checkTouchMove(dropTarget);
- // Check if we are hovering over the scroll areas
- mDistanceSinceScroll += Math.hypot(mLastTouch[0] - x, mLastTouch[1] - y);
mLastTouch[0] = x;
mLastTouch[1] = y;
- checkScrollState(x, y);
}
public void forceTouchMove() {
@@ -546,41 +544,11 @@ public class DragController {
mLastDropTarget = dropTarget;
}
- @Thunk void checkScrollState(int x, int y) {
- final int slop = ViewConfiguration.get(mLauncher).getScaledWindowTouchSlop();
- final int delay = mDistanceSinceScroll < slop ? RESCROLL_DELAY : SCROLL_DELAY;
- final DragLayer dragLayer = mLauncher.getDragLayer();
- final int forwardDirection = mIsRtl ? SCROLL_RIGHT : SCROLL_LEFT;
- final int backwardsDirection = mIsRtl ? SCROLL_LEFT : SCROLL_RIGHT;
-
- if (x < mScrollZone) {
- if (mScrollState == SCROLL_OUTSIDE_ZONE) {
- mScrollState = SCROLL_WAITING_IN_ZONE;
- if (mDragScroller.onEnterScrollArea(x, y, forwardDirection)) {
- dragLayer.onEnterScrollArea(forwardDirection);
- mScrollRunnable.setDirection(forwardDirection);
- mHandler.postDelayed(mScrollRunnable, delay);
- }
- }
- } else if (x > mScrollView.getWidth() - mScrollZone) {
- if (mScrollState == SCROLL_OUTSIDE_ZONE) {
- mScrollState = SCROLL_WAITING_IN_ZONE;
- if (mDragScroller.onEnterScrollArea(x, y, backwardsDirection)) {
- dragLayer.onEnterScrollArea(backwardsDirection);
- mScrollRunnable.setDirection(backwardsDirection);
- mHandler.postDelayed(mScrollRunnable, delay);
- }
- }
- } else {
- clearScrollRunnable();
- }
- }
-
/**
* Call this from a drag source view.
*/
public boolean onTouchEvent(MotionEvent ev) {
- if (!mDragging || mIsAccessibleDrag) {
+ if (mDragDriver == null || mIsAccessibleDrag) {
return false;
}
@@ -593,47 +561,18 @@ public class DragController {
final int dragLayerY = dragLayerPos[1];
switch (action) {
- case MotionEvent.ACTION_DOWN:
- // Remember where the motion event started
- mMotionDownX = dragLayerX;
- mMotionDownY = dragLayerY;
-
- if ((dragLayerX < mScrollZone) || (dragLayerX > mScrollView.getWidth() - mScrollZone)) {
- mScrollState = SCROLL_WAITING_IN_ZONE;
- mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
- } else {
- mScrollState = SCROLL_OUTSIDE_ZONE;
- }
- handleMoveEvent(dragLayerX, dragLayerY);
- break;
- case MotionEvent.ACTION_MOVE:
- handleMoveEvent(dragLayerX, dragLayerY);
- break;
- case MotionEvent.ACTION_UP:
- // Ensure that we've processed a move event at the current pointer location.
- handleMoveEvent(dragLayerX, dragLayerY);
- mHandler.removeCallbacks(mScrollRunnable);
-
- if (mDragging) {
- PointF vec = isFlingingToDelete(mDragObject.dragSource);
- if (!DeleteDropTarget.supportsDrop(mDragObject.dragInfo)) {
- vec = null;
- }
- if (vec != null) {
- dropOnFlingToDeleteTarget(dragLayerX, dragLayerY, vec);
- } else {
- drop(dragLayerX, dragLayerY);
- }
- }
- endDrag();
- break;
- case MotionEvent.ACTION_CANCEL:
- mHandler.removeCallbacks(mScrollRunnable);
- cancelDrag();
- break;
+ case MotionEvent.ACTION_DOWN:
+ // Remember where the motion event started
+ mMotionDownX = dragLayerX;
+ mMotionDownY = dragLayerY;
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mHandler.removeCallbacks(mScrollRunnable);
+ break;
}
- return true;
+ return mDragDriver.onTouchEvent(ev);
}
/**
@@ -643,7 +582,6 @@ public class DragController {
public void prepareAccessibleDrag(int x, int y) {
mMotionDownX = x;
mMotionDownY = y;
- mLastDropTarget = null;
}
/**
@@ -661,7 +599,7 @@ public class DragController {
dropTarget.prepareAccessibilityDrop();
// Perform the drop
- drop(location[0], location[1]);
+ drop(dropTarget, location[0], location[1], null);
endDrag();
}
@@ -676,64 +614,64 @@ public class DragController {
ViewConfiguration config = ViewConfiguration.get(mLauncher);
mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity());
-
+ PointF vel = new PointF(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
+ float theta = MAX_FLING_DEGREES + 1;
if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) {
// Do a quick dot product test to ensure that we are flinging upwards
- PointF vel = new PointF(mVelocityTracker.getXVelocity(),
- mVelocityTracker.getYVelocity());
PointF upVec = new PointF(0f, -1f);
- float theta = (float) Math.acos(((vel.x * upVec.x) + (vel.y * upVec.y)) /
- (vel.length() * upVec.length()));
- if (theta <= Math.toRadians(MAX_FLING_DEGREES)) {
- return vel;
- }
+ theta = getAngleBetweenVectors(vel, upVec);
+ } else if (mLauncher.getDeviceProfile().isVerticalBarLayout() &&
+ mVelocityTracker.getXVelocity() < mFlingToDeleteThresholdVelocity) {
+ // Remove icon is on left side instead of top, so check if we are flinging to the left.
+ PointF leftVec = new PointF(-1f, 0f);
+ theta = getAngleBetweenVectors(vel, leftVec);
+ }
+ if (theta <= Math.toRadians(MAX_FLING_DEGREES)) {
+ return vel;
}
return null;
}
- private void dropOnFlingToDeleteTarget(float x, float y, PointF vel) {
+ private float getAngleBetweenVectors(PointF vec1, PointF vec2) {
+ return (float) Math.acos(((vec1.x * vec2.x) + (vec1.y * vec2.y)) /
+ (vec1.length() * vec2.length()));
+ }
+
+ void drop(DropTarget dropTarget, float x, float y, PointF flingVel) {
final int[] coordinates = mCoordinatesTemp;
mDragObject.x = coordinates[0];
mDragObject.y = coordinates[1];
- // Clean up dragging on the target if it's not the current fling delete target otherwise,
- // start dragging to it.
- if (mLastDropTarget != null && mFlingToDeleteDropTarget != mLastDropTarget) {
- mLastDropTarget.onDragExit(mDragObject);
+ // Move dragging to the final target.
+ if (dropTarget != mLastDropTarget) {
+ if (mLastDropTarget != null) {
+ mLastDropTarget.onDragExit(mDragObject);
+ }
+ mLastDropTarget = dropTarget;
+ if (dropTarget != null) {
+ dropTarget.onDragEnter(mDragObject);
+ }
}
- // Drop onto the fling-to-delete target
- boolean accepted = false;
- mFlingToDeleteDropTarget.onDragEnter(mDragObject);
- // We must set dragComplete to true _only_ after we "enter" the fling-to-delete target for
- // "drop"
mDragObject.dragComplete = true;
- mFlingToDeleteDropTarget.onDragExit(mDragObject);
- if (mFlingToDeleteDropTarget.acceptDrop(mDragObject)) {
- mFlingToDeleteDropTarget.onFlingToDelete(mDragObject, vel);
- accepted = true;
- }
- mDragObject.dragSource.onDropCompleted((View) mFlingToDeleteDropTarget, mDragObject, true,
- accepted);
- }
- private void drop(float x, float y) {
- final int[] coordinates = mCoordinatesTemp;
- final DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
-
- mDragObject.x = coordinates[0];
- mDragObject.y = coordinates[1];
+ // Drop onto the target.
boolean accepted = false;
if (dropTarget != null) {
- mDragObject.dragComplete = true;
dropTarget.onDragExit(mDragObject);
if (dropTarget.acceptDrop(mDragObject)) {
- dropTarget.onDrop(mDragObject);
+ if (flingVel != null) {
+ dropTarget.onFlingToDelete(mDragObject, flingVel);
+ } else {
+ dropTarget.onDrop(mDragObject);
+ }
accepted = true;
}
}
- mDragObject.dragSource.onDropCompleted((View) dropTarget, mDragObject, false, accepted);
+ final View dropTargetAsView = dropTarget instanceof View ? (View) dropTarget : null;
+ mDragObject.dragSource.onDropCompleted(
+ dropTargetAsView, mDragObject, flingVel != null, accepted);
}
private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) {
@@ -819,17 +757,6 @@ public class DragController {
}
}
- /**
- * Set which view scrolls for touch events near the edge of the screen.
- */
- public void setScrollView(View v) {
- mScrollView = v;
- }
-
- DragView getDragView() {
- return mDragObject.dragView;
- }
-
private class ScrollRunnable implements Runnable {
private int mDirection;
@@ -844,14 +771,7 @@ public class DragController {
mDragScroller.scrollRight();
}
mScrollState = SCROLL_OUTSIDE_ZONE;
- mDistanceSinceScroll = 0;
mDragScroller.onExitScrollArea();
- mLauncher.getDragLayer().onExitScrollArea();
-
- if (isDragging()) {
- // Check the scroll again so that we can requeue the scroller if necessary
- checkScrollState(mLastTouch[0], mLastTouch[1]);
- }
}
}
diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java
new file mode 100644
index 000000000..6e4b430d4
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/DragDriver.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2015 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.launcher3.dragndrop;
+
+import com.android.launcher3.AnotherWindowDropTarget;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Utilities;
+
+import android.content.ClipData;
+import android.content.Intent;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.view.DragEvent;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * Base class for driving a drag/drop operation.
+ */
+public abstract class DragDriver {
+ protected final EventListener mEventListener;
+
+ public interface EventListener {
+ void onDriverDragMove(float x, float y);
+ void onDriverDragExitWindow();
+ void onDriverDragEnd(float x, float y, DropTarget dropTargetOverride);
+ void onDriverDragCancel();
+ }
+
+ public DragDriver(EventListener eventListener) {
+ mEventListener = eventListener;
+ }
+
+ /**
+ * Handles ending of the DragView animation.
+ */
+ public abstract void onDragViewAnimationEnd();
+
+ public boolean onTouchEvent(MotionEvent ev) {
+ final int action = ev.getAction();
+
+ switch (action) {
+ case MotionEvent.ACTION_MOVE:
+ mEventListener.onDriverDragMove(ev.getX(), ev.getY());
+ break;
+ case MotionEvent.ACTION_UP:
+ mEventListener.onDriverDragMove(ev.getX(), ev.getY());
+ mEventListener.onDriverDragEnd(ev.getX(), ev.getY(), null);
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ mEventListener.onDriverDragCancel();
+ break;
+ }
+
+ return true;
+ }
+
+ public abstract boolean onDragEvent (DragEvent event);
+
+
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ final int action = ev.getAction();
+
+ switch (action) {
+ case MotionEvent.ACTION_UP:
+ mEventListener.onDriverDragEnd(ev.getX(), ev.getY(), null);
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ mEventListener.onDriverDragCancel();
+ break;
+ }
+
+ return true;
+ }
+
+ public static DragDriver create(
+ DragController dragController, ItemInfo dragInfo, DragView dragView) {
+ if (Utilities.isNycOrAbove()) {
+ return new SystemDragDriver(dragController, dragInfo.getIntent(), dragView);
+ } else {
+ return new InternalDragDriver(dragController);
+ }
+ }
+
+};
+
+/**
+ * Class for driving a system (i.e. framework) drag/drop operation.
+ */
+class SystemDragDriver extends DragDriver {
+ /** Intent associated with the drag operation, or null is there no associated intent. */
+ private final Intent mDragIntent;
+
+ private final DragView mDragView;
+ boolean mIsFrameworkDragActive = false;
+ boolean mReceivedDropEvent = false;
+ float mLastX = 0;
+ float mLastY = 0;
+
+ public SystemDragDriver(DragController dragController, Intent dragIntent, DragView dragView) {
+ super(dragController);
+ mDragIntent = dragIntent;
+ mDragView = dragView;
+ }
+
+ private static class ShadowBuilder extends View.DragShadowBuilder {
+ final DragView mDragView;
+
+ public ShadowBuilder(DragView dragView) {
+ mDragView = dragView;
+ }
+
+ @Override
+ public void onProvideShadowMetrics (Point size, Point touch) {
+ mDragView.provideDragShadowMetrics(size, touch);
+ }
+
+ @Override
+ public void onDrawShadow(Canvas canvas) {
+ mDragView.drawDragShadow(canvas);
+ }
+ };
+
+ @Override
+ public void onDragViewAnimationEnd() {
+ // Clip data for the drag operation. If there is an intent, create an intent-based ClipData,
+ // which will be passed to a global DND.
+ // If there is no intent, craft a fake ClipData and start a local DND operation; this
+ // ClipData will be ignored.
+ final ClipData dragData = mDragIntent != null ?
+ ClipData.newIntent("", mDragIntent) :
+ ClipData.newPlainText("", "");
+
+ View.DragShadowBuilder shadowBuilder = new ShadowBuilder(mDragView);
+ // TODO: DND flags are in flux, once settled, use the appropriate constant.
+ final int flagGlobal = 1 << 0;
+ final int flagOpaque = 1 << 9;
+ final int flags = (mDragIntent != null ? flagGlobal : 0) | flagOpaque;
+
+ mIsFrameworkDragActive = true;
+
+ if (!mDragView.startDrag(dragData, shadowBuilder, null, flags)) {
+ mIsFrameworkDragActive = false;
+ mEventListener.onDriverDragCancel();
+ return;
+ }
+
+ // Starting from this point, the driver takes over showing the drag shadow, so hiding the
+ // drag view.
+ mDragView.setVisibility(View.INVISIBLE);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ return !mIsFrameworkDragActive && super.onTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return !mIsFrameworkDragActive && super.onInterceptTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onDragEvent (DragEvent event) {
+ if (!mIsFrameworkDragActive) {
+ // We are interested only in drag events started by this driver.
+ return false;
+ }
+
+ final int action = event.getAction();
+
+ switch (action) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ mLastX = event.getX();
+ mLastY = event.getY();
+ return true;
+
+ case DragEvent.ACTION_DRAG_ENTERED:
+ mLastX = event.getX();
+ mLastY = event.getY();
+ return true;
+
+ case DragEvent.ACTION_DRAG_LOCATION:
+ mLastX = event.getX();
+ mLastY = event.getY();
+ mEventListener.onDriverDragMove(event.getX(), event.getY());
+ return true;
+
+ case DragEvent.ACTION_DROP:
+ mLastX = event.getX();
+ mLastY = event.getY();
+ mReceivedDropEvent = true;
+ return true;
+
+ case DragEvent.ACTION_DRAG_EXITED:
+ mLastX = event.getX();
+ mLastY = event.getY();
+ mEventListener.onDriverDragExitWindow();
+ return true;
+
+ case DragEvent.ACTION_DRAG_ENDED:
+ final boolean dragAccepted = event.getResult();
+ final boolean acceptedByAnotherWindow = dragAccepted && !mReceivedDropEvent;
+
+ // When the system drag ends, its drag shadow disappears. Resume showing the drag
+ // view for the possible final animation.
+ mDragView.setVisibility(View.VISIBLE);
+
+ final DropTarget dropTargetOverride = acceptedByAnotherWindow ?
+ new AnotherWindowDropTarget(mDragView.getContext()) : null;
+
+ mEventListener.onDriverDragEnd(mLastX, mLastY, dropTargetOverride);
+ mIsFrameworkDragActive = false;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+};
+
+/**
+ * Class for driving an internal (i.e. not using framework) drag/drop operation.
+ */
+class InternalDragDriver extends DragDriver {
+ public InternalDragDriver(DragController dragController) {
+ super(dragController);
+ }
+
+ @Override
+ public void onDragViewAnimationEnd() {}
+
+ @Override
+ public boolean onDragEvent (DragEvent event) { return false; }
+};
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 1c18747c1..adcc12cb6 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.launcher3;
+package com.android.launcher3.dragndrop;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -28,6 +28,7 @@ import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
@@ -39,9 +40,24 @@ import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.TextView;
+import com.android.launcher3.AppWidgetResizeFrame;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.Folder;
+import com.android.launcher3.FolderIcon;
+import com.android.launcher3.InsettableFrameLayout;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetHostView;
+import com.android.launcher3.SearchDropTargetBar;
+import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.R;
+
import java.util.ArrayList;
/**
@@ -86,15 +102,6 @@ public class DragLayer extends InsettableFrameLayout {
// Darkening scrim
private float mBackgroundAlpha = 0;
- // Related to adjacent page hints
- private final Rect mScrollChildPosition = new Rect();
- private boolean mInScrollArea;
- private boolean mShowPageHints;
- private Drawable mLeftHoverDrawable;
- private Drawable mRightHoverDrawable;
- private Drawable mLeftHoverDrawableActive;
- private Drawable mRightHoverDrawableActive;
-
private boolean mBlockTouches = false;
/**
@@ -110,12 +117,7 @@ public class DragLayer extends InsettableFrameLayout {
setMotionEventSplittingEnabled(false);
setChildrenDrawingOrderEnabled(true);
- final Resources res = getResources();
- mLeftHoverDrawable = res.getDrawable(R.drawable.page_hover_left);
- mRightHoverDrawable = res.getDrawable(R.drawable.page_hover_right);
- mLeftHoverDrawableActive = res.getDrawable(R.drawable.page_hover_left_active);
- mRightHoverDrawableActive = res.getDrawable(R.drawable.page_hover_right_active);
- mIsRtl = Utilities.isRtl(res);
+ mIsRtl = Utilities.isRtl(getResources());
}
public void setup(Launcher launcher, DragController controller) {
@@ -163,6 +165,11 @@ public class DragLayer extends InsettableFrameLayout {
if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
return true;
}
+
+ getDescendantRectRelativeToSelf(mLauncher.getAppInfoDropTargetBar(), mHitRect);
+ if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
+ return true;
+ }
return false;
}
@@ -322,6 +329,7 @@ public class DragLayer extends InsettableFrameLayout {
if (isInAccessibleDrag()) {
childrenForAccessibility.add(mLauncher.getSearchDropTargetBar());
+ childrenForAccessibility.add(mLauncher.getAppInfoDropTargetBar());
}
} else {
super.addChildrenForAccessibility(childrenForAccessibility);
@@ -375,6 +383,11 @@ public class DragLayer extends InsettableFrameLayout {
return mDragController.onTouchEvent(ev);
}
+ @Override
+ public boolean onDragEvent (DragEvent event) {
+ return mDragController.onDragEvent(event);
+ }
+
/**
* Determine the rect of the descendant in this DragLayer's coordinates
*
@@ -764,7 +777,7 @@ public class DragLayer extends InsettableFrameLayout {
// Show the drop view if it was previously hidden
mDropView = view;
mDropView.cancelAnimation();
- mDropView.resetLayoutParams();
+ mDropView.requestLayout();
// Set the anchor view if the page is scrolling
if (anchorView != null) {
@@ -874,29 +887,6 @@ public class DragLayer extends InsettableFrameLayout {
}
}
- void onEnterScrollArea(int direction) {
- mInScrollArea = true;
- invalidate();
- }
-
- void onExitScrollArea() {
- mInScrollArea = false;
- invalidate();
- }
-
- void showPageHints() {
- mShowPageHints = true;
- Workspace workspace = mLauncher.getWorkspace();
- getDescendantRectRelativeToSelf(workspace.getChildAt(workspace.numCustomPages()),
- mScrollChildPosition);
- invalidate();
- }
-
- void hidePageHints() {
- mShowPageHints = false;
- invalidate();
- }
-
@Override
protected void dispatchDraw(Canvas canvas) {
// Draw the background below children.
@@ -908,41 +898,6 @@ public class DragLayer extends InsettableFrameLayout {
super.dispatchDraw(canvas);
}
- private void drawPageHints(Canvas canvas) {
- if (mShowPageHints) {
- Workspace workspace = mLauncher.getWorkspace();
- int width = getMeasuredWidth();
- int page = workspace.getNextPage();
- CellLayout leftPage = (CellLayout) workspace.getChildAt(mIsRtl ? page + 1 : page - 1);
- CellLayout rightPage = (CellLayout) workspace.getChildAt(mIsRtl ? page - 1 : page + 1);
-
- if (leftPage != null && leftPage.isDragTarget()) {
- Drawable left = mInScrollArea && leftPage.getIsDragOverlapping() ?
- mLeftHoverDrawableActive : mLeftHoverDrawable;
- left.setBounds(0, mScrollChildPosition.top,
- left.getIntrinsicWidth(), mScrollChildPosition.bottom);
- left.draw(canvas);
- }
- if (rightPage != null && rightPage.isDragTarget()) {
- Drawable right = mInScrollArea && rightPage.getIsDragOverlapping() ?
- mRightHoverDrawableActive : mRightHoverDrawable;
- right.setBounds(width - right.getIntrinsicWidth(),
- mScrollChildPosition.top, width, mScrollChildPosition.bottom);
- right.draw(canvas);
- }
- }
- }
-
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- boolean ret = super.drawChild(canvas, child, drawingTime);
-
- // We want to draw the page hints above the workspace, but below the drag view.
- if (child instanceof Workspace) {
- drawPageHints(canvas);
- }
- return ret;
- }
-
public void setBackgroundAlpha(float alpha) {
if (alpha != mBackgroundAlpha) {
mBackgroundAlpha = alpha;
diff --git a/src/com/android/launcher3/DragScroller.java b/src/com/android/launcher3/dragndrop/DragScroller.java
index e261f15d8..165d0b11c 100644
--- a/src/com/android/launcher3/DragScroller.java
+++ b/src/com/android/launcher3/dragndrop/DragScroller.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.launcher3;
+package com.android.launcher3.dragndrop;
/**
* Handles scrolling while dragging
diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 2acfc6140..f663d00d1 100644
--- a/src/com/android/launcher3/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -14,11 +14,14 @@
* limitations under the License.
*/
-package com.android.launcher3;
+package com.android.launcher3.dragndrop;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.FloatArrayEvaluator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -33,8 +36,13 @@ import android.os.Build;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.Utilities;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.R;
+
import java.util.Arrays;
public class DragView extends View {
@@ -45,19 +53,18 @@ public class DragView extends View {
private Bitmap mBitmap;
private Bitmap mCrossFadeBitmap;
@Thunk Paint mPaint;
- private int mRegistrationX;
- private int mRegistrationY;
+ private final int mRegistrationX;
+ private final int mRegistrationY;
private Point mDragVisualizeOffset = null;
private Rect mDragRegion = null;
- private DragLayer mDragLayer = null;
+ private final DragLayer mDragLayer;
+ @Thunk final DragController mDragController;
private boolean mHasDrawn = false;
@Thunk float mCrossFadeProgress = 0f;
+ private boolean mAnimationCancelled = false;
ValueAnimator mAnim;
- @Thunk float mOffsetX = 0.0f;
- @Thunk float mOffsetY = 0.0f;
- private float mInitialScale = 1f;
// The intrinsic icon scale factor is the scale factor for a drag icon over the workspace
// size. This is ignored for non-icons.
private float mIntrinsicIconScale = 1f;
@@ -70,7 +77,6 @@ public class DragView extends View {
* <p>
* The registration point is the point inside our view that the touch events should
* be centered upon.
- *
* @param launcher The Launcher instance
* @param bitmap The view that we're dragging around. We scale it up when we draw it.
* @param registrationX The x coordinate of the registration point.
@@ -78,14 +84,15 @@ public class DragView extends View {
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public DragView(Launcher launcher, Bitmap bitmap, int registrationX, int registrationY,
- int left, int top, int width, int height, final float initialScale) {
+ int left, int top, int width, int height, final float initialScale,
+ float finalDragViewScale) {
super(launcher);
mDragLayer = launcher.getDragLayer();
- mInitialScale = initialScale;
+ mDragController = launcher.getDragController();
final Resources res = getResources();
final float scaleDps = res.getDimensionPixelSize(R.dimen.dragViewScale);
- final float scale = (width + scaleDps) / width;
+ final float scale = finalDragViewScale * (width + scaleDps) / width;
// Set the initial scale to avoid any jumps
setScaleX(initialScale);
@@ -99,11 +106,6 @@ public class DragView extends View {
public void onAnimationUpdate(ValueAnimator animation) {
final float value = (Float) animation.getAnimatedValue();
- final int deltaX = (int) (-mOffsetX);
- final int deltaY = (int) (-mOffsetY);
-
- mOffsetX += deltaX;
- mOffsetY += deltaY;
setScaleX(initialScale + (value * (scale - initialScale)));
setScaleY(initialScale + (value * (scale - initialScale)));
if (sDragAlpha != 1f) {
@@ -112,9 +114,15 @@ public class DragView extends View {
if (getParent() == null) {
animation.cancel();
- } else {
- setTranslationX(getTranslationX() + deltaX);
- setTranslationY(getTranslationY() + deltaY);
+ }
+ }
+ });
+
+ mAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mAnimationCancelled) {
+ mDragController.onDragViewAnimationEnd();
}
}
});
@@ -145,10 +153,6 @@ public class DragView extends View {
return mIntrinsicIconScale;
}
- public float getOffsetY() {
- return mOffsetY;
- }
-
public int getDragRegionLeft() {
return mDragRegion.left;
}
@@ -181,30 +185,33 @@ public class DragView extends View {
return mDragRegion;
}
- public float getInitialScale() {
- return mInitialScale;
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());
}
- public void updateInitialScaleToCurrentScale() {
- mInitialScale = getScaleX();
+ // Draws drag shadow for system DND.
+ @SuppressLint("WrongCall")
+ public void drawDragShadow(Canvas canvas) {
+ final int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ canvas.scale(getScaleX(), getScaleY());
+ onDraw(canvas);
+ canvas.restoreToCount(saveCount);
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());
+ // Provides drag shadow metrics for system DND.
+ public void provideDragShadowMetrics(Point size, Point touch) {
+ size.set((int)(mBitmap.getWidth() * getScaleX()), (int)(mBitmap.getHeight() * getScaleY()));
+
+ final float xGrowth = mBitmap.getWidth() * (getScaleX() - 1);
+ final float yGrowth = mBitmap.getHeight() * (getScaleY() - 1);
+ touch.set(
+ mRegistrationX + (int)Math.round(xGrowth / 2),
+ mRegistrationY + (int)Math.round(yGrowth / 2));
}
@Override
protected void onDraw(Canvas canvas) {
- @SuppressWarnings("all") // suppress dead code warning
- final boolean debug = false;
- if (debug) {
- Paint p = new Paint();
- p.setStyle(Paint.Style.FILL);
- p.setColor(0x66ffffff);
- canvas.drawRect(0, 0, getWidth(), getHeight(), p);
- }
-
mHasDrawn = true;
boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null;
if (crossFade) {
@@ -214,12 +221,12 @@ public class DragView extends View {
canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
if (crossFade) {
mPaint.setAlpha((int) (255 * mCrossFadeProgress));
- canvas.save();
+ final int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth();
float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight();
canvas.scale(sX, sY);
canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint);
- canvas.restore();
+ canvas.restoreToCount(saveCount);
}
}
@@ -235,6 +242,7 @@ public class DragView extends View {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCrossFadeProgress = animation.getAnimatedFraction();
+ invalidate();
}
});
va.start();
@@ -328,16 +336,12 @@ public class DragView extends View {
}
public void cancelAnimation() {
+ mAnimationCancelled = true;
if (mAnim != null && mAnim.isRunning()) {
mAnim.cancel();
}
}
- public void resetLayoutParams() {
- mOffsetX = mOffsetY = 0;
- requestLayout();
- }
-
/**
* Move the window containing this view.
*
@@ -345,8 +349,8 @@ public class DragView extends View {
* @param touchY the y coordinate the user touched in DragLayer coordinates
*/
void move(int touchX, int touchY) {
- setTranslationX(touchX - mRegistrationX + (int) mOffsetX);
- setTranslationY(touchY - mRegistrationY + (int) mOffsetY);
+ setTranslationX(touchX - mRegistrationX);
+ setTranslationY(touchY - mRegistrationY);
}
void remove() {
diff --git a/src/com/android/launcher3/SpringLoadedDragController.java b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
index 45edaef86..d7f41c947 100644
--- a/src/com/android/launcher3/SpringLoadedDragController.java
+++ b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
@@ -14,7 +14,13 @@
* limitations under the License.
*/
-package com.android.launcher3;
+package com.android.launcher3.dragndrop;
+
+import com.android.launcher3.Alarm;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.OnAlarmListener;
+import com.android.launcher3.Workspace;
public class SpringLoadedDragController implements OnAlarmListener {
// how long the user must hover over a mini-screen before it unshrinks
diff --git a/src/com/android/launcher3/model/MigrateFromRestoreTask.java b/src/com/android/launcher3/model/MigrateFromRestoreTask.java
index 786ab6009..9cabc8d41 100644
--- a/src/com/android/launcher3/model/MigrateFromRestoreTask.java
+++ b/src/com/android/launcher3/model/MigrateFromRestoreTask.java
@@ -135,7 +135,10 @@ public class MigrateFromRestoreTask {
new boolean[mTrgX][mTrgY], deepCopy(mCarryOver), true);
placement.find();
if (placement.finalPlacedItems.size() > 0) {
- long newScreenId = LauncherAppState.getLauncherProvider().generateNewScreenId();
+ long newScreenId = LauncherSettings.Settings.call(
+ mContext.getContentResolver(),
+ LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
+ .getLong(LauncherSettings.Settings.EXTRA_VALUE);
allScreens.add(newScreenId);
for (DbEntry item : placement.finalPlacedItems) {
if (!mCarryOver.remove(itemMap.get(item.id))) {
diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java
index 30f228c68..ddc9cbfd9 100644
--- a/src/com/android/launcher3/model/PackageItemInfo.java
+++ b/src/com/android/launcher3/model/PackageItemInfo.java
@@ -20,8 +20,6 @@ import android.graphics.Bitmap;
import com.android.launcher3.ItemInfo;
-import java.util.Arrays;
-
/**
* Represents a {@link Package} in the widget tray section.
*/
@@ -59,7 +57,6 @@ public class PackageItemInfo extends ItemInfo {
return "PackageItemInfo(title=" + title + " id=" + this.id
+ " type=" + this.itemType + " container=" + this.container
+ " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY
- + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
- + " user=" + user + ")";
+ + " spanX=" + spanX + " spanY=" + spanY + " user=" + user + ")";
}
}
diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java
index 1bb57874e..89821768c 100644
--- a/src/com/android/launcher3/testing/LauncherExtension.java
+++ b/src/com/android/launcher3/testing/LauncherExtension.java
@@ -373,7 +373,8 @@ public class LauncherExtension extends Launcher {
@Override
public void onScrollInteractionEnd() {
if (mProgress > 25 && mLauncherOverlayCallbacks.enterFullImmersion()) {
- ObjectAnimator oa = LauncherAnimUtils.ofFloat(mSearchOverlay, "translationX", 0);
+ ObjectAnimator oa = LauncherAnimUtils.ofFloat(
+ mSearchOverlay, View.TRANSLATION_X, 0);
oa.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator arg0) {
diff --git a/src/com/android/launcher3/util/FlingAnimation.java b/src/com/android/launcher3/util/FlingAnimation.java
index 55c5d7dc2..da8bae712 100644
--- a/src/com/android/launcher3/util/FlingAnimation.java
+++ b/src/com/android/launcher3/util/FlingAnimation.java
@@ -7,9 +7,9 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.view.animation.DecelerateInterpolator;
-import com.android.launcher3.DragLayer;
-import com.android.launcher3.DragView;
import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragView;
public class FlingAnimation implements AnimatorUpdateListener {
@@ -51,7 +51,7 @@ public class FlingAnimation implements AnimatorUpdateListener {
mFrom.top += yOffset;
mFrom.bottom -= yOffset;
- mDuration = initDuration();
+ mDuration = Math.abs(vel.y) > Math.abs(vel.x) ? initFlingUpDuration() : initFlingLeftDuration();
mAnimationTimeFraction = ((float) mDuration) / (mDuration + DRAG_END_DELAY);
}
@@ -62,7 +62,7 @@ public class FlingAnimation implements AnimatorUpdateListener {
* - Calculate a constant acceleration in x direction such that the object reaches
* {@link #mIconRect} in the given time.
*/
- protected int initDuration() {
+ protected int initFlingUpDuration() {
float sY = -mFrom.bottom;
float d = mUY * mUY + 2 * sY * MAX_ACCELERATION;
@@ -83,6 +83,34 @@ public class FlingAnimation implements AnimatorUpdateListener {
return (int) Math.round(t);
}
+ /**
+ * The fling animation is based on the following system
+ * - Apply a constant force in the x direction to causing the fling to decelerate.
+ * - The animation runs for the time taken by the object to go out of the screen.
+ * - Calculate a constant acceleration in y direction such that the object reaches
+ * {@link #mIconRect} in the given time.
+ */
+ protected int initFlingLeftDuration() {
+ float sX = -mFrom.right;
+
+ float d = mUX * mUX + 2 * sX * MAX_ACCELERATION;
+ if (d >= 0) {
+ // sX can be reached under the MAX_ACCELERATION. Use MAX_ACCELERATION for x direction.
+ mAX = MAX_ACCELERATION;
+ } else {
+ // sX is not reachable, decrease the acceleration so that sX is almost reached.
+ d = 0;
+ mAX = mUX * mUX / (2 * -sX);
+ }
+ double t = (-mUX - Math.sqrt(d)) / mAX;
+
+ float sY = -mFrom.exactCenterY() + mIconRect.exactCenterY();
+
+ // Find vertical acceleration such that: u*t + a*t*t/2 = s
+ mAY = (float) ((sY - t * mUY) * 2 / (t * t));
+ return (int) Math.round(t);
+ }
+
public final int getDuration() {
return mDuration + DRAG_END_DELAY;
}
diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
new file mode 100644
index 000000000..01808ba57
--- /dev/null
+++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
@@ -0,0 +1,96 @@
+/**
+ * Copyright (C) 2015 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.launcher3.util;
+
+import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
+import android.view.ViewTreeObserver.OnDrawListener;
+
+import com.android.launcher3.DeferredHandler;
+import com.android.launcher3.Launcher;
+
+import java.util.ArrayList;
+import java.util.concurrent.Executor;
+
+/**
+ * An executor which runs all the tasks after the first onDraw is called on the target view.
+ */
+public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable,
+ OnAttachStateChangeListener {
+
+ private final ArrayList<Runnable> mTasks = new ArrayList<>();
+ private final DeferredHandler mHandler;
+
+ private Launcher mLauncher;
+ private View mAttachedView;
+ private boolean mCompleted;
+
+ public ViewOnDrawExecutor(DeferredHandler handler) {
+ mHandler = handler;
+ }
+
+ public void attachTo(Launcher launcher) {
+ mLauncher = launcher;
+ mAttachedView = launcher.getWorkspace();
+ mAttachedView.addOnAttachStateChangeListener(this);
+
+ attachObserver();
+ }
+
+ private void attachObserver() {
+ if (!mCompleted) {
+ mAttachedView.getViewTreeObserver().addOnDrawListener(this);
+ }
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ mTasks.add(command);
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ attachObserver();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) { }
+
+ @Override
+ public void onDraw() {
+ mAttachedView.post(this);
+ }
+
+ @Override
+ public void run() {
+ for (final Runnable r : mTasks) {
+ mHandler.post(r);
+ }
+ markCompleted();
+ }
+
+ public void markCompleted() {
+ mTasks.clear();
+ if (mAttachedView != null) {
+ mAttachedView.getViewTreeObserver().removeOnDrawListener(this);
+ mAttachedView.removeOnAttachStateChangeListener(this);
+ }
+ if (mLauncher != null) {
+ mLauncher.clearPendingExecutor(this);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
new file mode 100644
index 000000000..1fbd0dc13
--- /dev/null
+++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
@@ -0,0 +1,225 @@
+package com.android.launcher3.util;
+
+import android.app.WallpaperManager;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.Choreographer;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
+
+/**
+ * Utility class to handle wallpaper scrolling along with workspace.
+ */
+public class WallpaperOffsetInterpolator implements Choreographer.FrameCallback {
+ private static final String TAG = "WPOffsetInterpolator";
+ private static final int ANIMATION_DURATION = 250;
+
+ // Don't use all the wallpaper for parallax until you have at least this many pages
+ private static final int MIN_PARALLAX_PAGE_SPAN = 3;
+
+ private final Choreographer mChoreographer;
+ private final Interpolator mInterpolator;
+ private final WallpaperManager mWallpaperManager;
+ private final Workspace mWorkspace;
+ private final boolean mIsRtl;
+
+ private IBinder mWindowToken;
+ private boolean mWallpaperIsLiveWallpaper;
+ private float mLastSetWallpaperOffsetSteps = 0;
+
+ private float mFinalOffset = 0.0f;
+ private float mCurrentOffset = 0.5f; // to force an initial update
+ private boolean mWaitingForUpdate;
+
+ private boolean mAnimating;
+ private long mAnimationStartTime;
+ private float mAnimationStartOffset;
+ int mNumScreens;
+ int mNumPagesForWallpaperParallax;
+
+ public WallpaperOffsetInterpolator(Workspace workspace) {
+ mChoreographer = Choreographer.getInstance();
+ mInterpolator = new DecelerateInterpolator(1.5f);
+
+ mWorkspace = workspace;
+ mWallpaperManager = WallpaperManager.getInstance(workspace.getContext());
+ mIsRtl = Utilities.isRtl(workspace.getResources());
+ }
+
+ @Override
+ public void doFrame(long frameTimeNanos) {
+ updateOffset(false);
+ }
+
+ private void updateOffset(boolean force) {
+ if (mWaitingForUpdate || force) {
+ mWaitingForUpdate = false;
+ if (computeScrollOffset() && mWindowToken != null) {
+ try {
+ mWallpaperManager.setWallpaperOffsets(mWindowToken, getCurrX(), 0.5f);
+ setWallpaperOffsetSteps();
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Error updating wallpaper offset: " + e);
+ }
+ }
+ }
+ }
+
+ public boolean computeScrollOffset() {
+ final float oldOffset = mCurrentOffset;
+ if (mAnimating) {
+ long durationSinceAnimation = System.currentTimeMillis() - mAnimationStartTime;
+ float t0 = durationSinceAnimation / (float) ANIMATION_DURATION;
+ float t1 = mInterpolator.getInterpolation(t0);
+ mCurrentOffset = mAnimationStartOffset +
+ (mFinalOffset - mAnimationStartOffset) * t1;
+ mAnimating = durationSinceAnimation < ANIMATION_DURATION;
+ } else {
+ mCurrentOffset = mFinalOffset;
+ }
+
+ if (Math.abs(mCurrentOffset - mFinalOffset) > 0.0000001f) {
+ scheduleUpdate();
+ }
+ if (Math.abs(oldOffset - mCurrentOffset) > 0.0000001f) {
+ return true;
+ }
+ return false;
+ }
+
+ public float wallpaperOffsetForScroll(int scroll) {
+ // TODO: do different behavior if it's a live wallpaper?
+ // Don't use up all the wallpaper parallax until you have at least
+ // MIN_PARALLAX_PAGE_SPAN pages
+ int numScrollingPages = getNumScreensExcludingEmptyAndCustom();
+ int parallaxPageSpan;
+ if (mWallpaperIsLiveWallpaper) {
+ parallaxPageSpan = numScrollingPages - 1;
+ } else {
+ parallaxPageSpan = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages - 1);
+ }
+ mNumPagesForWallpaperParallax = parallaxPageSpan;
+
+ if (mWorkspace.getChildCount() <= 1) {
+ if (mIsRtl) {
+ return 1 - 1.0f/mNumPagesForWallpaperParallax;
+ }
+ return 0;
+ }
+
+ // Exclude the leftmost page
+ int emptyExtraPages = numEmptyScreensToIgnore();
+ int firstIndex = mWorkspace.numCustomPages();
+ // Exclude the last extra empty screen (if we have > MIN_PARALLAX_PAGE_SPAN pages)
+ int lastIndex = mWorkspace.getChildCount() - 1 - emptyExtraPages;
+ if (mIsRtl) {
+ int temp = firstIndex;
+ firstIndex = lastIndex;
+ lastIndex = temp;
+ }
+
+ int firstPageScrollX = mWorkspace.getScrollForPage(firstIndex);
+ int scrollRange = mWorkspace.getScrollForPage(lastIndex) - firstPageScrollX;
+ if (scrollRange == 0) {
+ return 0;
+ } else {
+ // Sometimes the left parameter of the pages is animated during a layout transition;
+ // this parameter offsets it to keep the wallpaper from animating as well
+ int adjustedScroll =
+ scroll - firstPageScrollX - mWorkspace.getLayoutTransitionOffsetForPage(0);
+ float offset = Math.min(1, adjustedScroll / (float) scrollRange);
+ offset = Math.max(0, offset);
+
+ // On RTL devices, push the wallpaper offset to the right if we don't have enough
+ // pages (ie if numScrollingPages < MIN_PARALLAX_PAGE_SPAN)
+ if (!mWallpaperIsLiveWallpaper && numScrollingPages < MIN_PARALLAX_PAGE_SPAN
+ && mIsRtl) {
+ return offset * (parallaxPageSpan - numScrollingPages + 1) / parallaxPageSpan;
+ }
+ return offset * (numScrollingPages - 1) / parallaxPageSpan;
+ }
+ }
+
+ private float wallpaperOffsetForCurrentScroll() {
+ return wallpaperOffsetForScroll(mWorkspace.getScrollX());
+ }
+
+ private int numEmptyScreensToIgnore() {
+ int numScrollingPages = mWorkspace.getChildCount() - mWorkspace.numCustomPages();
+ if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && mWorkspace.hasExtraEmptyScreen()) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ private int getNumScreensExcludingEmptyAndCustom() {
+ return mWorkspace.getChildCount() - numEmptyScreensToIgnore() - mWorkspace.numCustomPages();
+ }
+
+ public void syncWithScroll() {
+ float offset = wallpaperOffsetForCurrentScroll();
+ setFinalX(offset);
+ updateOffset(true);
+ }
+
+ public float getCurrX() {
+ return mCurrentOffset;
+ }
+
+ public float getFinalX() {
+ return mFinalOffset;
+ }
+
+ private void animateToFinal() {
+ mAnimating = true;
+ mAnimationStartOffset = mCurrentOffset;
+ mAnimationStartTime = System.currentTimeMillis();
+ }
+
+ private void setWallpaperOffsetSteps() {
+ // Set wallpaper offset steps (1 / (number of screens - 1))
+ float xOffset = 1.0f / mNumPagesForWallpaperParallax;
+ if (xOffset != mLastSetWallpaperOffsetSteps) {
+ mWallpaperManager.setWallpaperOffsetSteps(xOffset, 1.0f);
+ mLastSetWallpaperOffsetSteps = xOffset;
+ }
+ }
+
+ public void setFinalX(float x) {
+ scheduleUpdate();
+ mFinalOffset = Math.max(0f, Math.min(x, 1.0f));
+ if (getNumScreensExcludingEmptyAndCustom() != mNumScreens) {
+ if (mNumScreens > 0) {
+ // Don't animate if we're going from 0 screens
+ animateToFinal();
+ }
+ mNumScreens = getNumScreensExcludingEmptyAndCustom();
+ }
+ }
+
+ private void scheduleUpdate() {
+ if (!mWaitingForUpdate) {
+ mChoreographer.postFrameCallback(this);
+ mWaitingForUpdate = true;
+ }
+ }
+
+ public void jumpToFinal() {
+ mCurrentOffset = mFinalOffset;
+ }
+
+ public void onResume() {
+ mWallpaperIsLiveWallpaper = mWallpaperManager.getWallpaperInfo() != null;
+ // Force the wallpaper offset steps to be set again, because another app might have changed
+ // them
+ mLastSetWallpaperOffsetSteps = 0f;
+ }
+
+ public void setWindowToken(IBinder token) {
+ mWindowToken = token;
+ }
+} \ No newline at end of file
diff --git a/src/com/android/launcher3/util/WallpaperUtils.java b/src/com/android/launcher3/util/WallpaperUtils.java
index b9fccbcfd..a5251e190 100644
--- a/src/com/android/launcher3/util/WallpaperUtils.java
+++ b/src/com/android/launcher3/util/WallpaperUtils.java
@@ -17,13 +17,16 @@
package com.android.launcher3.util;
import android.annotation.TargetApi;
+import android.app.Activity;
import android.app.WallpaperManager;
+import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Point;
import android.os.Build;
import android.view.WindowManager;
+import com.android.launcher3.LauncherFiles;
import com.android.launcher3.Utilities;
/**
@@ -35,28 +38,59 @@ public final class WallpaperUtils {
public static final String WALLPAPER_HEIGHT_KEY = "wallpaper.height";
public static final float WALLPAPER_SCREENS_SPAN = 2f;
- public static void suggestWallpaperDimension(Resources res,
- final SharedPreferences sharedPrefs,
- WindowManager windowManager,
- final WallpaperManager wallpaperManager, boolean fallBackToDefaults) {
- final Point defaultWallpaperSize = WallpaperUtils.getDefaultWallpaperSize(res, windowManager);
- // If we have saved a wallpaper width/height, use that instead
+ public static void saveWallpaperDimensions(int width, int height, Activity activity) {
+ if (Utilities.ATLEAST_KITKAT) {
+ // From Kitkat onwards, ImageWallpaper does not care about the
+ // desired width and desired height of the wallpaper.
+ return;
+ }
+ String spKey = LauncherFiles.WALLPAPER_CROP_PREFERENCES_KEY;
+ SharedPreferences sp = activity.getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
+ SharedPreferences.Editor editor = sp.edit();
+ if (width != 0 && height != 0) {
+ editor.putInt(WALLPAPER_WIDTH_KEY, width);
+ editor.putInt(WALLPAPER_HEIGHT_KEY, height);
+ } else {
+ editor.remove(WALLPAPER_WIDTH_KEY);
+ editor.remove(WALLPAPER_HEIGHT_KEY);
+ }
+ editor.commit();
+ suggestWallpaperDimensionPreK(activity, true);
+ }
+
+ public static void suggestWallpaperDimensionPreK(
+ Activity activity, boolean fallBackToDefaults) {
+ final Point defaultWallpaperSize = getDefaultWallpaperSize(
+ activity.getResources(), activity.getWindowManager());
- int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, -1);
- int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, -1);
+ SharedPreferences sp = activity.getSharedPreferences(
+ LauncherFiles.WALLPAPER_CROP_PREFERENCES_KEY, Context.MODE_MULTI_PROCESS);
+ // If we have saved a wallpaper width/height, use that instead
+ int width = sp.getInt(WALLPAPER_WIDTH_KEY, -1);
+ int height = sp.getInt(WALLPAPER_HEIGHT_KEY, -1);
- if (savedWidth == -1 || savedHeight == -1) {
+ if (width == -1 || height == -1) {
if (!fallBackToDefaults) {
return;
} else {
- savedWidth = defaultWallpaperSize.x;
- savedHeight = defaultWallpaperSize.y;
+ width = defaultWallpaperSize.x;
+ height = defaultWallpaperSize.y;
}
}
- if (savedWidth != wallpaperManager.getDesiredMinimumWidth() ||
- savedHeight != wallpaperManager.getDesiredMinimumHeight()) {
- wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight);
+ WallpaperManager wm = WallpaperManager.getInstance(activity);
+ if (width != wm.getDesiredMinimumWidth() || height != wm.getDesiredMinimumHeight()) {
+ wm.suggestDesiredDimensions(width, height);
+ }
+ }
+
+ public static void suggestWallpaperDimension(Activity activity) {
+ // Only live wallpapers care about desired size. Update the size to what launcher expects.
+ final Point size = getDefaultWallpaperSize(
+ activity.getResources(), activity.getWindowManager());
+ WallpaperManager wm = WallpaperManager.getInstance(activity);
+ if (size.x != wm.getDesiredMinimumWidth() || size.y != wm.getDesiredMinimumHeight()) {
+ wm.suggestDesiredDimensions(size.x, size.y);
}
}
@@ -64,7 +98,7 @@ public final class WallpaperUtils {
* As a ratio of screen height, the total distance we want the parallax effect to span
* horizontally
*/
- public static float wallpaperTravelToScreenWidthRatio(int width, int height) {
+ private static float wallpaperTravelToScreenWidthRatio(int width, int height) {
float aspectRatio = width / (float) height;
// At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width
@@ -94,19 +128,10 @@ public final class WallpaperUtils {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static Point getDefaultWallpaperSize(Resources res, WindowManager windowManager) {
if (sDefaultWallpaperSize == null) {
- Point minDims = new Point();
- Point maxDims = new Point();
- windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims);
-
- int maxDim = Math.max(maxDims.x, maxDims.y);
- int minDim = Math.max(minDims.x, minDims.y);
-
- if (Utilities.ATLEAST_JB_MR1) {
- Point realSize = new Point();
- windowManager.getDefaultDisplay().getRealSize(realSize);
- maxDim = Math.max(realSize.x, realSize.y);
- minDim = Math.min(realSize.x, realSize.y);
- }
+ Point realSize = new Point();
+ windowManager.getDefaultDisplay().getRealSize(realSize);
+ int maxDim = Math.max(realSize.x, realSize.y);
+ int minDim = Math.min(realSize.x, realSize.y);
// We need to ensure that there is enough extra space in the wallpaper
// for the intended parallax effects
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 94bbd929f..70eceb9ce 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -36,6 +36,7 @@ import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.SimpleOnStylusPressListener;
import com.android.launcher3.R;
import com.android.launcher3.StylusEventHelper;
import com.android.launcher3.WidgetPreviewLoader;
@@ -93,7 +94,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
final Resources r = context.getResources();
mLauncher = (Launcher) context;
- mStylusEventHelper = new StylusEventHelper(this);
+ mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
mDimensionsFormatString = r.getString(R.string.widget_dims_format);
setContainerWidth();
@@ -211,7 +212,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = super.onTouchEvent(ev);
- if (mStylusEventHelper.checkAndPerformStylusEvent(ev)) {
+ if (mStylusEventHelper.onMotionEvent(ev)) {
return true;
}
return handled;
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index 5d3af5254..b47ba84fc 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -10,13 +10,14 @@ import android.util.Log;
import android.view.View;
import com.android.launcher3.AppWidgetResizeFrame;
-import com.android.launcher3.DragController;
-import com.android.launcher3.DragLayer;
import com.android.launcher3.DragSource;
+import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.util.Thunk;
public class WidgetHostViewLoader implements DragController.DragListener {
@@ -46,7 +47,7 @@ public class WidgetHostViewLoader implements DragController.DragListener {
}
@Override
- public void onDragStart(DragSource source, Object info, int dragAction) { }
+ public void onDragStart(DragSource source, ItemInfo info, int dragAction) { }
@Override
public void onDragEnd() {
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index b780f590b..a1472cc9f 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -32,7 +32,6 @@ import com.android.launcher3.BaseContainerView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.DragController;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.Folder;
@@ -45,6 +44,7 @@ import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.WidgetPreviewLoader;
import com.android.launcher3.Workspace;
+import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.util.Thunk;
@@ -89,7 +89,7 @@ public class WidgetsContainerView extends BaseContainerView
super(context, attrs, defStyleAttr);
mLauncher = (Launcher) context;
mDragController = mLauncher.getDragController();
- mAdapter = new WidgetsListAdapter(context, this, this, mLauncher);
+ mAdapter = new WidgetsListAdapter(this, this, mLauncher);
mIconCache = (LauncherAppState.getInstance()).getIconCache();
if (LOGD) {
Log.d(TAG, "WidgetsContainerView constructor");
@@ -255,9 +255,11 @@ public class WidgetsContainerView extends BaseContainerView
// Start the drag
mLauncher.lockScreenOrientation();
- mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, preview, clipAlpha);
mDragController.startDrag(image, preview, this, createItemInfo,
bounds, DragController.DRAG_ACTION_COPY, scale);
+ // This call expects the extra empty screen to already be created, which is why we call it
+ // after mDragController.startDrag().
+ mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, preview, clipAlpha);
preview.recycle();
return true;
@@ -269,7 +271,7 @@ public class WidgetsContainerView extends BaseContainerView
@Override
public boolean supportsFlingToDelete() {
- return false;
+ return true;
}
@Override
@@ -322,7 +324,7 @@ public class WidgetsContainerView extends BaseContainerView
int currentScreen = mLauncher.getCurrentWorkspaceScreen();
Workspace workspace = (Workspace) target;
CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
- ItemInfo itemInfo = (ItemInfo) d.dragInfo;
+ ItemInfo itemInfo = d.dragInfo;
if (layout != null) {
showOutOfSpaceMessage =
!layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index f1cde299d..ac9d62e9a 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -27,7 +27,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
-import android.view.ViewGroup.MarginLayoutParams;
import android.widget.LinearLayout;
import com.android.launcher3.BubbleTextView;
@@ -65,20 +64,17 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> {
private View.OnClickListener mIconClickListener;
private View.OnLongClickListener mIconLongClickListener;
- private static final int PRESET_INDENT_SIZE_TABLET = 56;
- private int mIndent = 0;
+ private final int mIndent;
- public WidgetsListAdapter(Context context,
- View.OnClickListener iconClickListener,
+ public WidgetsListAdapter(View.OnClickListener iconClickListener,
View.OnLongClickListener iconLongClickListener,
Launcher launcher) {
- mLayoutInflater = LayoutInflater.from(context);
+ mLayoutInflater = launcher.getLayoutInflater();
mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener;
mLauncher = launcher;
-
- setContainerHeight();
+ mIndent = launcher.getResources().getDimensionPixelSize(R.dimen.widget_section_indent);
}
public void setWidgetsModel(WidgetsModel w) {
@@ -207,12 +203,4 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> {
}
return mWidgetPreviewLoader;
}
-
- private void setContainerHeight() {
- Resources r = mLauncher.getResources();
- DeviceProfile profile = mLauncher.getDeviceProfile();
- if (profile.isLargeTablet || profile.isTablet) {
- mIndent = Utilities.pxFromDp(PRESET_INDENT_SIZE_TABLET, r.getDisplayMetrics());
- }
- }
}
diff --git a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
index 249559ab9..19bc868a4 100644
--- a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
+++ b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
@@ -16,10 +16,7 @@
package com.android.launcher3.widget;
import android.support.v7.widget.RecyclerView.ViewHolder;
-import android.view.View;
import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
public class WidgetsRowViewHolder extends ViewHolder {