diff options
376 files changed, 5372 insertions, 19917 deletions
diff --git a/Android.mk b/Android.mk index 1dde46bef..077386989 100644 --- a/Android.mk +++ b/Android.mk @@ -24,14 +24,14 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_STATIC_JAVA_LIBRARIES := \ + libWallpaperPicker \ android-support-v4 \ android-support-v7-recyclerview LOCAL_SRC_FILES := $(call all-java-files-under, src) \ - $(call all-java-files-under, WallpaperPicker/src) \ $(call all-proto-files-under, protos) -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/WallpaperPicker/res \ +LOCAL_RESOURCE_DIR := packages/apps/WallpaperPicker/res \ $(LOCAL_PATH)/res \ prebuilts/sdk/current/support/v7/recyclerview/res @@ -41,7 +41,8 @@ LOCAL_PROTOC_OPTIMIZE_TYPE := nano LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/ LOCAL_AAPT_FLAGS := \ --auto-add-overlay \ - --extra-packages android.support.v7.recyclerview + --extra-packages android.support.v7.recyclerview \ + --extra-packages com.android.wallpaperpicker LOCAL_SDK_VERSION := current LOCAL_PACKAGE_NAME := Launcher3 diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 990bde0b1..418b1d2d5 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" /> @@ -78,7 +76,7 @@ android:launchMode="singleTask" android:clearTaskOnLaunch="true" android:stateNotNeeded="true" - android:theme="@style/Theme" + android:theme="@style/LauncherTheme" android:windowSoftInputMode="adjustPan" android:screenOrientation="nosensor" android:configChanges="keyboard|keyboardHidden|navigation" @@ -95,7 +93,7 @@ <activity android:name="com.android.launcher3.WallpaperPickerActivity" - android:theme="@style/Theme.WallpaperPicker" + android:theme="@style/WallpaperTheme.Picker" android:label="@string/pick_wallpaper" android:icon="@mipmap/ic_launcher_wallpaper" android:finishOnCloseSystemDialogs="true" @@ -107,8 +105,8 @@ </activity> <activity - android:name="com.android.launcher3.WallpaperCropActivity" - android:theme="@style/Theme.WallpaperCropper" + android:name="com.android.wallpaperpicker.WallpaperCropActivity" + android:theme="@style/WallpaperTheme" android:label="@string/crop_wallpaper" android:icon="@mipmap/ic_launcher_wallpaper" android:finishOnCloseSystemDialogs="true" @@ -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/AndroidManifest.xml b/WallpaperPicker/AndroidManifest.xml deleted file mode 100644 index cb1457bdc..000000000 --- a/WallpaperPicker/AndroidManifest.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.launcher3" - android:versionCode="1" - android:versionName="1.0" - > - - <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="21" /> - <application/> -</manifest> diff --git a/WallpaperPicker/README b/WallpaperPicker/README deleted file mode 100644 index d8efb07f6..000000000 --- a/WallpaperPicker/README +++ /dev/null @@ -1,4 +0,0 @@ -This project contains the wallpaper picker for Launcher3. It's in a separate -folder to organize the code separately from the rest of the launcher, and has -a manifest so that a separate Eclipse project can exist for the wallpaper -picker (necessary to have the Eclipse build work)
\ No newline at end of file diff --git a/WallpaperPicker/res/anim/fade_out.xml b/WallpaperPicker/res/anim/fade_out.xml deleted file mode 100644 index 2749d9280..000000000 --- a/WallpaperPicker/res/anim/fade_out.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2014 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. ---> - -<!-- startOffset is the same as the duration of the wallpaper_enter animation. We have this delay so - that we don't see the wallpaper changing before fading back to the home screen. --> -<alpha xmlns:android="http://schemas.android.com/apk/res/android" - android:startOffset="@android:integer/config_longAnimTime" - android:duration="@android:integer/config_mediumAnimTime" - android:fromAlpha="1" - android:toAlpha="0"/> - diff --git a/WallpaperPicker/res/drawable-hdpi/ic_actionbar_accept.png b/WallpaperPicker/res/drawable-hdpi/ic_actionbar_accept.png Binary files differdeleted file mode 100755 index 53cf6877e..000000000 --- a/WallpaperPicker/res/drawable-hdpi/ic_actionbar_accept.png +++ /dev/null diff --git a/WallpaperPicker/res/drawable-hdpi/ic_images.png b/WallpaperPicker/res/drawable-hdpi/ic_images.png Binary files differdeleted file mode 100644 index 15e511c89..000000000 --- a/WallpaperPicker/res/drawable-hdpi/ic_images.png +++ /dev/null diff --git a/WallpaperPicker/res/drawable-hdpi/tile_shadow_bottom.9.png b/WallpaperPicker/res/drawable-hdpi/tile_shadow_bottom.9.png Binary files differdeleted file mode 100644 index e80558bad..000000000 --- a/WallpaperPicker/res/drawable-hdpi/tile_shadow_bottom.9.png +++ /dev/null diff --git a/WallpaperPicker/res/drawable-hdpi/tile_shadow_top.9.png b/WallpaperPicker/res/drawable-hdpi/tile_shadow_top.9.png Binary files differdeleted file mode 100644 index 7e93865e7..000000000 --- a/WallpaperPicker/res/drawable-hdpi/tile_shadow_top.9.png +++ /dev/null diff --git a/WallpaperPicker/res/drawable-mdpi/ic_actionbar_accept.png b/WallpaperPicker/res/drawable-mdpi/ic_actionbar_accept.png Binary files differdeleted file mode 100755 index 35cda8e11..000000000 --- a/WallpaperPicker/res/drawable-mdpi/ic_actionbar_accept.png +++ /dev/null diff --git a/WallpaperPicker/res/drawable-mdpi/ic_images.png b/WallpaperPicker/res/drawable-mdpi/ic_images.png Binary files differdeleted file mode 100644 index c4a2229e9..000000000 --- a/WallpaperPicker/res/drawable-mdpi/ic_images.png +++ /dev/null diff --git a/WallpaperPicker/res/drawable-mdpi/tile_shadow_bottom.9.png b/WallpaperPicker/res/drawable-mdpi/tile_shadow_bottom.9.png Binary files differdeleted file mode 100644 index d95787bac..000000000 --- a/WallpaperPicker/res/drawable-mdpi/tile_shadow_bottom.9.png +++ /dev/null diff --git a/WallpaperPicker/res/drawable-mdpi/tile_shadow_top.9.png b/WallpaperPicker/res/drawable-mdpi/tile_shadow_top.9.png Binary files differdeleted file mode 100644 index 8da913cc4..000000000 --- a/WallpaperPicker/res/drawable-mdpi/tile_shadow_top.9.png +++ /dev/null diff --git a/WallpaperPicker/res/drawable-v21/ic_tick.xml b/WallpaperPicker/res/drawable-v21/ic_tick.xml deleted file mode 100644 index 5b270279d..000000000 --- a/WallpaperPicker/res/drawable-v21/ic_tick.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2014 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. ---> - -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:height="48dp" - android:viewportHeight="48" - android:viewportWidth="48" - android:width="48dp" > - - <group> - <path - android:name="tick" - android:fillColor="#FFFFFFFF" - android:pathData="M18 32.34l-8.34-8.34-2.83 2.83 11.17 11.17 24-24-2.83-2.83z" /> - </group> - -</vector>
\ No newline at end of file diff --git a/WallpaperPicker/res/drawable-v21/wallpaper_tile_fg.xml b/WallpaperPicker/res/drawable-v21/wallpaper_tile_fg.xml deleted file mode 100644 index 97cdcd637..000000000 --- a/WallpaperPicker/res/drawable-v21/wallpaper_tile_fg.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2014 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. ---> - -<ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:color="#66FFFFFF" > - - <item - android:id="@android:id/mask" - android:drawable="@android:color/white"/> - <item - android:bottom="23.25dp" - android:left="29.25dp" - android:right="29.25dp" - android:top="23.25dp"> - <selector> - <item - android:drawable="@drawable/ic_tick" - android:state_selected="true"/> - <item - android:drawable="@drawable/ic_tick" - android:state_checked="true"/> - </selector> - </item> - -</ripple>
\ No newline at end of file diff --git a/WallpaperPicker/res/drawable-xhdpi/ic_actionbar_accept.png b/WallpaperPicker/res/drawable-xhdpi/ic_actionbar_accept.png Binary files differdeleted file mode 100755 index b52dc3701..000000000 --- a/WallpaperPicker/res/drawable-xhdpi/ic_actionbar_accept.png +++ /dev/null diff --git a/WallpaperPicker/res/drawable-xhdpi/ic_images.png b/WallpaperPicker/res/drawable-xhdpi/ic_images.png Binary files differdeleted file mode 100644 index 497479291..000000000 --- a/WallpaperPicker/res/drawable-xhdpi/ic_images.png +++ /dev/null diff --git a/WallpaperPicker/res/drawable-xhdpi/tile_shadow_bottom.9.png b/WallpaperPicker/res/drawable-xhdpi/tile_shadow_bottom.9.png Binary files differdeleted file mode 100644 index 81571f3b7..000000000 --- a/WallpaperPicker/res/drawable-xhdpi/tile_shadow_bottom.9.png +++ /dev/null diff --git a/WallpaperPicker/res/drawable-xhdpi/tile_shadow_top.9.png b/WallpaperPicker/res/drawable-xhdpi/tile_shadow_top.9.png Binary files differdeleted file mode 100644 index 8503a59fe..000000000 --- a/WallpaperPicker/res/drawable-xhdpi/tile_shadow_top.9.png +++ /dev/null diff --git a/WallpaperPicker/res/drawable-xxhdpi/ic_actionbar_accept.png b/WallpaperPicker/res/drawable-xxhdpi/ic_actionbar_accept.png Binary files differdeleted file mode 100755 index d9ad51c9b..000000000 --- a/WallpaperPicker/res/drawable-xxhdpi/ic_actionbar_accept.png +++ /dev/null diff --git a/WallpaperPicker/res/drawable-xxhdpi/ic_images.png b/WallpaperPicker/res/drawable-xxhdpi/ic_images.png Binary files differdeleted file mode 100644 index c8b9f757a..000000000 --- a/WallpaperPicker/res/drawable-xxhdpi/ic_images.png +++ /dev/null diff --git a/WallpaperPicker/res/drawable-xxhdpi/tile_shadow_bottom.9.png b/WallpaperPicker/res/drawable-xxhdpi/tile_shadow_bottom.9.png Binary files differdeleted file mode 100644 index 55250f041..000000000 --- a/WallpaperPicker/res/drawable-xxhdpi/tile_shadow_bottom.9.png +++ /dev/null diff --git a/WallpaperPicker/res/drawable-xxhdpi/tile_shadow_top.9.png b/WallpaperPicker/res/drawable-xxhdpi/tile_shadow_top.9.png Binary files differdeleted file mode 100644 index 3f2263364..000000000 --- a/WallpaperPicker/res/drawable-xxhdpi/tile_shadow_top.9.png +++ /dev/null diff --git a/WallpaperPicker/res/drawable-xxxhdpi/ic_images.png b/WallpaperPicker/res/drawable-xxxhdpi/ic_images.png Binary files differdeleted file mode 100644 index a19002e47..000000000 --- a/WallpaperPicker/res/drawable-xxxhdpi/ic_images.png +++ /dev/null diff --git a/WallpaperPicker/res/drawable/wallpaper_tile_fg.xml b/WallpaperPicker/res/drawable/wallpaper_tile_fg.xml deleted file mode 100644 index c66fa50ad..000000000 --- a/WallpaperPicker/res/drawable/wallpaper_tile_fg.xml +++ /dev/null @@ -1,47 +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. ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_checked="true" > - <shape> - <stroke - android:width="2dp" - android:color="#FFFFFFFF" /> - <solid android:color="#33FFFFFF"/> - </shape> - </item> - <item android:state_focused="true" > - <shape> - <stroke - android:width="2dp" - android:color="#FFFFFFFF" /> - </shape> - </item> - <item android:state_pressed="true"> - <shape android:shape="rectangle"> - <solid android:color="#33FFFFFF"/> - </shape> - </item> - <item android:state_selected="true" > - <shape> - <stroke - android:width="2dp" - android:color="#FFFFFFFF" /> - <solid android:color="#33FFFFFF"/> - </shape> - </item> - <item android:drawable="@android:color/transparent" /> -</selector> diff --git a/WallpaperPicker/res/layout/actionbar_set_wallpaper.xml b/WallpaperPicker/res/layout/actionbar_set_wallpaper.xml deleted file mode 100644 index 8e349b732..000000000 --- a/WallpaperPicker/res/layout/actionbar_set_wallpaper.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 2013, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<com.android.launcher3.AlphaDisableableButton - xmlns:android="http://schemas.android.com/apk/res/android" - style="@style/ActionBarSetWallpaperStyle" - android:id="@+id/set_wallpaper_button" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingRight="20dp" - android:drawableLeft="@drawable/ic_actionbar_accept" - android:drawablePadding="8dp" - android:gravity="start|center_vertical" - android:text="@string/wallpaper_instructions"> -</com.android.launcher3.AlphaDisableableButton> diff --git a/WallpaperPicker/res/layout/wallpaper_cropper.xml b/WallpaperPicker/res/layout/wallpaper_cropper.xml deleted file mode 100644 index ffe8df0fb..000000000 --- a/WallpaperPicker/res/layout/wallpaper_cropper.xml +++ /dev/null @@ -1,38 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 2013, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <com.android.launcher3.CropView - android:id="@+id/cropView" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - <ProgressBar - android:id="@+id/loading" - style="?android:attr/progressBarStyleLarge" - android:visibility="invisible" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerInParent="true" - android:indeterminate="true" - android:indeterminateOnly="true" - android:background="@android:color/transparent" /> -</RelativeLayout> diff --git a/WallpaperPicker/res/layout/wallpaper_picker.xml b/WallpaperPicker/res/layout/wallpaper_picker.xml deleted file mode 100644 index 0b970b09f..000000000 --- a/WallpaperPicker/res/layout/wallpaper_picker.xml +++ /dev/null @@ -1,91 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 2013, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:launcher="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="match_parent" > - - <com.android.launcher3.CropView - android:id="@+id/cropView" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - - <ProgressBar - android:id="@+id/loading" - style="?android:attr/progressBarStyleLarge" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:indeterminate="true" - android:indeterminateOnly="true" - android:visibility="invisible" /> - - <LinearLayout - android:id="@+id/wallpaper_strip" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="bottom" - android:fitsSystemWindows="true" - android:orientation="vertical" > - - <View - android:layout_width="match_parent" - android:layout_height="2dp" - android:background="@drawable/tile_shadow_top" /> - - <HorizontalScrollView - android:id="@+id/wallpaper_scroll_container" - android:layout_width="match_parent" - android:layout_height="wrap_content" > - - <LinearLayout - android:id="@+id/master_wallpaper_list" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" > - - <LinearLayout - android:id="@+id/wallpaper_list" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" /> - - <LinearLayout - android:id="@+id/live_wallpaper_list" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" /> - - <LinearLayout - android:id="@+id/third_party_wallpaper_list" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" /> - </LinearLayout> - </HorizontalScrollView> - - <View - android:layout_width="match_parent" - android:layout_height="2dp" - android:background="@drawable/tile_shadow_bottom" /> - </LinearLayout> - -</FrameLayout>
\ No newline at end of file diff --git a/WallpaperPicker/res/layout/wallpaper_picker_image_picker_item.xml b/WallpaperPicker/res/layout/wallpaper_picker_image_picker_item.xml deleted file mode 100644 index dc6524486..000000000 --- a/WallpaperPicker/res/layout/wallpaper_picker_image_picker_item.xml +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2008 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<com.android.launcher3.CheckableFrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="@dimen/wallpaperThumbnailWidth" - android:layout_height="@dimen/wallpaperThumbnailHeight" - android:focusable="true" - android:clickable="true" - android:foreground="@drawable/wallpaper_tile_fg"> - <ImageView - android:id="@+id/wallpaper_image" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@color/wallpaper_picker_translucent_gray" - android:scaleType="centerCrop" /> - <TextView - android:id="@+id/wallpaper_item_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:ellipsize="marquee" - android:gravity="center" - android:layout_gravity="center" - android:text="@string/pick_image" - android:drawableTop="@drawable/ic_images" - android:drawablePadding="4dp" - android:textColor="@android:color/white"/> -</com.android.launcher3.CheckableFrameLayout> diff --git a/WallpaperPicker/res/layout/wallpaper_picker_item.xml b/WallpaperPicker/res/layout/wallpaper_picker_item.xml deleted file mode 100644 index 3f57fcdbd..000000000 --- a/WallpaperPicker/res/layout/wallpaper_picker_item.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2008 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<com.android.launcher3.CheckableFrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="@dimen/wallpaperThumbnailWidth" - android:layout_height="@dimen/wallpaperThumbnailHeight" - android:focusable="true" - android:clickable="true" - android:foreground="@drawable/wallpaper_tile_fg"> - <ImageView - android:id="@+id/wallpaper_image" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:scaleType="centerCrop" /> -</com.android.launcher3.CheckableFrameLayout> diff --git a/WallpaperPicker/res/layout/wallpaper_picker_live_wallpaper_item.xml b/WallpaperPicker/res/layout/wallpaper_picker_live_wallpaper_item.xml deleted file mode 100644 index 2b152fce2..000000000 --- a/WallpaperPicker/res/layout/wallpaper_picker_live_wallpaper_item.xml +++ /dev/null @@ -1,48 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2008 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<com.android.launcher3.CheckableFrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="@dimen/wallpaperThumbnailWidth" - android:layout_height="@dimen/wallpaperThumbnailHeight" - android:focusable="true" - android:clickable="true" - android:foreground="@drawable/wallpaper_tile_fg"> - <ImageView - android:id="@+id/wallpaper_image" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="center" - android:background="@android:color/black" - android:scaleType="centerCrop" /> - <ImageView - android:id="@+id/wallpaper_icon" - android:layout_width="@dimen/wallpaperItemIconSize" - android:layout_height="@dimen/wallpaperItemIconSize" - android:layout_gravity="center" - android:visibility="gone" /> - <TextView - android:id="@+id/wallpaper_item_label" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="marquee" - android:gravity="center" - android:padding="4dp" - android:layout_gravity="bottom" - android:background="@color/wallpaper_picker_translucent_gray" - android:textColor="@android:color/white"/> -</com.android.launcher3.CheckableFrameLayout> diff --git a/WallpaperPicker/res/layout/wallpaper_picker_third_party_item.xml b/WallpaperPicker/res/layout/wallpaper_picker_third_party_item.xml deleted file mode 100644 index a7e3a0c79..000000000 --- a/WallpaperPicker/res/layout/wallpaper_picker_third_party_item.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2008 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<com.android.launcher3.CheckableFrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="@dimen/wallpaperThumbnailWidth" - android:layout_height="@dimen/wallpaperThumbnailHeight" - android:focusable="true" - android:clickable="true" - android:foreground="@drawable/wallpaper_tile_fg"> - <ImageView - android:id="@+id/wallpaper_image" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@color/wallpaper_picker_translucent_gray" - android:scaleType="centerCrop" /> - <TextView - android:id="@+id/wallpaper_item_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:ellipsize="marquee" - android:gravity="center" - android:layout_gravity="center" - android:drawablePadding="4dp" - android:textColor="@android:color/white"/> -</com.android.launcher3.CheckableFrameLayout> diff --git a/WallpaperPicker/res/mipmap-hdpi/ic_launcher_wallpaper.png b/WallpaperPicker/res/mipmap-hdpi/ic_launcher_wallpaper.png Binary files differdeleted file mode 100644 index affee851d..000000000 --- a/WallpaperPicker/res/mipmap-hdpi/ic_launcher_wallpaper.png +++ /dev/null diff --git a/WallpaperPicker/res/mipmap-mdpi/ic_launcher_wallpaper.png b/WallpaperPicker/res/mipmap-mdpi/ic_launcher_wallpaper.png Binary files differdeleted file mode 100644 index cb4443bdb..000000000 --- a/WallpaperPicker/res/mipmap-mdpi/ic_launcher_wallpaper.png +++ /dev/null diff --git a/WallpaperPicker/res/mipmap-xhdpi/ic_launcher_wallpaper.png b/WallpaperPicker/res/mipmap-xhdpi/ic_launcher_wallpaper.png Binary files differdeleted file mode 100644 index 60f8dceec..000000000 --- a/WallpaperPicker/res/mipmap-xhdpi/ic_launcher_wallpaper.png +++ /dev/null diff --git a/WallpaperPicker/res/mipmap-xxhdpi/ic_launcher_wallpaper.png b/WallpaperPicker/res/mipmap-xxhdpi/ic_launcher_wallpaper.png Binary files differdeleted file mode 100644 index 023fb5886..000000000 --- a/WallpaperPicker/res/mipmap-xxhdpi/ic_launcher_wallpaper.png +++ /dev/null diff --git a/WallpaperPicker/res/values-af/strings.xml b/WallpaperPicker/res/values-af/strings.xml deleted file mode 100644 index bc87fe121..000000000 --- a/WallpaperPicker/res/values-af/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Stel muurpapier"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Kon nie prent laai nie"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Kon nie prent as muurpapier laai nie"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Kon nie prent as muurpapier stel nie"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d gekies"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d gekies"</item> - <item quantity="other" msgid="479468347731745357">"%1$d gekies"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Muurpapier %1$d van %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Het <xliff:g id="LABEL">%1$s</xliff:g> gekies"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Vee uit"</string> - <string name="pick_image" msgid="3189640419551368385">"My foto\'s"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Muurpapiere"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Snoei muurpapier"</string> -</resources> diff --git a/WallpaperPicker/res/values-am/strings.xml b/WallpaperPicker/res/values-am/strings.xml deleted file mode 100644 index 23bf538e3..000000000 --- a/WallpaperPicker/res/values-am/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d ተመርጧል"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"ልጣፍ %1$d የ%2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> ተመርጧል"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"ሰርዝ"</string> - <string name="pick_image" msgid="3189640419551368385">"የእኔ ፎቶዎች"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"የግድግዳ ወረቀቶች"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"ልጣፍ ይከርክሙ"</string> -</resources> diff --git a/WallpaperPicker/res/values-ar/strings.xml b/WallpaperPicker/res/values-ar/strings.xml deleted file mode 100644 index db6834c9a..000000000 --- a/WallpaperPicker/res/values-ar/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"تم تحديد %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"الخلفية %1$d من %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"تم تحديد <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"حذف"</string> - <string name="pick_image" msgid="3189640419551368385">"صوري"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"الخلفيات"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"اقتصاص الخلفية"</string> -</resources> diff --git a/WallpaperPicker/res/values-az-rAZ/strings.xml b/WallpaperPicker/res/values-az-rAZ/strings.xml deleted file mode 100644 index 883673dd6..000000000 --- a/WallpaperPicker/res/values-az-rAZ/strings.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d seçilib"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Divar kağızı %1$d of %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> seçilib"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Sil"</string> - <string name="pick_image" msgid="3189640419551368385">"Fotolarım"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Divar kağızları"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Divar kağızını kəsin"</string> -</resources> diff --git a/WallpaperPicker/res/values-bg/strings.xml b/WallpaperPicker/res/values-bg/strings.xml deleted file mode 100644 index bf2a83b1e..000000000 --- a/WallpaperPicker/res/values-bg/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"Избрахте %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Тапет %1$d от %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Избрахте <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Изтриване"</string> - <string name="pick_image" msgid="3189640419551368385">"Моите снимки"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Тапети"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Подрязване на тапета"</string> -</resources> diff --git a/WallpaperPicker/res/values-bn-rBD/strings.xml b/WallpaperPicker/res/values-bn-rBD/strings.xml deleted file mode 100644 index 1c4d3d493..000000000 --- a/WallpaperPicker/res/values-bn-rBD/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$dটি নির্বাচন করা হয়েছে"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%2$dটির মধ্যে %1$dটি ওয়ালপেপার"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> নির্বাচন করা হয়েছে"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"মুছুন"</string> - <string name="pick_image" msgid="3189640419551368385">"আমার ফটো"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"ওয়ালপেপারগুলি"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"ওয়ালপেপার কাটছাঁট করুন"</string> -</resources> diff --git a/WallpaperPicker/res/values-ca/strings.xml b/WallpaperPicker/res/values-ca/strings.xml deleted file mode 100644 index 9533ae918..000000000 --- a/WallpaperPicker/res/values-ca/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Estableix el fons de pantalla"</string> - <string name="image_load_fail" msgid="7538534580694411837">"No s\'ha pogut carregar la imatge."</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"No s\'ha pogut carregar la imatge com a fons de pantalla."</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"No s\'ha pogut definir la imatge com a fons de pantalla"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"Seleccionats: %1$d"</item> - <item quantity="one" msgid="8409622005831789373">"Seleccionats: %1$d"</item> - <item quantity="other" msgid="479468347731745357">"Seleccionats: %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Fons de pantalla %1$d de %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"S\'ha seleccionat <xliff:g id="LABEL">%1$s</xliff:g>."</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Suprimeix"</string> - <string name="pick_image" msgid="3189640419551368385">"Les meves fotos"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Fons de pantalla"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Retallar fons de pantalla"</string> -</resources> diff --git a/WallpaperPicker/res/values-cs/strings.xml b/WallpaperPicker/res/values-cs/strings.xml deleted file mode 100644 index aab8cc8b3..000000000 --- a/WallpaperPicker/res/values-cs/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Nastavit jako tapetu"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Obrázek nelze načíst."</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Obrázek nelze načíst jako tapetu."</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Obrázek nelze nastavit jako tapetu"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"Vybráno: %1$d"</item> - <item quantity="one" msgid="8409622005831789373">"Vybráno: %1$d"</item> - <item quantity="other" msgid="479468347731745357">"Vybráno: %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Tapeta %1$d z %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Vybrána položka <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Smazat"</string> - <string name="pick_image" msgid="3189640419551368385">"Moje fotografie"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Tapety"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Oříznutí tapety"</string> -</resources> diff --git a/WallpaperPicker/res/values-da/strings.xml b/WallpaperPicker/res/values-da/strings.xml deleted file mode 100644 index 10ef5b3da..000000000 --- a/WallpaperPicker/res/values-da/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Angiv baggrund"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Billedet kunne ikke indlæses"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Billedet kunne ikke indlæses som baggrund"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Billedet kunne ikke indlæses som baggrund"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d er valgt"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d er valgt"</item> - <item quantity="other" msgid="479468347731745357">"%1$d er valgt"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Baggrund %1$d af %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> blev valgt"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Slet"</string> - <string name="pick_image" msgid="3189640419551368385">"Mine billeder"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Baggrunde"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Beskær baggrunden"</string> -</resources> diff --git a/WallpaperPicker/res/values-de/strings.xml b/WallpaperPicker/res/values-de/strings.xml deleted file mode 100644 index be35b736b..000000000 --- a/WallpaperPicker/res/values-de/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Hintergrund auswählen"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Bild konnte nicht geladen werden."</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Bild konnte nicht als Hintergrund geladen werden."</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Bild konnte nicht als Hintergrund festgelegt werden."</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d ausgewählt"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d ausgewählt"</item> - <item quantity="other" msgid="479468347731745357">"%1$d ausgewählt"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Hintergrund %1$d von %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> ausgewählt"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Löschen"</string> - <string name="pick_image" msgid="3189640419551368385">"Meine Fotos"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Hintergründe"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Hintergrund zuschneiden"</string> -</resources> diff --git a/WallpaperPicker/res/values-el/strings.xml b/WallpaperPicker/res/values-el/strings.xml deleted file mode 100644 index 1d799a86d..000000000 --- a/WallpaperPicker/res/values-el/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d επιλεγμένα"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Ταπετσαρία %1$d από %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Επιλέχθηκε το <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Διαγραφή"</string> - <string name="pick_image" msgid="3189640419551368385">"Οι φωτογραφίες μου"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Ταπετσαρίες"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Περικοπή ταπετσαρίας"</string> -</resources> diff --git a/WallpaperPicker/res/values-en-rAU/strings.xml b/WallpaperPicker/res/values-en-rAU/strings.xml deleted file mode 100644 index a384ff608..000000000 --- a/WallpaperPicker/res/values-en-rAU/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Set wallpaper"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Couldn\'t load image"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Couldn\'t load image as wallpaper"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Couldn\'t set image as wallpaper"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d selected"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d selected"</item> - <item quantity="other" msgid="479468347731745357">"%1$d selected"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Wallpaper %1$d of %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Selected <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Delete"</string> - <string name="pick_image" msgid="3189640419551368385">"My photos"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Wallpapers"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Crop wallpaper"</string> -</resources> diff --git a/WallpaperPicker/res/values-en-rGB/strings.xml b/WallpaperPicker/res/values-en-rGB/strings.xml deleted file mode 100644 index a384ff608..000000000 --- a/WallpaperPicker/res/values-en-rGB/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Set wallpaper"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Couldn\'t load image"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Couldn\'t load image as wallpaper"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Couldn\'t set image as wallpaper"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d selected"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d selected"</item> - <item quantity="other" msgid="479468347731745357">"%1$d selected"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Wallpaper %1$d of %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Selected <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Delete"</string> - <string name="pick_image" msgid="3189640419551368385">"My photos"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Wallpapers"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Crop wallpaper"</string> -</resources> diff --git a/WallpaperPicker/res/values-en-rIN/strings.xml b/WallpaperPicker/res/values-en-rIN/strings.xml deleted file mode 100644 index a384ff608..000000000 --- a/WallpaperPicker/res/values-en-rIN/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Set wallpaper"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Couldn\'t load image"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Couldn\'t load image as wallpaper"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Couldn\'t set image as wallpaper"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d selected"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d selected"</item> - <item quantity="other" msgid="479468347731745357">"%1$d selected"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Wallpaper %1$d of %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Selected <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Delete"</string> - <string name="pick_image" msgid="3189640419551368385">"My photos"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Wallpapers"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Crop wallpaper"</string> -</resources> diff --git a/WallpaperPicker/res/values-es-rUS/strings.xml b/WallpaperPicker/res/values-es-rUS/strings.xml deleted file mode 100644 index 924ea5615..000000000 --- a/WallpaperPicker/res/values-es-rUS/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Establecer como fondo de pantalla"</string> - <string name="image_load_fail" msgid="7538534580694411837">"No se pudo cargar la imagen."</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"No se pudo cargar la imagen como fondo de pantalla."</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"No se pudo establecer la imagen como fondo de pantalla"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d seleccionado"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d seleccionado"</item> - <item quantity="other" msgid="479468347731745357">"%1$d seleccionados"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Fondo de pantalla %1$d de %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> seleccionado"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Eliminar"</string> - <string name="pick_image" msgid="3189640419551368385">"Mis fotos"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Fondos de pantalla"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Recortar fondo de pantalla"</string> -</resources> diff --git a/WallpaperPicker/res/values-es/strings.xml b/WallpaperPicker/res/values-es/strings.xml deleted file mode 100644 index 8ecd3f4c3..000000000 --- a/WallpaperPicker/res/values-es/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Establecer fondo"</string> - <string name="image_load_fail" msgid="7538534580694411837">"No se ha podido cargar la imagen"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"No se ha podido cargar la imagen como fondo de pantalla"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"No se ha podido establecer la imagen como fondo de pantalla"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"Seleccionados: %1$d"</item> - <item quantity="one" msgid="8409622005831789373">"Seleccionados: %1$d"</item> - <item quantity="other" msgid="479468347731745357">"Seleccionados: %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Fondo de pantalla %1$d de %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> seleccionado"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Eliminar"</string> - <string name="pick_image" msgid="3189640419551368385">"Mis fotos"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Fondos de pantalla"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Recortar fondo de pantalla"</string> -</resources> diff --git a/WallpaperPicker/res/values-et-rEE/strings.xml b/WallpaperPicker/res/values-et-rEE/strings.xml deleted file mode 100644 index 59ca7704c..000000000 --- a/WallpaperPicker/res/values-et-rEE/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Määra taustapilt"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Kujutist ei õnnestunud laadida"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Kujutist ei õnnestunud taustapildina laadida"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Kujutist ei õnnestunud taustapildiks määrata"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"Valitud on %1$d"</item> - <item quantity="one" msgid="8409622005831789373">"Valitud on %1$d"</item> - <item quantity="other" msgid="479468347731745357">"Valitud on %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d/%2$d taustapildist"</string> - <string name="announce_selection" msgid="123723511662250539">"Valitud on <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Kustuta"</string> - <string name="pick_image" msgid="3189640419551368385">"Minu fotod"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Taustapildid"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Taustapildi kärpimine"</string> -</resources> diff --git a/WallpaperPicker/res/values-eu-rES/strings.xml b/WallpaperPicker/res/values-eu-rES/strings.xml deleted file mode 100644 index fb77b1acd..000000000 --- a/WallpaperPicker/res/values-eu-rES/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Ezarri horma-papera"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Ezin izan da irudia kargatu"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Ezin izan da irudia horma-paper gisa kargatu"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Ezin izan da ezarri irudia horma-paper gisa"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d hautatuta"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d hautatuta"</item> - <item quantity="other" msgid="479468347731745357">"%1$d hautatuta"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d/%2$d horma-papera"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> hautatu da"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Ezabatu"</string> - <string name="pick_image" msgid="3189640419551368385">"Nire argazkiak"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Horma-paperak"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Ebaki horma-papera"</string> -</resources> diff --git a/WallpaperPicker/res/values-fa/strings.xml b/WallpaperPicker/res/values-fa/strings.xml deleted file mode 100644 index da4b7a10d..000000000 --- a/WallpaperPicker/res/values-fa/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d انتخاب شد"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"کاغذدیواری %1$d از %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> انتخاب شد"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"حذف"</string> - <string name="pick_image" msgid="3189640419551368385">"عکسهای من"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"کاغذدیواریها"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"برش کاغذدیواری"</string> -</resources> diff --git a/WallpaperPicker/res/values-fi/strings.xml b/WallpaperPicker/res/values-fi/strings.xml deleted file mode 100644 index 3c8f1f538..000000000 --- a/WallpaperPicker/res/values-fi/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Aseta taustakuva"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Kuvan lataus epäonnistui"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Kuvaa ei voitu ladata taustakuvaksi"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Kuvan asettaminen taustakuvaksi epäonnistui."</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d valittu"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d valittu"</item> - <item quantity="other" msgid="479468347731745357">"%1$d valittu"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Taustakuva %1$d/%2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Valittu: <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Poista"</string> - <string name="pick_image" msgid="3189640419551368385">"Omat valokuvat"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Taustakuvat"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Rajaa taustakuva"</string> -</resources> diff --git a/WallpaperPicker/res/values-fr-rCA/strings.xml b/WallpaperPicker/res/values-fr-rCA/strings.xml deleted file mode 100644 index ba1d43000..000000000 --- a/WallpaperPicker/res/values-fr-rCA/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Définir le fond d\'écran"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Impossible de charger l\'image"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Impossible de charger l\'image comme fond d\'écran"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Impossible d\'utiliser l\'image comme fond d\'écran"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d sélectionné"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d sélectionné"</item> - <item quantity="other" msgid="479468347731745357">"%1$d sélectionné(s)"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Fond d\'écran %1$d de %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Sélection : <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Supprimer"</string> - <string name="pick_image" msgid="3189640419551368385">"Mes photos"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Fonds d\'écran"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Rogner le fond d\'écran"</string> -</resources> diff --git a/WallpaperPicker/res/values-fr/strings.xml b/WallpaperPicker/res/values-fr/strings.xml deleted file mode 100644 index 9f3b52531..000000000 --- a/WallpaperPicker/res/values-fr/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Définir comme fond d\'écran"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Impossible de charger l\'image."</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Impossible de charger l\'image comme fond d\'écran."</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Impossible de définir l\'image comme fond d\'écran."</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d élément sélectionné"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d élément sélectionné"</item> - <item quantity="other" msgid="479468347731745357">"%1$d éléments sélectionnés"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Fond d\'écran %1$d sur %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> sélectionné"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Supprimer"</string> - <string name="pick_image" msgid="3189640419551368385">"Mes photos"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Fonds d\'écran"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Recadrer le fond d\'écran"</string> -</resources> diff --git a/WallpaperPicker/res/values-gl-rES/strings.xml b/WallpaperPicker/res/values-gl-rES/strings.xml deleted file mode 100644 index 5805489a7..000000000 --- a/WallpaperPicker/res/values-gl-rES/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Establecer fondo de pantalla"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Non se puido cargar a imaxe"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Non se puido cargar a imaxe como fondo de pantalla"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Non se puido definir a imaxe como fondo de pantalla"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"Seleccionaches %1$d"</item> - <item quantity="one" msgid="8409622005831789373">"Seleccionaches %1$d"</item> - <item quantity="other" msgid="479468347731745357">"Seleccionaches %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Fondo de pantalla %1$d de %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Seleccionaches <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Eliminar"</string> - <string name="pick_image" msgid="3189640419551368385">"As miñas fotos"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Fondos de pantalla"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Recortar fondo de pantalla"</string> -</resources> diff --git a/WallpaperPicker/res/values-gu-rIN/strings.xml b/WallpaperPicker/res/values-gu-rIN/strings.xml deleted file mode 100644 index e201d523f..000000000 --- a/WallpaperPicker/res/values-gu-rIN/strings.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"વૉલપેપર સેટ કરો"</string> - <string name="image_load_fail" msgid="7538534580694411837">"છબી લોડ કરી શકાઈ નથી"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"વૉલપેપર તરીકે છબી લોડ કરી શકાઈ નથી"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d પસંદ કર્યો"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d પસંદ કર્યો"</item> - <item quantity="other" msgid="479468347731745357">"%1$d પસંદ કર્યો"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%2$d માંથી %1$d વૉલપેપર"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> પસંદ કર્યો"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"કાઢી નાખો"</string> - <string name="pick_image" msgid="3189640419551368385">"મારા ફોટા"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"વૉલપેપર્સ"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"વૉલપેપર કાપો"</string> -</resources> diff --git a/WallpaperPicker/res/values-hi/strings.xml b/WallpaperPicker/res/values-hi/strings.xml deleted file mode 100644 index 6f610b5bb..000000000 --- a/WallpaperPicker/res/values-hi/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d चयनित"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"वॉलपेपर %2$d में से %1$d"</string> - <string name="announce_selection" msgid="123723511662250539">"चयनित <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"हटाएं"</string> - <string name="pick_image" msgid="3189640419551368385">"मेरी फ़ोटो"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"वॉलपेपर"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"वॉलपेपर काटें"</string> -</resources> diff --git a/WallpaperPicker/res/values-hr/strings.xml b/WallpaperPicker/res/values-hr/strings.xml deleted file mode 100644 index aaf5e1bcd..000000000 --- a/WallpaperPicker/res/values-hr/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Postavi pozadinu"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Nije moguće učitati sliku"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Nije moguće učitati sliku kao pozadinu"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Postavljanje slike kao pozadine nije uspjelo"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"Odabrano je %1$d stavki"</item> - <item quantity="one" msgid="8409622005831789373">"Odabrana je %1$d stavka"</item> - <item quantity="other" msgid="479468347731745357">"Odabrano stavki: %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d. pozadinska slika od %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Odabrana je <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Izbriši"</string> - <string name="pick_image" msgid="3189640419551368385">"Moje fotografije"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Pozadine"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Obrezivanje pozadinske slike"</string> -</resources> diff --git a/WallpaperPicker/res/values-hu/strings.xml b/WallpaperPicker/res/values-hu/strings.xml deleted file mode 100644 index 06c0952a4..000000000 --- a/WallpaperPicker/res/values-hu/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Háttérkép beállítása"</string> - <string name="image_load_fail" msgid="7538534580694411837">"A kép betöltése nem sikerült"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"A kép betöltése háttérképként nem sikerült"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"A kép beállítása háttérképként nem sikerült"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d kiválasztva"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d kiválasztva"</item> - <item quantity="other" msgid="479468347731745357">"%1$d kiválasztva"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d/%2$d. háttérkép"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> kiválasztva"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Törlés"</string> - <string name="pick_image" msgid="3189640419551368385">"Saját fotók"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Háttérképek"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Háttérkép körbevágása"</string> -</resources> diff --git a/WallpaperPicker/res/values-hy-rAM/strings.xml b/WallpaperPicker/res/values-hy-rAM/strings.xml deleted file mode 100644 index f68d49f90..000000000 --- a/WallpaperPicker/res/values-hy-rAM/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d ընտրված"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d պաստառ՝ %2$d-ից"</string> - <string name="announce_selection" msgid="123723511662250539">"Ընտրված է <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Ջնջել"</string> - <string name="pick_image" msgid="3189640419551368385">"Իմ լուսանկարները"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Պաստառներ"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Եզրատել պաստառը"</string> -</resources> diff --git a/WallpaperPicker/res/values-in/strings.xml b/WallpaperPicker/res/values-in/strings.xml deleted file mode 100644 index 634eb1f76..000000000 --- a/WallpaperPicker/res/values-in/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Setel wallpaper"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Tidak dapat memuat gambar"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Tidak dapat memuat gambar sebagai wallpaper"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Tidak dapat menyetel gambar sebagai wallpaper"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d dipilih"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d dipilih"</item> - <item quantity="other" msgid="479468347731745357">"%1$d dipilih"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Wallpaper %1$d dari %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> terpilih"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Hapus"</string> - <string name="pick_image" msgid="3189640419551368385">"Foto saya"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Wallpaper"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Pangkas wallpaper"</string> -</resources> diff --git a/WallpaperPicker/res/values-is-rIS/strings.xml b/WallpaperPicker/res/values-is-rIS/strings.xml deleted file mode 100644 index eac44ec2a..000000000 --- a/WallpaperPicker/res/values-is-rIS/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Velja veggfóður"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Ekki var hægt að hlaða mynd"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Ekki var hægt að hlaða mynd sem veggfóður"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Ekki var hægt að nota mynd sem veggfóður"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d valin"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d valið"</item> - <item quantity="other" msgid="479468347731745357">"%1$d valin"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Veggfóður %1$d af %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> valið"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Eyða"</string> - <string name="pick_image" msgid="3189640419551368385">"Myndirnar mínar"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Veggfóður"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Skera veggfóður"</string> -</resources> diff --git a/WallpaperPicker/res/values-it/strings.xml b/WallpaperPicker/res/values-it/strings.xml deleted file mode 100644 index fdb0ce8de..000000000 --- a/WallpaperPicker/res/values-it/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Imposta sfondo"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Impossibile caricare l\'immagine"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Impossibile caricare l\'immagine come sfondo"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Impossibile impostare l\'immagine come sfondo"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d selezionati"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d selezionato"</item> - <item quantity="other" msgid="479468347731745357">"%1$d selezionati"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Sfondo %1$d di %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Elemento selezionato: <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Elimina"</string> - <string name="pick_image" msgid="3189640419551368385">"Le mie foto"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Sfondi"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Ritaglia sfondo"</string> -</resources> diff --git a/WallpaperPicker/res/values-iw/strings.xml b/WallpaperPicker/res/values-iw/strings.xml deleted file mode 100644 index c6a583d0e..000000000 --- a/WallpaperPicker/res/values-iw/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d נבחרו"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"טפט %1$d מתוך %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"בחרת <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"מחק"</string> - <string name="pick_image" msgid="3189640419551368385">"התמונות שלי"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"טפטים"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"חתוך את הטפט"</string> -</resources> diff --git a/WallpaperPicker/res/values-ja/strings.xml b/WallpaperPicker/res/values-ja/strings.xml deleted file mode 100644 index f18da5d42..000000000 --- a/WallpaperPicker/res/values-ja/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d個選択済み"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"壁紙: %1$d/%2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"選択: <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"削除"</string> - <string name="pick_image" msgid="3189640419551368385">"マイフォト"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"壁紙"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"壁紙のトリミング"</string> -</resources> diff --git a/WallpaperPicker/res/values-ka-rGE/strings.xml b/WallpaperPicker/res/values-ka-rGE/strings.xml deleted file mode 100644 index 1f652823f..000000000 --- a/WallpaperPicker/res/values-ka-rGE/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"არჩეულია %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"ფონი %1$d %2$d-დან"</string> - <string name="announce_selection" msgid="123723511662250539">"არჩეული <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"წაშლა"</string> - <string name="pick_image" msgid="3189640419551368385">"ჩემი ფოტოები"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"ფონები"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"ფონის ჩამოჭრა"</string> -</resources> diff --git a/WallpaperPicker/res/values-kk-rKZ/strings.xml b/WallpaperPicker/res/values-kk-rKZ/strings.xml deleted file mode 100644 index 6f4ee7ca2..000000000 --- a/WallpaperPicker/res/values-kk-rKZ/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d таңдалған"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d артқы фон, барлығы %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> таңдалған"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Жою"</string> - <string name="pick_image" msgid="3189640419551368385">"Менің фотосуреттерім"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Артқы фондар"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Артқы фонды кесу"</string> -</resources> diff --git a/WallpaperPicker/res/values-km-rKH/strings.xml b/WallpaperPicker/res/values-km-rKH/strings.xml deleted file mode 100644 index 801260d3f..000000000 --- a/WallpaperPicker/res/values-km-rKH/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"បានជ្រើស %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"ផ្ទាំងរូបភាព %1$d នៃ %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"បានជ្រើស <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"លុប"</string> - <string name="pick_image" msgid="3189640419551368385">"រូបថតរបស់ខ្ញុំ"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"ផ្ទាំងរូបភាព"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"ច្រឹបផ្ទាំងរូបភាព"</string> -</resources> diff --git a/WallpaperPicker/res/values-kn-rIN/strings.xml b/WallpaperPicker/res/values-kn-rIN/strings.xml deleted file mode 100644 index 7d4d7e70e..000000000 --- a/WallpaperPicker/res/values-kn-rIN/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%2$d ರಲ್ಲಿ %1$d ವಾಲ್ಪೇಪರ್"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"ಅಳಿಸು"</string> - <string name="pick_image" msgid="3189640419551368385">"ನನ್ನ ಫೋಟೋಗಳು"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"ವಾಲ್ಪೇಪರ್ಗಳು"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"ವಾಲ್ಪೇಪರ್ ಕತ್ತರಿಸಿ"</string> -</resources> diff --git a/WallpaperPicker/res/values-ko/strings.xml b/WallpaperPicker/res/values-ko/strings.xml deleted file mode 100644 index f4072946d..000000000 --- a/WallpaperPicker/res/values-ko/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d개 선택됨"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"배경화면 %1$d/%2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> 선택함"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"삭제"</string> - <string name="pick_image" msgid="3189640419551368385">"내 사진"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"배경화면"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"배경화면 잘라내기"</string> -</resources> diff --git a/WallpaperPicker/res/values-ky-rKG/strings.xml b/WallpaperPicker/res/values-ky-rKG/strings.xml deleted file mode 100644 index f53f52b29..000000000 --- a/WallpaperPicker/res/values-ky-rKG/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d тандалды"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%2$d ичинен %1$d тушкагаз"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> тандалды"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Жок кылуу"</string> - <string name="pick_image" msgid="3189640419551368385">"Менин сүрөттөрүм"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Тушкагаздар"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Тушкагазды тегиздөө"</string> -</resources> diff --git a/WallpaperPicker/res/values-lo-rLA/strings.xml b/WallpaperPicker/res/values-lo-rLA/strings.xml deleted file mode 100644 index 1da6d29c4..000000000 --- a/WallpaperPicker/res/values-lo-rLA/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"ເລືອກ %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"ພາບພື້ນຫຼັງ %1$d ໃນ %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"ເລືອກ <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"ລຶບ"</string> - <string name="pick_image" msgid="3189640419551368385">"ຮູບຂອງຂ້ອຍ"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"ພາບພື້ນຫຼັງ"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"ຕັດພາບພື້ນຫຼັງ"</string> -</resources> diff --git a/WallpaperPicker/res/values-lt/strings.xml b/WallpaperPicker/res/values-lt/strings.xml deleted file mode 100644 index 98c9a365c..000000000 --- a/WallpaperPicker/res/values-lt/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Nustatyti ekrano foną"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Nepavyko įkelti vaizdo"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Nepavyko įkelti vaizdo kaip ekrano fono"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Nepavyko nustatyti vaizdo kaip ekrano fono"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"Pasirinkta: %1$d"</item> - <item quantity="one" msgid="8409622005831789373">"Pasirinkta: %1$d"</item> - <item quantity="other" msgid="479468347731745357">"Pasirinkta: %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d ekrano fonas iš %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Pasirinkta: <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Ištrinti"</string> - <string name="pick_image" msgid="3189640419551368385">"Mano nuotraukos"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Ekrano fonai"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Apkirpti ekrano foną"</string> -</resources> diff --git a/WallpaperPicker/res/values-lv/strings.xml b/WallpaperPicker/res/values-lv/strings.xml deleted file mode 100644 index ff7876c72..000000000 --- a/WallpaperPicker/res/values-lv/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Iestatīt fona tapeti"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Nevarēja ielādēt attēlu."</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Nevarēja ielādēt attēlu kā fona tapeti."</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Nevarēja iestatīt attēlu kā fona tapeti."</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"Atlasīts: %1$d"</item> - <item quantity="one" msgid="8409622005831789373">"Atlasīta: %1$d"</item> - <item quantity="other" msgid="479468347731745357">"Atlasītas: %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d. fona tapete no %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Atlasīta: <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Dzēst"</string> - <string name="pick_image" msgid="3189640419551368385">"Mani fotoattēli"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Fona tapetes"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Apgriezt fona tapeti"</string> -</resources> diff --git a/WallpaperPicker/res/values-mk-rMK/strings.xml b/WallpaperPicker/res/values-mk-rMK/strings.xml deleted file mode 100644 index 13b38cd1d..000000000 --- a/WallpaperPicker/res/values-mk-rMK/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"Избрано %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Тапет %1$d од %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Избран <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Избриши"</string> - <string name="pick_image" msgid="3189640419551368385">"Моите фотографии"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Тапети"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Исечи тапет"</string> -</resources> diff --git a/WallpaperPicker/res/values-ml-rIN/strings.xml b/WallpaperPicker/res/values-ml-rIN/strings.xml deleted file mode 100644 index 5831b36b9..000000000 --- a/WallpaperPicker/res/values-ml-rIN/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d തിരഞ്ഞെടുത്തു"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d / %2$d വാൾപേപ്പർ"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> തിരഞ്ഞെടുത്തു"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"ഇല്ലാതാക്കുക"</string> - <string name="pick_image" msgid="3189640419551368385">"എന്റെ ഫോട്ടോകൾ"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"വാൾപേപ്പറുകൾ"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"വാൾപേപ്പറിന്റെ വലുപ്പം മാറ്റൽ"</string> -</resources> diff --git a/WallpaperPicker/res/values-mn-rMN/strings.xml b/WallpaperPicker/res/values-mn-rMN/strings.xml deleted file mode 100644 index 9995547d5..000000000 --- a/WallpaperPicker/res/values-mn-rMN/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d сонгогдсон"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%2$d ханын цаасны %1$d нь"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> сонгогдсон"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Устгах"</string> - <string name="pick_image" msgid="3189640419551368385">"Миний зураг"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Ханын зураг"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Ханын зургийг тайрах"</string> -</resources> diff --git a/WallpaperPicker/res/values-mr-rIN/strings.xml b/WallpaperPicker/res/values-mr-rIN/strings.xml deleted file mode 100644 index d740fd29e..000000000 --- a/WallpaperPicker/res/values-mr-rIN/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d निवडले"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%2$d पैकी %1$d वॉलपेपर"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> निवडले"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"हटवा"</string> - <string name="pick_image" msgid="3189640419551368385">"माझे फोटो"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"वॉलपेपर"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"वॉलपेपर कापा"</string> -</resources> diff --git a/WallpaperPicker/res/values-ms-rMY/strings.xml b/WallpaperPicker/res/values-ms-rMY/strings.xml deleted file mode 100644 index 759e48cec..000000000 --- a/WallpaperPicker/res/values-ms-rMY/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Tetapkan kertas dinding"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Tidak dapat memuatkan imej"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Tidak dapat memuatkan imej sebagai kertas dinding"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Tidak dapat menetapkan imej sebagai kertas dinding"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d dipilih"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d dipilih"</item> - <item quantity="other" msgid="479468347731745357">"%1$d dipilih"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Kertas dinding %1$d daripada %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Memilih <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Padam"</string> - <string name="pick_image" msgid="3189640419551368385">"Foto saya"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Kertas dinding"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Pangkas kertas dinding"</string> -</resources> diff --git a/WallpaperPicker/res/values-my-rMM/strings.xml b/WallpaperPicker/res/values-my-rMM/strings.xml deleted file mode 100644 index 5197b98ea..000000000 --- a/WallpaperPicker/res/values-my-rMM/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d ရွေးချယ်ပြီး"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"နောက်ခံ %1$d မှ %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"ရွေးချယ်ထားသော <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"ဖျက်ပါ"</string> - <string name="pick_image" msgid="3189640419551368385">"ကျွန်ုပ်၏ ဓာတ်ပုံများ"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"နောက်ခံများ"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"နောက်ခံအား ဖြတ်ခြင်း"</string> -</resources> diff --git a/WallpaperPicker/res/values-nb/strings.xml b/WallpaperPicker/res/values-nb/strings.xml deleted file mode 100644 index 8125b53bf..000000000 --- a/WallpaperPicker/res/values-nb/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Angi bakgrunn"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Kunne ikke laste inn bildet"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Kunne ikke laste inn bildet som bakgrunn"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Kunne ikke angi bildet som bakgrunn"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d valgt"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d valgt"</item> - <item quantity="other" msgid="479468347731745357">"%1$d valgt"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Bakgrunn %1$d av %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Valgt <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Slett"</string> - <string name="pick_image" msgid="3189640419551368385">"Mine bilder"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Bakgrunner"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Beskjær bakgrunnen"</string> -</resources> diff --git a/WallpaperPicker/res/values-ne-rNP/strings.xml b/WallpaperPicker/res/values-ne-rNP/strings.xml deleted file mode 100644 index b77a1c5d7..000000000 --- a/WallpaperPicker/res/values-ne-rNP/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d चयन भयो"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%2$d को %1$d वालपेपर"</string> - <string name="announce_selection" msgid="123723511662250539">"चयन गरिएको <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"मेट्नुहोस्"</string> - <string name="pick_image" msgid="3189640419551368385">"मेरा तस्बिरहरू"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"वालपेपरहरु"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"वालपेपर काँटछाट गर्नुहोस्"</string> -</resources> diff --git a/WallpaperPicker/res/values-nl/strings.xml b/WallpaperPicker/res/values-nl/strings.xml deleted file mode 100644 index dc78305aa..000000000 --- a/WallpaperPicker/res/values-nl/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Achtergrond instellen"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Kan afbeelding niet laden"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Kan afbeelding niet laden als achtergrond"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Kan afbeelding niet instellen als achtergrond"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d geselecteerd"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d geselecteerd"</item> - <item quantity="other" msgid="479468347731745357">"%1$d geselecteerd"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Achtergrond %1$d van %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> is geselecteerd"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Verwijderen"</string> - <string name="pick_image" msgid="3189640419551368385">"Mijn foto\'s"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Achtergronden"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Achtergrond bijsnijden"</string> -</resources> diff --git a/WallpaperPicker/res/values-nodpi/wallpapers.xml b/WallpaperPicker/res/values-nodpi/wallpapers.xml deleted file mode 100644 index 1e340e4b2..000000000 --- a/WallpaperPicker/res/values-nodpi/wallpapers.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - --> - -<resources> - <string-array name="wallpapers" translatable="false"> - </string-array> -</resources> diff --git a/WallpaperPicker/res/values-pa-rIN/strings.xml b/WallpaperPicker/res/values-pa-rIN/strings.xml deleted file mode 100644 index e4225e02d..000000000 --- a/WallpaperPicker/res/values-pa-rIN/strings.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"ਵਾਲਪੇਪਰ ਸੈਟ ਕਰੋ"</string> - <string name="image_load_fail" msgid="7538534580694411837">"ਚਿੱਤਰ ਲੋਡ ਨਹੀਂ ਕਰ ਸਕਿਆ"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"ਵਾਲਪੇਪਰ ਦੇ ਤੌਰ ਤੇ ਚਿੱਤਰ ਲੋਡ ਨਹੀਂ ਕਰ ਸਕਿਆ"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d ਚੁਣਿਆ ਗਿਆ"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d ਚੁਣਿਆ ਗਿਆ"</item> - <item quantity="other" msgid="479468347731745357">"%1$d ਚੁਣਿਆ ਗਿਆ"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"ਵਾਲਪੇਪਰ %2$d ਦਾ %1$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> ਚੁਣਿਆ ਗਿਆ"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"ਮਿਟਾਓ"</string> - <string name="pick_image" msgid="3189640419551368385">"ਮੇਰੀਆਂ ਫੋਟੋਆਂ"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"ਵਾਲਪੇਪਰ"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"ਵਾਲਪੇਪਰ ਕੱਟੋ"</string> -</resources> diff --git a/WallpaperPicker/res/values-pl/strings.xml b/WallpaperPicker/res/values-pl/strings.xml deleted file mode 100644 index 9693de46c..000000000 --- a/WallpaperPicker/res/values-pl/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Ustaw tapetę"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Nie udało się załadować obrazu"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Nie udało się załadować obrazu jako tapety"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Nie udało się ustawić obrazu jako tapety"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"Wybranych %1$d"</item> - <item quantity="one" msgid="8409622005831789373">"Wybrana %1$d"</item> - <item quantity="other" msgid="479468347731745357">"Wybrane: %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Tapeta %1$d z %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Wybrano <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Usuń"</string> - <string name="pick_image" msgid="3189640419551368385">"Moje zdjęcia"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Tapety"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Przytnij tapetę"</string> -</resources> diff --git a/WallpaperPicker/res/values-pt-rPT/strings.xml b/WallpaperPicker/res/values-pt-rPT/strings.xml deleted file mode 100644 index 3c4fa9b73..000000000 --- a/WallpaperPicker/res/values-pt-rPT/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Definir imagem fundo"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Não foi possível carregar a imagem"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Não foi possível carregar a imagem como imagem de fundo"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Não foi possível definir a imagem como imagem de fundo"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d selecionadas"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d selecionada"</item> - <item quantity="other" msgid="479468347731745357">"%1$d selecionadas"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Imagem de fundo %1$d de %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> selecionada"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Eliminar"</string> - <string name="pick_image" msgid="3189640419551368385">"As minhas fotos"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Imagens de fundo"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Recortar imagem de fundo"</string> -</resources> diff --git a/WallpaperPicker/res/values-pt/strings.xml b/WallpaperPicker/res/values-pt/strings.xml deleted file mode 100644 index 2520eed3a..000000000 --- a/WallpaperPicker/res/values-pt/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Definir plano de fundo"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Não foi possível carregar a imagem"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Não foi possível carregar a imagem como plano de fundo"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Não foi possível definir a imagem como plano de fundo"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d selecionados"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d selecionado"</item> - <item quantity="other" msgid="479468347731745357">"%1$d selecionados"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Plano de fundo %1$d de %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> selecionado"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Excluir"</string> - <string name="pick_image" msgid="3189640419551368385">"Minhas fotos"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Planos de fundo"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Cortar plano de fundo"</string> -</resources> diff --git a/WallpaperPicker/res/values-ro/strings.xml b/WallpaperPicker/res/values-ro/strings.xml deleted file mode 100644 index f5df3eee2..000000000 --- a/WallpaperPicker/res/values-ro/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Setați imaginea de fundal"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Nu s-a putut încărca imaginea"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Nu s-a putut încărca imaginea ca fundal"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Nu s-a putut seta ca imagine de fundal"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d selectate"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d selectată"</item> - <item quantity="other" msgid="479468347731745357">"%1$d selectate"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Imaginea de fundal %1$d din %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"S-a selectat <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Ștergeți"</string> - <string name="pick_image" msgid="3189640419551368385">"Fotografiile mele"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Imagini de fundal"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Decupați imaginea de fundal"</string> -</resources> diff --git a/WallpaperPicker/res/values-ru/strings.xml b/WallpaperPicker/res/values-ru/strings.xml deleted file mode 100644 index f8c350a1f..000000000 --- a/WallpaperPicker/res/values-ru/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"Выбрано: %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Обои %1$d из %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Выбран элемент \"<xliff:g id="LABEL">%1$s</xliff:g>\""</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Удалить"</string> - <string name="pick_image" msgid="3189640419551368385">"Мои фото"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Обои"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Кадрировать обои"</string> -</resources> diff --git a/WallpaperPicker/res/values-si-rLK/strings.xml b/WallpaperPicker/res/values-si-rLK/strings.xml deleted file mode 100644 index 3945bdf89..000000000 --- a/WallpaperPicker/res/values-si-rLK/strings.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d තෝරා ගන්නා ලදි"</item> - </plurals> - <!-- String.format failed for translation --> - <!-- no translation found for wallpaper_accessibility_name (4093221025304876354) --> - <skip /> - <string name="announce_selection" msgid="123723511662250539">"තෝරාගත්තේ <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"මකන්න"</string> - <string name="pick_image" msgid="3189640419551368385">"මගේ ඡායාරූප"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"වෝල්පේපර"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"වෝල්පේපරය කප්පාදු කිරීම"</string> -</resources> diff --git a/WallpaperPicker/res/values-sk/strings.xml b/WallpaperPicker/res/values-sk/strings.xml deleted file mode 100644 index fb2c819b2..000000000 --- a/WallpaperPicker/res/values-sk/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Nastaviť tapetu"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Obrázok nie je možné načítať"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Obrázok nie je možné načítať ako tapetu"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Obrázok nie je možné nastaviť ako tapetu"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"Počet vybratých položiek: %1$d"</item> - <item quantity="one" msgid="8409622005831789373">"Počet vybratých položiek: %1$d"</item> - <item quantity="other" msgid="479468347731745357">"Počet vybratých položiek: %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Tapeta %1$d z %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Vybratá položka <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Odstrániť"</string> - <string name="pick_image" msgid="3189640419551368385">"Moje fotky"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Tapety"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Orezanie tapety"</string> -</resources> diff --git a/WallpaperPicker/res/values-sl/strings.xml b/WallpaperPicker/res/values-sl/strings.xml deleted file mode 100644 index a7ff0891d..000000000 --- a/WallpaperPicker/res/values-sl/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Nastavi ozadje"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Slike ni bilo mogoče naložiti"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Slike ni bilo mogoče naložiti kot ozadje"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Slike ni bilo mogoče nastaviti kot ozadje"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"Št. izbranih: %1$d"</item> - <item quantity="one" msgid="8409622005831789373">"Št. izbranih: %1$d"</item> - <item quantity="other" msgid="479468347731745357">"Št. izbranih: %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d. ozadje od %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Izbrano: <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Izbriši"</string> - <string name="pick_image" msgid="3189640419551368385">"Moje fotografije"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Ozadja"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Obrezovanje ozadja"</string> -</resources> diff --git a/WallpaperPicker/res/values-sq-rAL/strings.xml b/WallpaperPicker/res/values-sq-rAL/strings.xml deleted file mode 100644 index 8a9983b4b..000000000 --- a/WallpaperPicker/res/values-sq-rAL/strings.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <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> - <item quantity="other" msgid="479468347731745357">"Të përzgjedhur: %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Imazhi i sfondit: %1$d nga gjithsej %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> u përzgjodh"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Fshi"</string> - <string name="pick_image" msgid="3189640419551368385">"Fotografitë e mia"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Imazhet e sfondit"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Prit imazhin e sfondit"</string> -</resources> diff --git a/WallpaperPicker/res/values-sr/strings.xml b/WallpaperPicker/res/values-sr/strings.xml deleted file mode 100644 index 6154526cd..000000000 --- a/WallpaperPicker/res/values-sr/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"Изабраних: %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Позадина %1$d од %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Изабрана је <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Избриши"</string> - <string name="pick_image" msgid="3189640419551368385">"Моје фотографије"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Позадине"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Опсеци позадину"</string> -</resources> diff --git a/WallpaperPicker/res/values-sv/strings.xml b/WallpaperPicker/res/values-sv/strings.xml deleted file mode 100644 index 38062b9ee..000000000 --- a/WallpaperPicker/res/values-sv/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Ange bakgrund"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Det gick inte att läsa in bilden"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Det gick inte att läsa in bilden som bakgrund"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Det gick inte att ange bilden som bakgrund"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d har valts"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d har valts"</item> - <item quantity="other" msgid="479468347731745357">"%1$d har valts"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Bakgrund %1$d av %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> har valts"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Ta bort"</string> - <string name="pick_image" msgid="3189640419551368385">"Mina foton"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Bakgrunder"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Beskär bakgrund"</string> -</resources> diff --git a/WallpaperPicker/res/values-sw/strings.xml b/WallpaperPicker/res/values-sw/strings.xml deleted file mode 100644 index 729a79a74..000000000 --- a/WallpaperPicker/res/values-sw/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Weka mandhari"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Haikuweza kupakia picha"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Haikuweza kupakia picha iwe mandhari"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Haikuweza kuweka picha kuwa mandhari"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d zimechaguliwa"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d zimechaguliwa"</item> - <item quantity="other" msgid="479468347731745357">"%1$d zimechaguliwa"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Mandhari %1$d ya %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> iliyochaguliwa"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Futa"</string> - <string name="pick_image" msgid="3189640419551368385">"Picha zangu"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Mandhari"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Punguza mandhari"</string> -</resources> diff --git a/WallpaperPicker/res/values-ta-rIN/strings.xml b/WallpaperPicker/res/values-ta-rIN/strings.xml deleted file mode 100644 index 69a9d386b..000000000 --- a/WallpaperPicker/res/values-ta-rIN/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d தேர்ந்தெடுக்கப்பட்டன"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"வால்பேப்பர் %1$d / %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> தேர்ந்தெடுக்கப்பட்டது"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"நீக்கு"</string> - <string name="pick_image" msgid="3189640419551368385">"எனது படங்கள்"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"வால்பேப்பர்கள்"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"வால்பேப்பரைச் செதுக்கு"</string> -</resources> diff --git a/WallpaperPicker/res/values-te-rIN/strings.xml b/WallpaperPicker/res/values-te-rIN/strings.xml deleted file mode 100644 index 6fb5fa29b..000000000 --- a/WallpaperPicker/res/values-te-rIN/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d ఎంచుకోబడింది"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%2$dలో %1$dవ వాల్పేపర్"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> ఎంచుకోబడింది"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"తొలగించు"</string> - <string name="pick_image" msgid="3189640419551368385">"నా ఫోటోలు"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"వాల్పేపర్లు"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"వాల్పేపర్ను కత్తిరించండి"</string> -</resources> diff --git a/WallpaperPicker/res/values-th/strings.xml b/WallpaperPicker/res/values-th/strings.xml deleted file mode 100644 index c68943606..000000000 --- a/WallpaperPicker/res/values-th/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"เลือกไว้ %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"วอลเปเปอร์ %1$d จาก %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"เลือก <xliff:g id="LABEL">%1$s</xliff:g> แล้ว"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"ลบ"</string> - <string name="pick_image" msgid="3189640419551368385">"รูปภาพของฉัน"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"วอลเปเปอร์"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"ครอบตัดวอลเปเปอร์"</string> -</resources> diff --git a/WallpaperPicker/res/values-tl/strings.xml b/WallpaperPicker/res/values-tl/strings.xml deleted file mode 100644 index c760d7f6e..000000000 --- a/WallpaperPicker/res/values-tl/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Itakda ang wallpaper"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Hindi ma-load ang larawan"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Hindi ma-load ang larawan bilang wallpaper"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Hindi maitakda ang larawan bilang wallpaper"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d ang napili"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d ang napili"</item> - <item quantity="other" msgid="479468347731745357">"%1$d ang napili"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Wallpaper %1$d ng %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Napili ang <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Tanggalin"</string> - <string name="pick_image" msgid="3189640419551368385">"Aking mga larawan"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Mga Wallpaper"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"I-crop ang wallpaper"</string> -</resources> diff --git a/WallpaperPicker/res/values-tr/strings.xml b/WallpaperPicker/res/values-tr/strings.xml deleted file mode 100644 index e9dc1d2b2..000000000 --- a/WallpaperPicker/res/values-tr/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Duvar kağıdını ayarla"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Resim yüklenemedi"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Resim duvar kağıdı olarak yüklenemedi"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Resim, duvar kağıdı olarak ayarlanamadı"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d tane seçildi"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d tane seçildi"</item> - <item quantity="other" msgid="479468347731745357">"%1$d tane seçildi"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%2$d duvar kağıdı arasından duvar kağıdı %1$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> seçildi"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Sil"</string> - <string name="pick_image" msgid="3189640419551368385">"Fotoğraflarım"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Duvar kağıtları"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Duvar kağıdını kırp"</string> -</resources> diff --git a/WallpaperPicker/res/values-uk/strings.xml b/WallpaperPicker/res/values-uk/strings.xml deleted file mode 100644 index c6669c0c8..000000000 --- a/WallpaperPicker/res/values-uk/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"Вибрано %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Фоновий малюнок %1$d з %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"Вибрано <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Видалити"</string> - <string name="pick_image" msgid="3189640419551368385">"Мої фото"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Фонові малюнки"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Обрізати фоновий малюнок"</string> -</resources> diff --git a/WallpaperPicker/res/values-ur-rPK/strings.xml b/WallpaperPicker/res/values-ur-rPK/strings.xml deleted file mode 100644 index e240e1a50..000000000 --- a/WallpaperPicker/res/values-ur-rPK/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"%1$d کو منتخب کیا گیا"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"وال پیپر %1$d از %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> کو منتخب کیا گیا"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"حذف کریں"</string> - <string name="pick_image" msgid="3189640419551368385">"میری تصاویر"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"وال پیپرز"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"وال پیپر کو تراشیں"</string> -</resources> diff --git a/WallpaperPicker/res/values-uz-rUZ/strings.xml b/WallpaperPicker/res/values-uz-rUZ/strings.xml deleted file mode 100644 index 5a79981e9..000000000 --- a/WallpaperPicker/res/values-uz-rUZ/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Fonga rasm o‘rnatish"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Rasm yuklanmadi"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Fon rasmi sifatida rasm yuklanmadi"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Rasmni fon rasmi sifatida o‘rnatib bo‘lmadi"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d ta tanlandi"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d ta tanlandi"</item> - <item quantity="other" msgid="479468347731745357">"%1$d ta tanlandi"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Fon rasmi %2$ddan %1$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> tanlandi"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"O‘chirish"</string> - <string name="pick_image" msgid="3189640419551368385">"Mening rasmlarim"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Fon rasmlari"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Fon rasmini kesish"</string> -</resources> diff --git a/WallpaperPicker/res/values-v19/styles.xml b/WallpaperPicker/res/values-v19/styles.xml deleted file mode 100644 index 15fb0ea2a..000000000 --- a/WallpaperPicker/res/values-v19/styles.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ ---> - -<resources> - <style name="Theme.WallpaperCropper" parent="@android:style/Theme.DeviceDefault"> - <item name="android:actionBarStyle">@style/WallpaperCropperActionBar</item> - <item name="android:windowFullscreen">true</item> - <item name="android:windowActionBarOverlay">true</item> - <item name="android:windowTranslucentNavigation">true</item> - </style> - - <style name="Theme" parent="@style/BaseWallpaperTheme"> - <item name="android:windowTranslucentStatus">true</item> - <item name="android:windowTranslucentNavigation">true</item> - </style> -</resources> diff --git a/WallpaperPicker/res/values-v21/styles.xml b/WallpaperPicker/res/values-v21/styles.xml deleted file mode 100644 index de4b2f2af..000000000 --- a/WallpaperPicker/res/values-v21/styles.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?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. -*/ ---> - -<resources xmlns:android="http://schemas.android.com/apk/res/android"> - - <style name="WallpaperCropperActionBar" parent="@android:style/Widget.DeviceDefault.ActionBar"> - <item name="android:displayOptions">showCustom</item> - <item name="android:background">#88000000</item> - <item name="android:contentInsetEnd">0dp</item> - <item name="android:contentInsetLeft">0dp</item> - <item name="android:contentInsetRight">0dp</item> - <item name="android:contentInsetStart">0dp</item> - </style> - - <style name="ActionBarSetWallpaperStyle" parent="@android:style/Widget.DeviceDefault.ActionButton"> - <item name="android:textColor">#ffffffff</item> - <item name="android:background">?android:attr/selectableItemBackgroundBorderless</item> - </style> - - <style name="Theme" parent="@style/BaseWallpaperTheme"> - <item name="android:statusBarColor">#00000000</item> - <item name="android:navigationBarColor">#00000000</item> - <item name="android:colorControlActivated">@color/launcher_accent_color</item> - <item name="android:colorAccent">@color/launcher_accent_color</item> - <item name="android:colorPrimary">@color/launcher_accent_color</item> - </style> -</resources>
\ No newline at end of file diff --git a/WallpaperPicker/res/values-vi/strings.xml b/WallpaperPicker/res/values-vi/strings.xml deleted file mode 100644 index a7c636de3..000000000 --- a/WallpaperPicker/res/values-vi/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Đặt hình nền"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Không thể tải hình ảnh"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Không thể tải hình ảnh làm hình nền"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Không thể đặt hình ảnh làm hình nền"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"Đã chọn %1$d"</item> - <item quantity="one" msgid="8409622005831789373">"Đã chọn %1$d"</item> - <item quantity="other" msgid="479468347731745357">"Đã chọn %1$d"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Hình nền %1$d / %2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> được chọn"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Xóa"</string> - <string name="pick_image" msgid="3189640419551368385">"Ảnh của tôi"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Hình nền"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Cắt hình nền"</string> -</resources> diff --git a/WallpaperPicker/res/values-zh-rCN/strings.xml b/WallpaperPicker/res/values-zh-rCN/strings.xml deleted file mode 100644 index 4656ec687..000000000 --- a/WallpaperPicker/res/values-zh-rCN/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"已选择%1$d项"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"第%1$d张壁纸,共%2$d张"</string> - <string name="announce_selection" msgid="123723511662250539">"已选择<xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"删除"</string> - <string name="pick_image" msgid="3189640419551368385">"我的照片"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"壁纸"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"剪裁壁纸"</string> -</resources> diff --git a/WallpaperPicker/res/values-zh-rHK/strings.xml b/WallpaperPicker/res/values-zh-rHK/strings.xml deleted file mode 100644 index eb9c32734..000000000 --- a/WallpaperPicker/res/values-zh-rHK/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"已選取 %1$d 張"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"第 %1$d 張桌布,共 %2$d 張"</string> - <string name="announce_selection" msgid="123723511662250539">"已選取<xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"刪除"</string> - <string name="pick_image" msgid="3189640419551368385">"我的相片"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"桌布"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"裁剪桌布"</string> -</resources> diff --git a/WallpaperPicker/res/values-zh-rTW/strings.xml b/WallpaperPicker/res/values-zh-rTW/strings.xml deleted file mode 100644 index fda123c79..000000000 --- a/WallpaperPicker/res/values-zh-rTW/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <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> - <item quantity="other" msgid="479468347731745357">"已選取 %1$d 個"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"第 %1$d 張桌布,共 %2$d 張"</string> - <string name="announce_selection" msgid="123723511662250539">"已選取<xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"刪除"</string> - <string name="pick_image" msgid="3189640419551368385">"我的相片"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"桌布"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"裁剪桌布"</string> -</resources> diff --git a/WallpaperPicker/res/values-zu/strings.xml b/WallpaperPicker/res/values-zu/strings.xml deleted file mode 100644 index 1a5b95e93..000000000 --- a/WallpaperPicker/res/values-zu/strings.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="wallpaper_instructions" msgid="3524143401182707094">"Setha isithombe sangemuva"</string> - <string name="image_load_fail" msgid="7538534580694411837">"Ayikwazanga ukulayisha isithombe"</string> - <string name="wallpaper_load_fail" msgid="4800700444605404650">"Ayikwazanga ukulayisha isithombe njengesithombe sangemuva"</string> - <string name="wallpaper_set_fail" msgid="7023180794008631780">"Ayikwazanga ukusetha isithombe njengesithombe sangemuva"</string> - <plurals name="number_of_items_selected"> - <item quantity="zero" msgid="9015111147509924344">"%1$d khethiwe"</item> - <item quantity="one" msgid="8409622005831789373">"%1$d khethiwe"</item> - <item quantity="other" msgid="479468347731745357">"%1$d khethiwe"</item> - </plurals> - <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Isithombe sangemuva esingu-%1$d kwezingu-%2$d"</string> - <string name="announce_selection" msgid="123723511662250539">"I-<xliff:g id="LABEL">%1$s</xliff:g> ekhethiwe"</string> - <string name="wallpaper_delete" msgid="1459353972739215344">"Susa"</string> - <string name="pick_image" msgid="3189640419551368385">"Izithombe zami"</string> - <string name="pick_wallpaper" msgid="4628969645948454559">"Izithombe zangemuva"</string> - <string name="crop_wallpaper" msgid="4882870800623585836">"Nqampuna isithombe sangemuva"</string> -</resources> diff --git a/WallpaperPicker/res/values/colors.xml b/WallpaperPicker/res/values/colors.xml deleted file mode 100644 index 6ba32f06d..000000000 --- a/WallpaperPicker/res/values/colors.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/assets/res/any/colors.xml -** -** Copyright 2013, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<resources> - <color name="wallpaper_picker_translucent_gray">#66000000</color> - - <color name="launcher_accent_color">#ff009688</color> -</resources> diff --git a/WallpaperPicker/res/values/config.xml b/WallpaperPicker/res/values/config.xml deleted file mode 100644 index 2f5174ce6..000000000 --- a/WallpaperPicker/res/values/config.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2013 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<resources> - <!-- Specifies whether to expand the cropped area on both sides (rather - than just to one side) --> - <bool name="center_crop">false</bool> -</resources> diff --git a/WallpaperPicker/res/values/dimens.xml b/WallpaperPicker/res/values/dimens.xml deleted file mode 100644 index 0447c6d1f..000000000 --- a/WallpaperPicker/res/values/dimens.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2013 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<resources> -<!-- Wallpaper picker --> - <dimen name="wallpaperThumbnailWidth">106.5dp</dimen> - <dimen name="wallpaperThumbnailHeight">94.5dp</dimen> - <dimen name="wallpaperItemIconSize">32dp</dimen> -</resources> diff --git a/WallpaperPicker/res/values/strings.xml b/WallpaperPicker/res/values/strings.xml deleted file mode 100644 index 2bfd4767f..000000000 --- a/WallpaperPicker/res/values/strings.xml +++ /dev/null @@ -1,56 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ ---> - -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- Button label on Wallpaper picker screen; user selects this button to set a specific wallpaper --> - <string name="wallpaper_instructions">Set wallpaper</string> - <!-- Error message when an image is selected as a wallpaper, - but the wallpaper picker cannot load it --> - <string name="image_load_fail">Coudn\'t load image</string> - <!-- Error message when an image is selected as a wallpaper, - but the wallpaper cropper cannot load it. The user will - usually see this when using another app and trying to set - an image as the wallpaper --> - <string name="wallpaper_load_fail">Couldn\'t load image as wallpaper</string> - <!-- Error message when an image is selected as a wallpaper, - but something goes wrong when the user clicks "Set wallpaper" --> - <string name="wallpaper_set_fail">Couldn\'t set image as wallpaper</string> - <!-- Shown when wallpapers are selected in Wallpaper picker --> - <!-- String indicating how many media item(s) is(are) selected - eg. 1 selected [CHAR LIMIT=30] --> - <plurals name="number_of_items_selected"> - <item quantity="zero">%1$d selected</item> - <item quantity="one">%1$d selected</item> - <item quantity="other">%1$d selected</item> - </plurals> - <!-- Accessibility string used as a label for a particular wallpaper in the Wallpaper Picker list. - e.g. "Wallpaper 3 of 10" --> - <string name="wallpaper_accessibility_name">Wallpaper %1$d of %2$d</string> - <!-- Accessibility string used to announce that a wallpaper has been selected. --> - <string name="announce_selection">Selected <xliff:g id="label" example="Wallpaper 3 of 10">%1$s</xliff:g></string> - - <!-- Label on button to delete wallpaper(s) --> - <string name="wallpaper_delete">Delete</string> - <!-- Label on button in Wallpaper Picker to pick an image --> - <string name="pick_image">My photos</string> - <!-- Option in "Select wallpaper from" dialog box --> - <string name="pick_wallpaper">Wallpapers</string> - <!-- Title of activity for cropping wallpapers --> - <string name="crop_wallpaper">Crop wallpaper</string> -</resources> diff --git a/WallpaperPicker/res/values/styles.xml b/WallpaperPicker/res/values/styles.xml deleted file mode 100644 index d1c945a32..000000000 --- a/WallpaperPicker/res/values/styles.xml +++ /dev/null @@ -1,51 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ ---> - -<resources xmlns:android="http://schemas.android.com/apk/res/android"> - <style name="Theme.WallpaperCropper" parent="@android:style/Theme.DeviceDefault"> - <item name="android:actionBarStyle">@style/WallpaperCropperActionBar</item> - <item name="android:windowFullscreen">true</item> - <item name="android:windowActionBarOverlay">true</item> - </style> - - <style name="Theme.WallpaperPicker" parent="Theme.WallpaperCropper"> - <item name="android:windowBackground">@android:color/transparent</item> - <item name="android:colorBackgroundCacheHint">@null</item> - <item name="android:windowShowWallpaper">true</item> - </style> - - <style name="WallpaperCropperActionBar" parent="@android:style/Widget.DeviceDefault.ActionBar"> - <item name="android:displayOptions">showCustom</item> - <item name="android:background">#88000000</item> - </style> - - <style name="BaseWallpaperTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar"> - <item name="android:windowBackground">@android:color/transparent</item> - <item name="android:colorBackgroundCacheHint">@null</item> - <item name="android:windowShowWallpaper">true</item> - <item name="android:windowNoTitle">true</item> - </style> - - <style name="Theme" parent="@style/BaseWallpaperTheme"></style> - - <style name="ActionBarSetWallpaperStyle" parent="@android:style/Widget.DeviceDefault.ActionButton"> - <item name="android:textColor">#ffffffff</item> - <item name="android:background">?android:attr/selectableItemBackground</item> - </style> -</resources> diff --git a/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java b/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java deleted file mode 100644 index 8b5144748..000000000 --- a/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java +++ /dev/null @@ -1,409 +0,0 @@ -/** - * 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.app.WallpaperManager; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Bitmap.CompressFormat; -import android.graphics.BitmapFactory; -import android.graphics.BitmapRegionDecoder; -import android.graphics.Canvas; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.RectF; -import android.net.Uri; -import android.os.AsyncTask; -import android.util.Log; -import android.widget.Toast; - -import com.android.launcher3.R; - -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; - -public class BitmapCropTask extends AsyncTask<Void, Void, Boolean> { - - public interface OnBitmapCroppedHandler { - public void onBitmapCropped(byte[] imageBytes); - } - - public interface OnEndCropHandler { - public void run(boolean cropSucceeded); - } - - private static final int DEFAULT_COMPRESS_QUALITY = 90; - private static final String LOGTAG = "BitmapCropTask"; - - Uri mInUri = null; - Context mContext; - String mInFilePath; - byte[] mInImageBytes; - int mInResId = 0; - RectF mCropBounds = null; - int mOutWidth, mOutHeight; - int mRotation; - boolean mSetWallpaper; - boolean mSaveCroppedBitmap; - Bitmap mCroppedBitmap; - BitmapCropTask.OnEndCropHandler mOnEndCropHandler; - Resources mResources; - BitmapCropTask.OnBitmapCroppedHandler mOnBitmapCroppedHandler; - boolean mNoCrop; - - public BitmapCropTask(Context c, String filePath, - RectF cropBounds, int rotation, int outWidth, int outHeight, - boolean setWallpaper, boolean saveCroppedBitmap, OnEndCropHandler onEndCropHandler) { - mContext = c; - mInFilePath = filePath; - init(cropBounds, rotation, - outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndCropHandler); - } - - public BitmapCropTask(byte[] imageBytes, - RectF cropBounds, int rotation, int outWidth, int outHeight, - boolean setWallpaper, boolean saveCroppedBitmap, OnEndCropHandler onEndCropHandler) { - mInImageBytes = imageBytes; - init(cropBounds, rotation, - outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndCropHandler); - } - - public BitmapCropTask(Context c, Uri inUri, - RectF cropBounds, int rotation, int outWidth, int outHeight, - boolean setWallpaper, boolean saveCroppedBitmap, OnEndCropHandler onEndCropHandler) { - mContext = c; - mInUri = inUri; - init(cropBounds, rotation, - outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndCropHandler); - } - - public BitmapCropTask(Context c, Resources res, int inResId, - RectF cropBounds, int rotation, int outWidth, int outHeight, - boolean setWallpaper, boolean saveCroppedBitmap, OnEndCropHandler onEndCropHandler) { - mContext = c; - mInResId = inResId; - mResources = res; - init(cropBounds, rotation, - outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndCropHandler); - } - - private void init(RectF cropBounds, int rotation, int outWidth, int outHeight, - boolean setWallpaper, boolean saveCroppedBitmap, OnEndCropHandler onEndCropHandler) { - mCropBounds = cropBounds; - mRotation = rotation; - mOutWidth = outWidth; - mOutHeight = outHeight; - mSetWallpaper = setWallpaper; - mSaveCroppedBitmap = saveCroppedBitmap; - mOnEndCropHandler = onEndCropHandler; - } - - public void setOnBitmapCropped(BitmapCropTask.OnBitmapCroppedHandler handler) { - mOnBitmapCroppedHandler = handler; - } - - public void setNoCrop(boolean value) { - mNoCrop = value; - } - - public void setOnEndRunnable(OnEndCropHandler onEndCropHandler) { - mOnEndCropHandler = onEndCropHandler; - } - - // Helper to setup input stream - private InputStream regenerateInputStream() { - if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) { - Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " + - "image byte array given"); - } else { - try { - if (mInUri != null) { - return new BufferedInputStream( - mContext.getContentResolver().openInputStream(mInUri)); - } else if (mInFilePath != null) { - return mContext.openFileInput(mInFilePath); - } else if (mInImageBytes != null) { - return new BufferedInputStream(new ByteArrayInputStream(mInImageBytes)); - } else { - return new BufferedInputStream(mResources.openRawResource(mInResId)); - } - } catch (FileNotFoundException e) { - Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e); - } - } - return null; - } - - public Point getImageBounds() { - InputStream is = regenerateInputStream(); - if (is != null) { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeStream(is, null, options); - Utils.closeSilently(is); - if (options.outWidth != 0 && options.outHeight != 0) { - return new Point(options.outWidth, options.outHeight); - } - } - return null; - } - - public void setCropBounds(RectF cropBounds) { - mCropBounds = cropBounds; - } - - public Bitmap getCroppedBitmap() { - return mCroppedBitmap; - } - public boolean cropBitmap() { - boolean failure = false; - - - WallpaperManager wallpaperManager = null; - if (mSetWallpaper) { - wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext()); - } - - - if (mSetWallpaper && mNoCrop) { - try { - InputStream is = regenerateInputStream(); - if (is != null) { - wallpaperManager.setStream(is); - Utils.closeSilently(is); - } - } catch (IOException e) { - Log.w(LOGTAG, "cannot write stream to wallpaper", e); - failure = true; - } - return !failure; - } else { - // Find crop bounds (scaled to original image size) - Rect roundedTrueCrop = new Rect(); - Matrix rotateMatrix = new Matrix(); - Matrix inverseRotateMatrix = new Matrix(); - - Point bounds = getImageBounds(); - if (mRotation > 0) { - rotateMatrix.setRotate(mRotation); - inverseRotateMatrix.setRotate(-mRotation); - - mCropBounds.roundOut(roundedTrueCrop); - mCropBounds = new RectF(roundedTrueCrop); - - if (bounds == null) { - Log.w(LOGTAG, "cannot get bounds for image"); - failure = true; - return false; - } - - float[] rotatedBounds = new float[] { bounds.x, bounds.y }; - rotateMatrix.mapPoints(rotatedBounds); - rotatedBounds[0] = Math.abs(rotatedBounds[0]); - rotatedBounds[1] = Math.abs(rotatedBounds[1]); - - mCropBounds.offset(-rotatedBounds[0]/2, -rotatedBounds[1]/2); - inverseRotateMatrix.mapRect(mCropBounds); - mCropBounds.offset(bounds.x/2, bounds.y/2); - - } - - mCropBounds.roundOut(roundedTrueCrop); - - if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) { - Log.w(LOGTAG, "crop has bad values for full size image"); - failure = true; - return false; - } - - // See how much we're reducing the size of the image - int scaleDownSampleSize = Math.max(1, Math.min(roundedTrueCrop.width() / mOutWidth, - roundedTrueCrop.height() / mOutHeight)); - // Attempt to open a region decoder - BitmapRegionDecoder decoder = null; - InputStream is = null; - try { - is = regenerateInputStream(); - if (is == null) { - Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString()); - failure = true; - return false; - } - decoder = BitmapRegionDecoder.newInstance(is, false); - Utils.closeSilently(is); - } catch (IOException e) { - Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e); - } finally { - Utils.closeSilently(is); - is = null; - } - - Bitmap crop = null; - if (decoder != null) { - // Do region decoding to get crop bitmap - BitmapFactory.Options options = new BitmapFactory.Options(); - if (scaleDownSampleSize > 1) { - options.inSampleSize = scaleDownSampleSize; - } - crop = decoder.decodeRegion(roundedTrueCrop, options); - decoder.recycle(); - } - - if (crop == null) { - // BitmapRegionDecoder has failed, try to crop in-memory - is = regenerateInputStream(); - Bitmap fullSize = null; - if (is != null) { - BitmapFactory.Options options = new BitmapFactory.Options(); - if (scaleDownSampleSize > 1) { - options.inSampleSize = scaleDownSampleSize; - } - fullSize = BitmapFactory.decodeStream(is, null, options); - Utils.closeSilently(is); - } - if (fullSize != null) { - // Find out the true sample size that was used by the decoder - scaleDownSampleSize = bounds.x / fullSize.getWidth(); - mCropBounds.left /= scaleDownSampleSize; - mCropBounds.top /= scaleDownSampleSize; - mCropBounds.bottom /= scaleDownSampleSize; - mCropBounds.right /= scaleDownSampleSize; - mCropBounds.roundOut(roundedTrueCrop); - - // Adjust values to account for issues related to rounding - if (roundedTrueCrop.width() > fullSize.getWidth()) { - // Adjust the width - roundedTrueCrop.right = roundedTrueCrop.left + fullSize.getWidth(); - } - if (roundedTrueCrop.right > fullSize.getWidth()) { - // Adjust the left and right values. - roundedTrueCrop.offset(-(roundedTrueCrop.right - fullSize.getWidth()), 0); - } - if (roundedTrueCrop.height() > fullSize.getHeight()) { - // Adjust the height - roundedTrueCrop.bottom = roundedTrueCrop.top + fullSize.getHeight(); - } - if (roundedTrueCrop.bottom > fullSize.getHeight()) { - // Adjust the top and bottom values. - roundedTrueCrop.offset(0, -(roundedTrueCrop.bottom - fullSize.getHeight())); - } - - crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, - roundedTrueCrop.top, roundedTrueCrop.width(), - roundedTrueCrop.height()); - } - } - - if (crop == null) { - Log.w(LOGTAG, "cannot decode file: " + mInUri.toString()); - failure = true; - return false; - } - if (mOutWidth > 0 && mOutHeight > 0 || mRotation > 0) { - float[] dimsAfter = new float[] { crop.getWidth(), crop.getHeight() }; - rotateMatrix.mapPoints(dimsAfter); - dimsAfter[0] = Math.abs(dimsAfter[0]); - dimsAfter[1] = Math.abs(dimsAfter[1]); - - if (!(mOutWidth > 0 && mOutHeight > 0)) { - mOutWidth = Math.round(dimsAfter[0]); - mOutHeight = Math.round(dimsAfter[1]); - } - - RectF cropRect = new RectF(0, 0, dimsAfter[0], dimsAfter[1]); - RectF returnRect = new RectF(0, 0, mOutWidth, mOutHeight); - - Matrix m = new Matrix(); - if (mRotation == 0) { - m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); - } else { - Matrix m1 = new Matrix(); - m1.setTranslate(-crop.getWidth() / 2f, -crop.getHeight() / 2f); - Matrix m2 = new Matrix(); - m2.setRotate(mRotation); - Matrix m3 = new Matrix(); - m3.setTranslate(dimsAfter[0] / 2f, dimsAfter[1] / 2f); - Matrix m4 = new Matrix(); - m4.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); - - Matrix c1 = new Matrix(); - c1.setConcat(m2, m1); - Matrix c2 = new Matrix(); - c2.setConcat(m4, m3); - m.setConcat(c2, c1); - } - - Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(), - (int) returnRect.height(), Bitmap.Config.ARGB_8888); - if (tmp != null) { - Canvas c = new Canvas(tmp); - Paint p = new Paint(); - p.setFilterBitmap(true); - c.drawBitmap(crop, m, p); - crop = tmp; - } - } - - if (mSaveCroppedBitmap) { - mCroppedBitmap = crop; - } - - // Compress to byte array - ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048); - if (crop.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, tmpOut)) { - // If we need to set to the wallpaper, set it - if (mSetWallpaper && wallpaperManager != null) { - try { - byte[] outByteArray = tmpOut.toByteArray(); - wallpaperManager.setStream(new ByteArrayInputStream(outByteArray)); - if (mOnBitmapCroppedHandler != null) { - mOnBitmapCroppedHandler.onBitmapCropped(outByteArray); - } - } catch (IOException e) { - Log.w(LOGTAG, "cannot write stream to wallpaper", e); - failure = true; - } - } - } else { - Log.w(LOGTAG, "cannot compress bitmap"); - failure = true; - } - } - return !failure; // True if any of the operations failed - } - - @Override - protected Boolean doInBackground(Void... params) { - return cropBitmap(); - } - - @Override - protected void onPostExecute(Boolean cropSucceeded) { - if (!cropSucceeded) { - Toast.makeText(mContext, R.string.wallpaper_set_fail, Toast.LENGTH_SHORT).show(); - } - if (mOnEndCropHandler != null) { - mOnEndCropHandler.run(cropSucceeded); - } - } -}
\ No newline at end of file diff --git a/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java b/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java deleted file mode 100644 index 9ac5c1bf7..000000000 --- a/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java +++ /dev/null @@ -1,81 +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.common; - -import android.content.Context; -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; - -public class BitmapUtils { - - private static final String TAG = "BitmapUtils"; - - // Find the min x that 1 / x >= scale - public static int computeSampleSizeLarger(float scale) { - int initialSize = (int) Math.floor(1f / scale); - if (initialSize <= 1) return 1; - - return initialSize <= 8 - ? Utils.prevPowerOf2(initialSize) - : initialSize / 8 * 8; - } - - public static int getRotationFromExif(Context context, Uri uri) { - return BitmapUtils.getRotationFromExifHelper(null, 0, context, uri); - } - - public static int getRotationFromExif(Resources res, int resId) { - return BitmapUtils.getRotationFromExifHelper(res, resId, null, null); - } - - private static int getRotationFromExifHelper(Resources res, int resId, Context context, Uri uri) { - ExifInterface ei = new ExifInterface(); - 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 - 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/Utils.java b/WallpaperPicker/src/com/android/gallery3d/common/Utils.java deleted file mode 100644 index 8466c22cb..000000000 --- a/WallpaperPicker/src/com/android/gallery3d/common/Utils.java +++ /dev/null @@ -1,129 +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.common; - -import android.database.Cursor; -import android.graphics.RectF; -import android.os.ParcelFileDescriptor; -import android.util.Log; - -import java.io.Closeable; -import java.io.IOException; - -public class Utils { - private static final String TAG = "Utils"; - - // Throws AssertionError if the input is false. - public static void assertTrue(boolean cond) { - if (!cond) { - throw new AssertionError(); - } - } - - // Returns the next power of two. - // Returns the input if it is already power of 2. - // Throws IllegalArgumentException if the input is <= 0 or - // the answer overflows. - public static int nextPowerOf2(int n) { - if (n <= 0 || n > (1 << 30)) throw new IllegalArgumentException("n is invalid: " + n); - n -= 1; - n |= n >> 16; - n |= n >> 8; - n |= n >> 4; - n |= n >> 2; - n |= n >> 1; - return n + 1; - } - - // Returns the previous power of two. - // Returns the input if it is already power of 2. - // Throws IllegalArgumentException if the input is <= 0 - public static int prevPowerOf2(int n) { - if (n <= 0) throw new IllegalArgumentException(); - return Integer.highestOneBit(n); - } - - // Returns the input value x clamped to the range [min, max]. - public static int clamp(int x, int min, int max) { - if (x > max) return max; - if (x < min) return min; - return x; - } - - public static int ceilLog2(float value) { - int i; - for (i = 0; i < 31; i++) { - if ((1 << i) >= value) break; - } - return i; - } - - public static int floorLog2(float value) { - int i; - for (i = 0; i < 31; i++) { - if ((1 << i) > value) break; - } - return i - 1; - } - - public static void closeSilently(Closeable c) { - if (c == null) return; - try { - c.close(); - } catch (IOException t) { - Log.w(TAG, "close fail ", t); - } - } - - public static void closeSilently(ParcelFileDescriptor fd) { - try { - if (fd != null) fd.close(); - } catch (Throwable t) { - Log.w(TAG, "fail to close", t); - } - } - - public static void closeSilently(Cursor cursor) { - try { - if (cursor != null) cursor.close(); - } catch (Throwable t) { - Log.w(TAG, "fail to close", t); - } - } - - public static RectF getMaxCropRect( - int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) { - RectF cropRect = new RectF(); - // Get a crop rect that will fit this - if (inWidth / (float) inHeight > outWidth / (float) outHeight) { - cropRect.top = 0; - cropRect.bottom = inHeight; - cropRect.left = (inWidth - (outWidth / (float) outHeight) * inHeight) / 2; - cropRect.right = inWidth - cropRect.left; - if (leftAligned) { - cropRect.right -= cropRect.left; - cropRect.left = 0; - } - } else { - cropRect.left = 0; - cropRect.right = inWidth; - cropRect.top = (inHeight - (outHeight / (float) outWidth) * inWidth) / 2; - cropRect.bottom = inHeight - cropRect.top; - } - return cropRect; - } -} 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 deleted file mode 100644 index 0f3efb727..000000000 --- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/BasicTexture.java +++ /dev/null @@ -1,211 +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 com.android.gallery3d.common.Utils; - -import java.util.WeakHashMap; - -// BasicTexture is a Texture corresponds to a real GL texture. -// The state of a BasicTexture indicates whether its data is loaded to GL memory. -// If a BasicTexture is loaded into GL memory, it has a GL texture id. -public abstract class BasicTexture implements Texture { - - private static final String TAG = "BasicTexture"; - protected static final int UNSPECIFIED = -1; - - protected static final int STATE_UNLOADED = 0; - protected static final int STATE_LOADED = 1; - protected static final int STATE_ERROR = -1; - - // Log a warning if a texture is larger along a dimension - private static final int MAX_TEXTURE_SIZE = 4096; - - protected int mId = -1; - protected int mState; - - protected int mWidth = UNSPECIFIED; - protected int mHeight = UNSPECIFIED; - - protected int mTextureWidth; - protected int mTextureHeight; - - private boolean mHasBorder; - - protected GLCanvas mCanvasRef = null; - private static WeakHashMap<BasicTexture, Object> sAllTextures - = new WeakHashMap<BasicTexture, Object>(); - private static ThreadLocal sInFinalizer = new ThreadLocal(); - - protected BasicTexture(GLCanvas canvas, int id, int state) { - setAssociatedCanvas(canvas); - mId = id; - mState = state; - synchronized (sAllTextures) { - sAllTextures.put(this, null); - } - } - - protected BasicTexture() { - this(null, 0, STATE_UNLOADED); - } - - protected void setAssociatedCanvas(GLCanvas canvas) { - mCanvasRef = canvas; - } - - /** - * Sets the content size of this texture. In OpenGL, the actual texture - * size must be of power of 2, the size of the content may be smaller. - */ - public void setSize(int width, int height) { - mWidth = width; - mHeight = height; - mTextureWidth = width > 0 ? Utils.nextPowerOf2(width) : 0; - mTextureHeight = height > 0 ? Utils.nextPowerOf2(height) : 0; - if (mTextureWidth > MAX_TEXTURE_SIZE || mTextureHeight > MAX_TEXTURE_SIZE) { - Log.w(TAG, String.format("texture is too large: %d x %d", - mTextureWidth, mTextureHeight), new Exception()); - } - } - - public boolean isFlippedVertically() { - return false; - } - - public int getId() { - return mId; - } - - @Override - public int getWidth() { - return mWidth; - } - - @Override - public int getHeight() { - return mHeight; - } - - // Returns the width rounded to the next power of 2. - public int getTextureWidth() { - return mTextureWidth; - } - - // Returns the height rounded to the next power of 2. - public int getTextureHeight() { - 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()); - } - - @Override - public void draw(GLCanvas canvas, int x, int y, int w, int h) { - canvas.drawTexture(this, x, y, w, h); - } - - // onBind is called before GLCanvas binds this 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; - } - - // recycle() is called when the texture will never be used again, - // so it can free all resources. - public void recycle() { - freeResource(); - } - - // yield() is called when the texture will not be used temporarily, - // so it can free some resources. - // The default implementation unloads the texture from GL memory, so - // the subclass should make sure it can reload the texture to GL memory - // later, or it will have to override this method. - public void yield() { - freeResource(); - } - - private void freeResource() { - GLCanvas canvas = mCanvasRef; - if (canvas != null && mId != -1) { - canvas.unloadTexture(this); - mId = -1; // Don't free it again. - } - mState = STATE_UNLOADED; - setAssociatedCanvas(null); - } - - @Override - protected void finalize() { - sInFinalizer.set(BasicTexture.class); - recycle(); - 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()) { - t.yield(); - } - } - } - - public static void invalidateAllTextures() { - synchronized (sAllTextures) { - for (BasicTexture t : sAllTextures.keySet()) { - t.mState = STATE_UNLOADED; - t.setAssociatedCanvas(null); - } - } - } -} diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/BitmapTexture.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/BitmapTexture.java deleted file mode 100644 index f8b01cb42..000000000 --- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/BitmapTexture.java +++ /dev/null @@ -1,54 +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.graphics.Bitmap; - -import com.android.gallery3d.common.Utils; - -// BitmapTexture is a texture whose content is specified by a fixed Bitmap. -// -// The texture does not own the Bitmap. The user should make sure the Bitmap -// is valid during the texture's lifetime. When the texture is recycled, it -// does not free the Bitmap. -public class BitmapTexture extends UploadedTexture { - protected Bitmap mContentBitmap; - - public BitmapTexture(Bitmap bitmap) { - this(bitmap, false); - } - - public BitmapTexture(Bitmap bitmap, boolean hasBorder) { - super(hasBorder); - Utils.assertTrue(bitmap != null && !bitmap.isRecycled()); - mContentBitmap = bitmap; - } - - @Override - protected void onFreeBitmap(Bitmap bitmap) { - // Do nothing. - } - - @Override - protected Bitmap onGetBitmap() { - return mContentBitmap; - } - - public Bitmap getBitmap() { - return mContentBitmap; - } -} diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLCanvas.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLCanvas.java deleted file mode 100644 index 5b0747704..000000000 --- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLCanvas.java +++ /dev/null @@ -1,215 +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.graphics.Bitmap; -import android.graphics.Rect; -import android.graphics.RectF; - -// -// GLCanvas gives a convenient interface to draw using OpenGL. -// -// When a rectangle is specified in this interface, it means the region -// [x, x+width) * [y, y+height) -// -public interface GLCanvas { - - public GLId getGLId(); - - // Tells GLCanvas the size of the underlying GL surface. This should be - // called before first drawing and when the size of GL surface is changed. - // This is called by GLRoot and should not be called by the clients - // who only want to draw on the GLCanvas. Both width and height must be - // nonnegative. - public abstract void setSize(int width, int height); - - // 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, - // alpha, and clip). This call balances a previous call to save(), and is - // used to remove all modifications to the configuration state since the - // 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); - - // 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 - * GL_LINEAR for GL_TEXTURE_MIN_FILTER and GL_TEXTURE_MAG_FILTER. - * bindTexture() must be called prior to this. - * - * @param texture The texture to set parameters on. - */ - public abstract void setTextureParameters(BasicTexture texture); - - /** - * Initializes the texture to a size by calling texImage2D on it. - * - * @param texture The texture to initialize the size. - * @param format The texture format (e.g. GL_RGBA) - * @param type The texture type (e.g. GL_UNSIGNED_BYTE) - */ - public abstract void initializeTextureSize(BasicTexture texture, int format, int type); - - /** - * Initializes the texture to a size by calling texImage2D on it. - * - * @param texture The texture to initialize the size. - * @param bitmap The bitmap to initialize the bitmap with. - */ - public abstract void initializeTexture(BasicTexture texture, Bitmap bitmap); - - /** - * Calls glTexSubImage2D to upload a bitmap to the texture. - * - * @param texture The target texture to write to. - * @param xOffset Specifies a texel offset in the x direction within the - * texture array. - * @param yOffset Specifies a texel offset in the y direction within the - * texture array. - * @param format The texture format (e.g. GL_RGBA) - * @param type The texture type (e.g. GL_UNSIGNED_BYTE) - */ - public abstract void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, - Bitmap bitmap, - int format, int type); - - /** - * Generates buffers and uploads the buffer data. - * - * @param buffer The buffer to upload - * @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 deleted file mode 100644 index 933260b48..000000000 --- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20Canvas.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.glrenderer; - -import android.graphics.Bitmap; -import android.graphics.Rect; -import android.graphics.RectF; -import android.opengl.GLES20; -import android.opengl.GLUtils; -import android.opengl.Matrix; -import android.util.Log; - -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; -import java.util.ArrayList; -import java.util.Arrays; - -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 float[] BOX_COORDINATES = { - 0, 0, // Fill rectangle - 1, 0, - 0, 1, - 1, 1, - 0, 0, // Draw line - 1, 1, - 0, 0, // Draw rectangle outline - 0, 1, - 1, 1, - 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" - + "uniform mat4 " + TEXTURE_MATRIX_UNIFORM + ";\n" - + "attribute vec2 " + POSITION_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_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" - + "uniform float " + ALPHA_UNIFORM + ";\n" - + "uniform sampler2D " + TEXTURE_SAMPLER_UNIFORM + ";\n" - + "void main() {\n" - + " gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n" - + " 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 - private int mWidth; - private int mHeight; - - // 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; - - // Handle indices -- common - 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; - - public ShaderParameter(String name) { - mName = name; - } - - public abstract void loadHandle(int program); - } - - private static class UniformShaderParameter extends ShaderParameter { - public UniformShaderParameter(String name) { - super(name); - } - - @Override - public void loadHandle(int program) { - handle = GLES20.glGetUniformLocation(program, mName); - checkError(); - } - } - - private static class AttributeShaderParameter extends ShaderParameter { - public AttributeShaderParameter(String name) { - super(name); - } - - @Override - public void loadHandle(int program) { - handle = GLES20.glGetAttribLocation(program, mName); - checkError(); - } - } - - ShaderParameter[] mDrawParameters = { - new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION - new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX - new UniformShaderParameter(COLOR_UNIFORM), // INDEX_COLOR - }; - 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]; - private final int[] mTempIntArray = new int[1]; - - private static final GLId mGLId = new GLES20IdImpl(); - - 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(); - } - - private static FloatBuffer createBuffer(float[] values) { - // First create an nio buffer, then create a VBO from it. - int size = values.length * FLOAT_SIZE; - FloatBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()) - .asFloatBuffer(); - buffer.put(values, 0, values.length).position(0); - return buffer; - } - - private int assembleProgram(int vertexShader, int fragmentShader, ShaderParameter[] params) { - int program = GLES20.glCreateProgram(); - checkError(); - if (program == 0) { - throw new RuntimeException("Cannot create GL program: " + GLES20.glGetError()); - } - GLES20.glAttachShader(program, vertexShader); - checkError(); - GLES20.glAttachShader(program, fragmentShader); - checkError(); - GLES20.glLinkProgram(program); - checkError(); - int[] mLinkStatus = mTempIntArray; - GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, mLinkStatus, 0); - if (mLinkStatus[0] != GLES20.GL_TRUE) { - Log.e(TAG, "Could not link program: "); - Log.e(TAG, GLES20.glGetProgramInfoLog(program)); - GLES20.glDeleteProgram(program); - program = 0; - } - for (int i = 0; i < params.length; i++) { - params[i].loadHandle(program); - } - return program; - } - - private static int loadShader(int type, String shaderCode) { - // create a vertex shader type (GLES20.GL_VERTEX_SHADER) - // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) - int shader = GLES20.glCreateShader(type); - - // add the source code to the shader and compile it - GLES20.glShaderSource(shader, shaderCode); - checkError(); - GLES20.glCompileShader(shader); - checkError(); - - return shader; - } - - @Override - public void setSize(int width, int height) { - mWidth = width; - mHeight = height; - GLES20.glViewport(0, 0, mWidth, mHeight); - 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); - } - } - - @Override - public void clearBuffer() { - GLES20.glClearColor(0f, 0f, 0f, 1f); - checkError(); - GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); - 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 - @Override - public void translate(float x, float y) { - int index = mCurrentMatrixIndex; - float[] m = mMatrices; - m[index + 12] += m[index + 0] * x + m[index + 4] * y; - m[index + 13] += m[index + 1] * x + m[index + 5] * y; - m[index + 14] += m[index + 2] * x + m[index + 6] * y; - m[index + 15] += m[index + 3] * x + m[index + 7] * y; - } - - @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; - } - float[] temp = mTempMatrix; - Matrix.setRotateM(temp, 0, angle, x, y, z); - float[] matrix = mMatrices; - int index = mCurrentMatrixIndex; - Matrix.multiplyMM(temp, MATRIX_SIZE, matrix, index, temp, 0); - System.arraycopy(temp, MATRIX_SIZE, matrix, index, MATRIX_SIZE); - } - - @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; - mCurrentMatrixIndex += MATRIX_SIZE; - if (mMatrices.length <= mCurrentMatrixIndex) { - mMatrices = Arrays.copyOf(mMatrices, mMatrices.length * 2); - } - System.arraycopy(mMatrices, currentIndex, mMatrices, mCurrentMatrixIndex, MATRIX_SIZE); - } - mSaveFlags.add(saveFlags); - } - - @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(); - GLES20.glVertexAttribPointer(params[INDEX_POSITION].handle, COORDS_PER_VERTEX, - GLES20.GL_FLOAT, false, VERTEX_STRIDE, offset * VERTEX_STRIDE); - checkError(); - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); - checkError(); - } - - private void draw(ShaderParameter[] params, int type, int count, float x, float y, float width, - float height) { - setMatrix(params, x, y, width, height); - int positionHandle = params[INDEX_POSITION].handle; - GLES20.glEnableVertexAttribArray(positionHandle); - checkError(); - GLES20.glDrawArrays(type, 0, count); - checkError(); - GLES20.glDisableVertexAttribArray(positionHandle); - checkError(); - } - - private void setMatrix(ShaderParameter[] params, float x, float y, float width, float height) { - Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f); - Matrix.scaleM(mTempMatrix, 0, width, height, 1f); - Matrix.multiplyMM(mTempMatrix, MATRIX_SIZE, mProjectionMatrix, 0, mTempMatrix, 0); - GLES20.glUniformMatrix4fv(params[INDEX_MATRIX].handle, 1, false, mTempMatrix, MATRIX_SIZE); - checkError(); - } - - @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; - } - copyTextureCoordinates(texture, mTempSourceRect); - mTempTargetRect.set(x, y, x + width, y + height); - convertCoordinate(mTempSourceRect, mTempTargetRect, texture); - drawTextureRect(texture, mTempSourceRect, mTempTargetRect); - } - - 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); - } - - @Override - public void drawTexture(BasicTexture texture, RectF source, RectF target) { - if (target.width() <= 0 || target.height() <= 0) { - return; - } - mTempSourceRect.set(source); - mTempTargetRect.set(target); - - convertCoordinate(mTempSourceRect, mTempTargetRect, texture); - 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); - } - - private void setTextureMatrix(RectF source) { - mTempTextureMatrix[0] = source.width(); - mTempTextureMatrix[5] = source.height(); - mTempTextureMatrix[12] = source.left; - mTempTextureMatrix[13] = source.top; - } - - // This function changes the source coordinate to the texture coordinates. - // It also clips the source and target coordinates if it is beyond the - // bound of the texture. - private static void convertCoordinate(RectF source, RectF target, BasicTexture texture) { - int width = texture.getWidth(); - int height = texture.getHeight(); - int texWidth = texture.getTextureWidth(); - int texHeight = texture.getTextureHeight(); - // Convert to texture coordinates - source.left /= texWidth; - source.right /= texWidth; - source.top /= texHeight; - source.bottom /= texHeight; - - // Clip if the rendering range is beyond the bound of the texture. - float xBound = (float) width / texWidth; - if (source.right > xBound) { - target.right = target.left + target.width() * (xBound - source.left) / source.width(); - source.right = xBound; - } - float yBound = (float) height / texHeight; - if (source.bottom > yBound) { - target.bottom = target.top + target.height() * (yBound - source.top) / source.height(); - source.bottom = yBound; - } - } - - private void drawTextureRect(BasicTexture texture, float[] textureMatrix, RectF target) { - ShaderParameter[] params = prepareTexture(texture); - 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; - } - prepareTexture(texture, program, params); - return params; - } - - private void prepareTexture(BasicTexture texture, int program, ShaderParameter[] params) { - deleteRecycledResources(); - GLES20.glUseProgram(program); - checkError(); - enableBlending(!texture.isOpaque() || getAlpha() < OPAQUE_ALPHA); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - checkError(); - texture.onBind(this); - GLES20.glBindTexture(texture.getTarget(), 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); - 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 - public boolean unloadTexture(BasicTexture texture) { - boolean unload = texture.isLoaded(); - if (unload) { - synchronized (mUnboundTextures) { - mUnboundTextures.add(texture.getId()); - } - } - return unload; - } - - @Override - public void deleteBuffer(int bufferId) { - synchronized (mUnboundTextures) { - mDeleteBuffers.add(bufferId); - } - } - - @Override - public void deleteRecycledResources() { - synchronized (mUnboundTextures) { - IntArray ids = mUnboundTextures; - if (mUnboundTextures.size() > 0) { - 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()); - 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); - } - - @Override - public void initializeTextureSize(BasicTexture texture, int format, int type) { - int target = texture.getTarget(); - GLES20.glBindTexture(target, texture.getId()); - checkError(); - int width = texture.getTextureWidth(); - int height = texture.getTextureHeight(); - GLES20.glTexImage2D(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()); - checkError(); - GLUtils.texImage2D(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()); - checkError(); - GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type); - } - - @Override - public int uploadBuffer(FloatBuffer buf) { - 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(); - int bufferId = mTempIntArray[0]; - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferId); - checkError(); - GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, buffer.capacity() * elementSize, buffer, - GLES20.GL_STATIC_DRAW); - checkError(); - return bufferId; - } - - public static void checkError() { - int error = GLES20.glGetError(); - if (error != 0) { - Throwable t = new Throwable(); - Log.e(TAG, "GL error: " + error, t); - } - } - - @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/GLES20IdImpl.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20IdImpl.java deleted file mode 100644 index 6cd7149cb..000000000 --- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20IdImpl.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.android.gallery3d.glrenderer; - -import android.opengl.GLES20; - -import javax.microedition.khronos.opengles.GL11; -import javax.microedition.khronos.opengles.GL11ExtensionPack; - -public class GLES20IdImpl implements GLId { - private final int[] mTempIntArray = new int[1]; - - @Override - public int generateTexture() { - GLES20.glGenTextures(1, mTempIntArray, 0); - GLES20Canvas.checkError(); - return mTempIntArray[0]; - } - - @Override - public void glGenBuffers(int n, int[] buffers, int offset) { - GLES20.glGenBuffers(n, buffers, offset); - GLES20Canvas.checkError(); - } - - @Override - public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) { - GLES20.glDeleteTextures(n, textures, offset); - GLES20Canvas.checkError(); - } - - - @Override - public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) { - GLES20.glDeleteBuffers(n, buffers, offset); - GLES20Canvas.checkError(); - } - - @Override - public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) { - GLES20.glDeleteFramebuffers(n, buffers, offset); - GLES20Canvas.checkError(); - } -} diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLId.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLId.java deleted file mode 100644 index 3cec558f6..000000000 --- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLId.java +++ /dev/null @@ -1,33 +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.glrenderer; - -import javax.microedition.khronos.opengles.GL11; -import javax.microedition.khronos.opengles.GL11ExtensionPack; - -// This mimics corresponding GL functions. -public interface GLId { - public int generateTexture(); - - public void glGenBuffers(int n, int[] buffers, int offset); - - public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset); - - public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset); - - public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset); -} 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/IntArray.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/IntArray.java deleted file mode 100644 index f123624d6..000000000 --- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/IntArray.java +++ /dev/null @@ -1,60 +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; - -public class IntArray { - private static final int INIT_CAPACITY = 8; - - private int mData[] = new int[INIT_CAPACITY]; - private int mSize = 0; - - public void add(int value) { - if (mData.length == mSize) { - int temp[] = new int[mSize + mSize]; - System.arraycopy(mData, 0, temp, 0, mSize); - mData = temp; - } - mData[mSize++] = value; - } - - public int removeLast() { - mSize--; - return mData[mSize]; - } - - public int size() { - return mSize; - } - - // For testing only - public int[] toArray(int[] result) { - if (result == null || result.length < mSize) { - result = new int[mSize]; - } - System.arraycopy(mData, 0, result, 0, mSize); - return result; - } - - public int[] getInternalArray() { - return mData; - } - - public void clear() { - mSize = 0; - if (mData.length != INIT_CAPACITY) mData = new int[INIT_CAPACITY]; - } -} 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 deleted file mode 100644 index 3dcae4aec..000000000 --- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/Texture.java +++ /dev/null @@ -1,44 +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; - - -// Texture is a rectangular image which can be drawn on GLCanvas. -// The isOpaque() function gives a hint about whether the texture is opaque, -// so the drawing can be done faster. -// -// 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 deleted file mode 100644 index 8075bf868..000000000 --- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/UploadedTexture.java +++ /dev/null @@ -1,299 +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.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.opengl.GLUtils; - -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 -// implement onFreeBitmap(mBitmap) which will be called when the Bitmap -// is not needed anymore. -// -// isContentValid() is meaningful only when the isLoaded() returns true. -// It means whether the content needs to be updated. -// -// The user of this class should call recycle() when the texture is not -// needed anymore. -// -// By default an UploadedTexture is opaque (so it can be drawn faster without -// blending). The user or subclass can override it using setOpaque(). -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; - - // 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; - - 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; - 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); - } - return bitmap; - } - - private Bitmap getBitmap() { - if (mBitmap == null) { - mBitmap = onGetBitmap(); - int w = mBitmap.getWidth() + mBorder * 2; - int h = mBitmap.getHeight() + mBorder * 2; - if (mWidth == UNSPECIFIED) { - setSize(w, h); - } - } - return mBitmap; - } - - private void freeBitmap() { - Utils.assertTrue(mBitmap != null); - onFreeBitmap(mBitmap); - mBitmap = null; - } - - @Override - public int getWidth() { - if (mWidth == UNSPECIFIED) getBitmap(); - return mWidth; - } - - @Override - public int getHeight() { - if (mWidth == UNSPECIFIED) getBitmap(); - return mHeight; - } - - protected abstract Bitmap onGetBitmap(); - - protected abstract void onFreeBitmap(Bitmap bitmap); - - protected void invalidateContent() { - if (mBitmap != null) freeBitmap(); - mContentValid = false; - mWidth = UNSPECIFIED; - mHeight = UNSPECIFIED; - } - - /** - * Whether the content on GPU is valid. - */ - public boolean isContentValid() { - return isLoaded() && mContentValid; - } - - /** - * Updates the content on GPU's memory. - * @param canvas - */ - 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); - 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(); - - Utils.assertTrue(bWidth <= texWidth && bHeight <= texHeight); - - // Upload the bitmap to a new texture. - mId = canvas.getGLId().generateTexture(); - canvas.setTextureParameters(this); - - if (bWidth == texWidth && bHeight == texHeight) { - canvas.initializeTexture(this, bitmap); - } else { - int format = GLUtils.getInternalFormat(bitmap); - int type = GLUtils.getType(bitmap); - 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); - } - - // Right border - if (mBorder + bWidth < texWidth) { - Bitmap line = getBorderLine(true, config, texHeight); - canvas.texSubImage2D(this, mBorder + bWidth, 0, line, format, type); - } - - // Bottom border - if (mBorder + bHeight < texHeight) { - Bitmap line = getBorderLine(false, config, texWidth); - canvas.texSubImage2D(this, 0, mBorder + bHeight, line, format, type); - } - } - } finally { - freeBitmap(); - } - // Update texture state. - setAssociatedCanvas(canvas); - mState = STATE_LOADED; - mContentValid = true; - } else { - mState = STATE_ERROR; - throw new RuntimeException("Texture load fail, no bitmap"); - } - } - - @Override - protected boolean onBind(GLCanvas canvas) { - updateContent(canvas); - return isContentValid(); - } - - @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/AlphaDisableableButton.java b/WallpaperPicker/src/com/android/launcher3/AlphaDisableableButton.java deleted file mode 100644 index f0796c36f..000000000 --- a/WallpaperPicker/src/com/android/launcher3/AlphaDisableableButton.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2014 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 android.widget.Button; - -/** - * A Button which becomes translucent when it is disabled - */ -public class AlphaDisableableButton extends Button { - public static float DISABLED_ALPHA_VALUE = 0.4f; - public AlphaDisableableButton(Context context) { - this(context, null); - } - - public AlphaDisableableButton(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public AlphaDisableableButton(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - setLayerType(LAYER_TYPE_HARDWARE, null); - } - - @Override - public void setEnabled(boolean enabled) { - super.setEnabled(enabled); - if(enabled) { - setAlpha(1.0f); - } else { - setAlpha(DISABLED_ALPHA_VALUE); - } - } -} diff --git a/WallpaperPicker/src/com/android/launcher3/CheckableFrameLayout.java b/WallpaperPicker/src/com/android/launcher3/CheckableFrameLayout.java deleted file mode 100644 index 5b7d82425..000000000 --- a/WallpaperPicker/src/com/android/launcher3/CheckableFrameLayout.java +++ /dev/null @@ -1,63 +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.Context; -import android.util.AttributeSet; -import android.widget.Checkable; -import android.widget.FrameLayout; - -public class CheckableFrameLayout extends FrameLayout implements Checkable { - private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked }; - boolean mChecked; - - public CheckableFrameLayout(Context context) { - super(context); - } - - public CheckableFrameLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public CheckableFrameLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - public boolean isChecked() { - return mChecked; - } - - public void setChecked(boolean checked) { - if (checked != mChecked) { - mChecked = checked; - refreshDrawableState(); - } - } - - public void toggle() { - setChecked(!mChecked); - } - - @Override - protected int[] onCreateDrawableState(int extraSpace) { - final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); - if (isChecked()) { - mergeDrawableStates(drawableState, CHECKED_STATE_SET); - } - return drawableState; - } -} diff --git a/WallpaperPicker/src/com/android/launcher3/CropView.java b/WallpaperPicker/src/com/android/launcher3/CropView.java deleted file mode 100644 index e98e23e98..000000000 --- a/WallpaperPicker/src/com/android/launcher3/CropView.java +++ /dev/null @@ -1,332 +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.Context; -import android.graphics.Matrix; -import android.graphics.Point; -import android.graphics.RectF; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.ScaleGestureDetector; -import android.view.ScaleGestureDetector.OnScaleGestureListener; -import android.view.ViewConfiguration; -import android.view.ViewTreeObserver; -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 { - - private ScaleGestureDetector mScaleGestureDetector; - private long mTouchDownTime; - private float mFirstX, mFirstY; - private float mLastX, mLastY; - private float mCenterX, mCenterY; - private float mMinScale; - private boolean mTouchEnabled = true; - private RectF mTempEdges = new RectF(); - private float[] mTempPoint = new float[] { 0, 0 }; - private float[] mTempCoef = new float[] { 0, 0 }; - private float[] mTempAdjustment = new float[] { 0, 0 }; - private float[] mTempImageDims = new float[] { 0, 0 }; - private float[] mTempRendererCenter = new float[] { 0, 0 }; - TouchCallback mTouchCallback; - Matrix mRotateMatrix; - Matrix mInverseRotateMatrix; - - public interface TouchCallback { - void onTouchDown(); - void onTap(); - void onTouchUp(); - } - - public CropView(Context context) { - this(context, null); - } - - public CropView(Context context, AttributeSet attrs) { - super(context, attrs); - mScaleGestureDetector = new ScaleGestureDetector(context, this); - mRotateMatrix = new Matrix(); - mInverseRotateMatrix = new Matrix(); - } - - private float[] getImageDims() { - final float imageWidth = mRenderer.source.getImageWidth(); - final float imageHeight = mRenderer.source.getImageHeight(); - float[] imageDims = mTempImageDims; - imageDims[0] = imageWidth; - imageDims[1] = imageHeight; - mRotateMatrix.mapPoints(imageDims); - imageDims[0] = Math.abs(imageDims[0]); - imageDims[1] = Math.abs(imageDims[1]); - return imageDims; - } - - private void getEdgesHelper(RectF edgesOut) { - final float width = getWidth(); - final float height = getHeight(); - final float[] imageDims = getImageDims(); - final float imageWidth = imageDims[0]; - final float imageHeight = imageDims[1]; - - float initialCenterX = mRenderer.source.getImageWidth() / 2f; - float initialCenterY = mRenderer.source.getImageHeight() / 2f; - - float[] rendererCenter = mTempRendererCenter; - rendererCenter[0] = mCenterX - initialCenterX; - rendererCenter[1] = mCenterY - initialCenterY; - mRotateMatrix.mapPoints(rendererCenter); - rendererCenter[0] += imageWidth / 2; - rendererCenter[1] += imageHeight / 2; - - final float scale = mRenderer.scale; - float centerX = (width / 2f - rendererCenter[0] + (imageWidth - width) / 2f) - * scale + width / 2f; - float centerY = (height / 2f - rendererCenter[1] + (imageHeight - height) / 2f) - * scale + height / 2f; - float leftEdge = centerX - imageWidth / 2f * scale; - float rightEdge = centerX + imageWidth / 2f * scale; - float topEdge = centerY - imageHeight / 2f * scale; - float bottomEdge = centerY + imageHeight / 2f * scale; - - edgesOut.left = leftEdge; - edgesOut.right = rightEdge; - edgesOut.top = topEdge; - edgesOut.bottom = bottomEdge; - } - - public int getImageRotation() { - return mRenderer.rotation; - } - - public RectF getCrop() { - final RectF edges = mTempEdges; - getEdgesHelper(edges); - final float scale = mRenderer.scale; - - float cropLeft = -edges.left / scale; - float cropTop = -edges.top / scale; - float cropRight = cropLeft + getWidth() / scale; - float cropBottom = cropTop + getHeight() / scale; - - return new RectF(cropLeft, cropTop, cropRight, cropBottom); - } - - public Point getSourceDimensions() { - return new Point(mRenderer.source.getImageWidth(), mRenderer.source.getImageHeight()); - } - - public void setTileSource(TileSource source, Runnable isReadyCallback) { - super.setTileSource(source, isReadyCallback); - mCenterX = mRenderer.centerX; - mCenterY = mRenderer.centerY; - mRotateMatrix.reset(); - mRotateMatrix.setRotate(mRenderer.rotation); - mInverseRotateMatrix.reset(); - mInverseRotateMatrix.setRotate(-mRenderer.rotation); - updateMinScale(getWidth(), getHeight(), source, true); - } - - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - updateMinScale(w, h, mRenderer.source, false); - } - - public void setScale(float scale) { - synchronized (mLock) { - mRenderer.scale = scale; - } - } - - private void updateMinScale(int w, int h, TileSource source, boolean resetScale) { - synchronized (mLock) { - if (resetScale) { - mRenderer.scale = 1; - } - if (source != null) { - final float[] imageDims = getImageDims(); - final float imageWidth = imageDims[0]; - final float imageHeight = imageDims[1]; - mMinScale = Math.max(w / imageWidth, h / imageHeight); - mRenderer.scale = - Math.max(mMinScale, resetScale ? Float.MIN_VALUE : mRenderer.scale); - } - } - } - - @Override - public boolean onScaleBegin(ScaleGestureDetector detector) { - return true; - } - - @Override - public boolean onScale(ScaleGestureDetector detector) { - // Don't need the lock because this will only fire inside of - // onTouchEvent - mRenderer.scale *= detector.getScaleFactor(); - mRenderer.scale = Math.max(mMinScale, mRenderer.scale); - invalidate(); - return true; - } - - @Override - public void onScaleEnd(ScaleGestureDetector detector) { - } - - /** - * Offsets wallpaper preview according to the state it will be displayed in upon returning home. - * @param offset Ranges from 0 to 1, where 0 is the leftmost parallax and 1 is the rightmost. - */ - public void setParallaxOffset(float offset, RectF crop) { - offset = Math.max(0, Math.min(offset, 1)); // Make sure the offset is in the correct range. - float screenWidth = getWidth() / mRenderer.scale; - mCenterX = screenWidth / 2 + offset * (crop.width() - screenWidth) + crop.left; - updateCenter(); - } - - public void moveToLeft() { - if (getWidth() == 0 || getHeight() == 0) { - final ViewTreeObserver observer = getViewTreeObserver(); - observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { - public void onGlobalLayout() { - moveToLeft(); - getViewTreeObserver().removeOnGlobalLayoutListener(this); - } - }); - } - final RectF edges = mTempEdges; - getEdgesHelper(edges); - final float scale = mRenderer.scale; - mCenterX += Math.ceil(edges.left / scale); - updateCenter(); - } - - private void updateCenter() { - mRenderer.centerX = Math.round(mCenterX); - mRenderer.centerY = Math.round(mCenterY); - } - - public void setTouchEnabled(boolean enabled) { - mTouchEnabled = enabled; - } - - public void setTouchCallback(TouchCallback cb) { - mTouchCallback = cb; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - int action = event.getActionMasked(); - final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP; - final int skipIndex = pointerUp ? event.getActionIndex() : -1; - - // Determine focal point - float sumX = 0, sumY = 0; - final int count = event.getPointerCount(); - for (int i = 0; i < count; i++) { - if (skipIndex == i) - continue; - sumX += event.getX(i); - sumY += event.getY(i); - } - final int div = pointerUp ? count - 1 : count; - float x = sumX / div; - float y = sumY / div; - - if (action == MotionEvent.ACTION_DOWN) { - mFirstX = x; - mFirstY = y; - mTouchDownTime = System.currentTimeMillis(); - if (mTouchCallback != null) { - mTouchCallback.onTouchDown(); - } - } else if (action == MotionEvent.ACTION_UP) { - ViewConfiguration config = ViewConfiguration.get(getContext()); - - float squaredDist = (mFirstX - x) * (mFirstX - x) + (mFirstY - y) * (mFirstY - y); - float slop = config.getScaledTouchSlop() * config.getScaledTouchSlop(); - long now = System.currentTimeMillis(); - if (mTouchCallback != null) { - // only do this if it's a small movement - if (squaredDist < slop && - now < mTouchDownTime + ViewConfiguration.getTapTimeout()) { - mTouchCallback.onTap(); - } - mTouchCallback.onTouchUp(); - } - } - - if (!mTouchEnabled) { - return true; - } - - synchronized (mLock) { - mScaleGestureDetector.onTouchEvent(event); - switch (action) { - case MotionEvent.ACTION_MOVE: - float[] point = mTempPoint; - point[0] = (mLastX - x) / mRenderer.scale; - point[1] = (mLastY - y) / mRenderer.scale; - mInverseRotateMatrix.mapPoints(point); - mCenterX += point[0]; - mCenterY += point[1]; - updateCenter(); - invalidate(); - break; - } - if (mRenderer.source != null) { - // Adjust position so that the wallpaper covers the entire area - // of the screen - final RectF edges = mTempEdges; - getEdgesHelper(edges); - final float scale = mRenderer.scale; - - float[] coef = mTempCoef; - coef[0] = 1; - coef[1] = 1; - mRotateMatrix.mapPoints(coef); - float[] adjustment = mTempAdjustment; - mTempAdjustment[0] = 0; - mTempAdjustment[1] = 0; - if (edges.left > 0) { - adjustment[0] = edges.left / scale; - } else if (edges.right < getWidth()) { - adjustment[0] = (edges.right - getWidth()) / scale; - } - if (edges.top > 0) { - adjustment[1] = (float) Math.ceil(edges.top / scale); - } else if (edges.bottom < getHeight()) { - adjustment[1] = (edges.bottom - getHeight()) / scale; - } - for (int dim = 0; dim <= 1; dim++) { - if (coef[dim] > 0) adjustment[dim] = (float) Math.ceil(adjustment[dim]); - } - - mInverseRotateMatrix.mapPoints(adjustment); - mCenterX += adjustment[0]; - mCenterY += adjustment[1]; - updateCenter(); - } - } - - mLastX = x; - mLastY = y; - return true; - } -} diff --git a/WallpaperPicker/src/com/android/launcher3/DrawableTileSource.java b/WallpaperPicker/src/com/android/launcher3/DrawableTileSource.java deleted file mode 100644 index c1f2eff0f..000000000 --- a/WallpaperPicker/src/com/android/launcher3/DrawableTileSource.java +++ /dev/null @@ -1,102 +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.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; - -import com.android.gallery3d.glrenderer.BasicTexture; -import com.android.gallery3d.glrenderer.BitmapTexture; -import com.android.photos.views.TiledImageRenderer; - -public class DrawableTileSource implements TiledImageRenderer.TileSource { - private static final int GL_SIZE_LIMIT = 2048; - // This must be no larger than half the size of the GL_SIZE_LIMIT - // due to decodePreview being allowed to be up to 2x the size of the target - public static final int MAX_PREVIEW_SIZE = GL_SIZE_LIMIT / 2; - - private int mTileSize; - private int mPreviewSize; - private Drawable mDrawable; - private BitmapTexture mPreview; - - public DrawableTileSource(Context context, Drawable d, int previewSize) { - mTileSize = TiledImageRenderer.suggestedTileSize(context); - mDrawable = d; - mPreviewSize = Math.min(previewSize, MAX_PREVIEW_SIZE); - } - - @Override - public int getTileSize() { - return mTileSize; - } - - @Override - public int getImageWidth() { - return mDrawable.getIntrinsicWidth(); - } - - @Override - public int getImageHeight() { - return mDrawable.getIntrinsicHeight(); - } - - @Override - public int getRotation() { - return 0; - } - - @Override - public BasicTexture getPreview() { - if (mPreviewSize == 0) { - return null; - } - if (mPreview == null){ - float width = getImageWidth(); - float height = getImageHeight(); - while (width > MAX_PREVIEW_SIZE || height > MAX_PREVIEW_SIZE) { - width /= 2; - height /= 2; - } - Bitmap b = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888); - Canvas c = new Canvas(b); - mDrawable.setBounds(new Rect(0, 0, (int) width, (int) height)); - mDrawable.draw(c); - c.setBitmap(null); - mPreview = new BitmapTexture(b); - } - return mPreview; - } - - @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); - } - Canvas c = new Canvas(bitmap); - Rect bounds = new Rect(0, 0, getImageWidth(), getImageHeight()); - bounds.offset(-x, -y); - mDrawable.setBounds(bounds); - mDrawable.draw(c); - c.setBitmap(null); - return bitmap; - } -} diff --git a/WallpaperPicker/src/com/android/launcher3/LauncherWallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/LauncherWallpaperPickerActivity.java deleted file mode 100644 index 091c05462..000000000 --- a/WallpaperPicker/src/com/android/launcher3/LauncherWallpaperPickerActivity.java +++ /dev/null @@ -1,21 +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; - -// TODO: Remove this class -public class LauncherWallpaperPickerActivity extends WallpaperPickerActivity { -}
\ No newline at end of file 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 deleted file mode 100644 index 64b0ac466..000000000 --- a/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java +++ /dev/null @@ -1,224 +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.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.graphics.Bitmap; -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 java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; - - -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 { - private int mDbId; - public SavedWallpaperTile(int dbId, File target, Drawable thumb) { - super(target, thumb); - mDbId = dbId; - } - - @Override - public void onDelete(WallpaperPickerActivity a) { - a.getSavedImages().deleteImage(mDbId); - } - } - - 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>(); - SQLiteDatabase db = mDb.getReadableDatabase(); - Cursor result = db.query(ImageDb.TABLE_NAME, - new String[] { ImageDb.COLUMN_ID, - ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME, - ImageDb.COLUMN_IMAGE_FILENAME}, // cols to return - null, // select query - null, // args to select query - null, - null, - ImageDb.COLUMN_ID + " DESC", - null); - - while (result.moveToNext()) { - String filename = result.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.close(); - } - - public int getCount() { - return mImages.size(); - } - - 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); - } - - 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 - ImageDb.COLUMN_ID + " = ?", // select query - new String[] { Integer.toString(id) }, // args to select query - null, - 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; - } - } - - 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[] { - Integer.toString(id) // args to SELECT query - }); - } - - public void writeImage(Bitmap thumbnail, byte[] imageBytes) { - try { - File imageFile = File.createTempFile("wallpaper", "", mContext.getFilesDir()); - FileOutputStream imageFileStream = - mContext.openFileOutput(imageFile.getName(), Context.MODE_PRIVATE); - imageFileStream.write(imageBytes); - imageFileStream.close(); - - File thumbFile = File.createTempFile("wallpaperthumb", "", mContext.getFilesDir()); - FileOutputStream thumbFileStream = - mContext.openFileOutput(thumbFile.getName(), Context.MODE_PRIVATE); - thumbnail.compress(Bitmap.CompressFormat.JPEG, 95, thumbFileStream); - thumbFileStream.close(); - - SQLiteDatabase db = mDb.getWritableDatabase(); - ContentValues values = new ContentValues(); - values.put(ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME, thumbFile.getName()); - values.put(ImageDb.COLUMN_IMAGE_FILENAME, imageFile.getName()); - db.insert(ImageDb.TABLE_NAME, null, values); - } catch (IOException e) { - Log.e(TAG, "Failed writing images to storage " + e); - } - } - - 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) { - // 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 - File oldSavedImagesFile = new File(context.getCacheDir(), - LauncherFiles.WALLPAPER_IMAGES_DB); - File savedImagesFile = context.getDatabasePath(LauncherFiles.WALLPAPER_IMAGES_DB); - if (oldSavedImagesFile.exists()) { - oldSavedImagesFile.renameTo(savedImagesFile); - } - } - @Override - public void onCreate(SQLiteDatabase database) { - database.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" + - COLUMN_ID + " INTEGER NOT NULL, " + - COLUMN_IMAGE_THUMBNAIL_FILENAME + " TEXT NOT NULL, " + - COLUMN_IMAGE_FILENAME + " TEXT NOT NULL, " + - "PRIMARY KEY (" + COLUMN_ID + " ASC) " + - ");"); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (oldVersion != newVersion) { - // Delete all the records; they'll be repopulated as this is a cache - db.execSQL("DELETE FROM " + TABLE_NAME); - } - } - } -} 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/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java deleted file mode 100644 index 75bdb8a61..000000000 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java +++ /dev/null @@ -1,493 +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.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.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Matrix; -import android.graphics.Point; -import android.graphics.RectF; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Message; -import android.util.Log; -import android.view.Display; -import android.view.View; -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.base.BaseActivity; -import com.android.launcher3.util.Thunk; -import com.android.launcher3.util.WallpaperUtils; -import com.android.photos.BitmapRegionTileSource; -import com.android.photos.BitmapRegionTileSource.BitmapSource; -import com.android.photos.BitmapRegionTileSource.BitmapSource.InBitmapProvider; -import com.android.photos.views.TiledImageRenderer.TileSource; - -import java.util.Collections; -import java.util.Set; -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; - private Handler mLoaderHandler; - @Thunk LoadRequest mCurrentLoadRequest; - private byte[] mTempStorageForDecoding = new byte[16 * 1024]; - // A weak-set of reusable bitmaps - @Thunk Set<Bitmap> mReusableBitmaps = - Collections.newSetFromMap(new WeakHashMap<Bitmap, Boolean>()); - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mLoaderThread = new HandlerThread("wallpaper_loader"); - mLoaderThread.start(); - mLoaderHandler = new Handler(mLoaderThread.getLooper(), this); - - init(); - if (!enableRotation()) { - setRequestedOrientation(Configuration.ORIENTATION_PORTRAIT); - } - } - - protected void init() { - setContentView(R.layout.wallpaper_cropper); - - mCropView = (CropView) findViewById(R.id.cropView); - mProgressView = findViewById(R.id.loading); - - Intent cropIntent = getIntent(); - final Uri imageUri = cropIntent.getData(); - - if (imageUri == null) { - Log.e(LOGTAG, "No URI passed in intent, exiting WallpaperCropActivity"); - finish(); - return; - } - - // Action bar - // Show the custom action bar view - final ActionBar actionBar = getActionBar(); - actionBar.setCustomView(R.layout.actionbar_set_wallpaper); - actionBar.getCustomView().setOnClickListener( - new View.OnClickListener() { - @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); - } - }); - mSetWallpaperButton = findViewById(R.id.set_wallpaper_button); - - // Load image in background - final BitmapRegionTileSource.UriBitmapSource bitmapSource = - new BitmapRegionTileSource.UriBitmapSource(getContext(), imageUri); - mSetWallpaperButton.setEnabled(false); - Runnable onLoad = new Runnable() { - public void run() { - if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) { - Toast.makeText(getContext(), R.string.wallpaper_load_fail, - Toast.LENGTH_LONG).show(); - finish(); - } else { - mSetWallpaperButton.setEnabled(true); - } - } - }; - setCropViewTileSource(bitmapSource, true, false, null, onLoad); - } - - @Override - public void onDestroy() { - if (mCropView != null) { - mCropView.destroy(); - } - if (mLoaderThread != null) { - mLoaderThread.quit(); - } - super.onDestroy(); - } - - /** - * This is called on {@link #mLoaderThread} - */ - @Override - public boolean handleMessage(Message msg) { - if (msg.what == MSG_LOAD_IMAGE) { - final LoadRequest req = (LoadRequest) msg.obj; - 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); - } - } - 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; - } - } - - 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); - } else { - addReusableBitmap(req.result); - } - } - }); - return true; - } - return false; - } - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) - protected boolean isActivityDestroyed() { - return Utilities.ATLEAST_JB_MR1 && isDestroyed(); - } - - @Thunk void addReusableBitmap(TileSource src) { - synchronized (mReusableBitmaps) { - if (Utilities.ATLEAST_KITKAT && src instanceof BitmapRegionTileSource) { - Bitmap preview = ((BitmapRegionTileSource) src).getBitmap(); - if (preview != null && preview.isMutable()) { - mReusableBitmaps.add(preview); - } - } - } - } - - protected void onLoadRequestComplete(LoadRequest req, boolean success) { - mCurrentLoadRequest = null; - if (success) { - TileSource oldSrc = mCropView.getTileSource(); - mCropView.setTileSource(req.result, null); - mCropView.setTouchEnabled(req.touchEnabled); - if (req.moveToLeft) { - mCropView.moveToLeft(); - } - if (req.scaleAndOffsetProvider != null) { - TileSource src = req.result; - Point wallpaperSize = WallpaperUtils.getDefaultWallpaperSize( - getResources(), getWindowManager()); - RectF crop = Utils.getMaxCropRect(src.getImageWidth(), src.getImageHeight(), - wallpaperSize.x, wallpaperSize.y, false /* leftAligned */); - mCropView.setScale(req.scaleAndOffsetProvider.getScale(wallpaperSize, crop)); - mCropView.setParallaxOffset(req.scaleAndOffsetProvider.getParallaxOffset(), crop); - } - - // Free last image - if (oldSrc != null) { - // Call yield instead of recycle, as we only want to free GL resource. - // We can still reuse the bitmap for decoding any other image. - oldSrc.getPreview().yield(); - } - addReusableBitmap(oldSrc); - } - if (req.postExecute != null) { - req.postExecute.run(); - } - mProgressView.setVisibility(View.GONE); - } - - public final void setCropViewTileSource(BitmapSource bitmapSource, boolean touchEnabled, - boolean moveToLeft, CropViewScaleAndOffsetProvider scaleProvider, Runnable postExecute) { - final LoadRequest req = new LoadRequest(); - req.moveToLeft = moveToLeft; - req.src = bitmapSource; - req.touchEnabled = touchEnabled; - req.postExecute = postExecute; - req.scaleAndOffsetProvider = scaleProvider; - mCurrentLoadRequest = req; - - // Remove any pending requests - mLoaderHandler.removeMessages(MSG_LOAD_IMAGE); - Message.obtain(mLoaderHandler, MSG_LOAD_IMAGE, req).sendToTarget(); - - // We don't want to show the spinner every time we load an image, because that would be - // annoying; instead, only start showing the spinner if loading the image has taken - // longer than 1 sec (ie 1000 ms) - mProgressView.postDelayed(new Runnable() { - public void run() { - if (mCurrentLoadRequest == req) { - mProgressView.setVisibility(View.VISIBLE); - } - } - }, 1000); - } - - - public boolean enableRotation() { - return getResources().getBoolean(R.bool.allow_rotation); - } - - protected void setWallpaper(Uri uri, final boolean finishActivityWhenDone, - final 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); - cropTask.setNoCrop(true); - cropTask.execute(); - } - - protected void cropImageAndSetWallpaper(Resources res, int resId, - final boolean finishActivityWhenDone, final boolean shouldFadeOutOnFinish) { - // crop this image and scale it down to the default wallpaper size for - // this device - int rotation = BitmapUtils.getRotationFromExif(res, resId); - 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); - } - } - } - }; - 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, - BitmapCropTask.OnBitmapCroppedHandler onBitmapCroppedHandler, - final boolean finishActivityWhenDone, final boolean shouldFadeOutOnFinish) { - // Give some feedback so user knows something is happening. - mProgressView.setVisibility(View.VISIBLE); - - boolean centerCrop = getResources().getBoolean(R.bool.center_crop); - // Get the crop - boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; - - Display d = getWindowManager().getDefaultDisplay(); - - Point displaySize = new Point(); - d.getSize(displaySize); - boolean isPortrait = displaySize.x < displaySize.y; - - Point defaultWallpaperSize = WallpaperUtils.getDefaultWallpaperSize(getResources(), - getWindowManager()); - // Get the crop - RectF cropRect = mCropView.getCrop(); - - Point inSize = mCropView.getSourceDimensions(); - - int cropRotation = mCropView.getImageRotation(); - float cropScale = mCropView.getWidth() / (float) cropRect.width(); - - - Matrix rotateMatrix = new Matrix(); - rotateMatrix.setRotate(cropRotation); - float[] rotatedInSize = new float[] { inSize.x, inSize.y }; - rotateMatrix.mapPoints(rotatedInSize); - rotatedInSize[0] = Math.abs(rotatedInSize[0]); - rotatedInSize[1] = Math.abs(rotatedInSize[1]); - - - // due to rounding errors in the cropview renderer the edges can be slightly offset - // therefore we ensure that the boundaries are sanely defined - cropRect.left = Math.max(0, cropRect.left); - cropRect.right = Math.min(rotatedInSize[0], cropRect.right); - cropRect.top = Math.max(0, cropRect.top); - cropRect.bottom = Math.min(rotatedInSize[1], cropRect.bottom); - - // ADJUST CROP WIDTH - // Extend the crop all the way to the right, for parallax - // (or all the way to the left, in RTL) - float extraSpace; - if (centerCrop) { - extraSpace = 2f * Math.min(rotatedInSize[0] - cropRect.right, cropRect.left); - } else { - extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left; - } - // Cap the amount of extra width - float maxExtraSpace = defaultWallpaperSize.x / cropScale - cropRect.width(); - extraSpace = Math.min(extraSpace, maxExtraSpace); - - if (centerCrop) { - cropRect.left -= extraSpace / 2f; - cropRect.right += extraSpace / 2f; - } else { - if (ltr) { - cropRect.right += extraSpace; - } else { - cropRect.left -= extraSpace; - } - } - - // ADJUST CROP HEIGHT - if (isPortrait) { - cropRect.bottom = cropRect.top + defaultWallpaperSize.y / cropScale; - } else { // LANDSCAPE - float extraPortraitHeight = - defaultWallpaperSize.y / cropScale - cropRect.height(); - float expandHeight = - Math.min(Math.min(rotatedInSize[1] - cropRect.bottom, cropRect.top), - extraPortraitHeight / 2); - cropRect.top -= expandHeight; - cropRect.bottom += expandHeight; - } - final int outWidth = (int) Math.round(cropRect.width() * cropScale); - final int outHeight = (int) Math.round(cropRect.height() * cropScale); - - 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) { - cropTask.setOnBitmapCropped(onBitmapCroppedHandler); - } - 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); - } - editor.apply(); - WallpaperUtils.suggestWallpaperDimension(getResources(), - sp, getWindowManager(), WallpaperManager.getInstance(getContext()), true); - } - - static class LoadRequest { - BitmapSource src; - boolean touchEnabled; - boolean moveToLeft; - Runnable postExecute; - CropViewScaleAndOffsetProvider scaleAndOffsetProvider; - - TileSource result; - } - - 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 deleted file mode 100644 index 5d4123843..000000000 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ /dev/null @@ -1,1205 +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.Manifest; -import android.animation.LayoutTransition; -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.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; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.View.OnLayoutChangeListener; -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 java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; - -public class WallpaperPickerActivity extends WallpaperCropActivity { - static final String TAG = "Launcher.WallpaperPickerActivity"; - - public static final int IMAGE_PICK = 5; - public static final int PICK_WALLPAPER_THIRD_PARTY_ACTIVITY = 6; - /** An Intent extra used when opening the wallpaper picker from the workspace overlay. */ - public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET"; - private static final String TEMP_WALLPAPER_TILES = "TEMP_WALLPAPER_TILES"; - 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 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 - * @param visible should the system wallpaper be shown - */ - protected void setSystemWallpaperVisiblity(final boolean visible) { - // hide our own wallpaper preview if necessary - if(!visible) { - mCropView.setVisibility(View.VISIBLE); - } else { - changeWallpaperFlags(visible); - } - // the change of the flag must be delayed in order to avoid flickering, - // a simple post / double post does not suffice here - mCropView.postDelayed(new Runnable() { - @Override - public void run() { - if(!visible) { - changeWallpaperFlags(visible); - } else { - mCropView.setVisibility(View.INVISIBLE); - } - } - }, FLAG_POST_DELAY_MILLIS); - } - - @Thunk void changeWallpaperFlags(boolean visible) { - int desiredWallpaperFlag = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0; - int currentWallpaperFlag = getWindow().getAttributes().flags - & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; - if (desiredWallpaperFlag != currentWallpaperFlag) { - getWindow().setFlags(desiredWallpaperFlag, - WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER); - } - } - - @Override - protected void onLoadRequestComplete(LoadRequest req, boolean success) { - super.onLoadRequestComplete(req, success); - if (success) { - setSystemWallpaperVisiblity(false); - } - } - - // called by onCreate; this is subclassed to overwrite WallpaperCropActivity - protected void init() { - setContentView(R.layout.wallpaper_picker); - - mCropView = (CropView) findViewById(R.id.cropView); - mCropView.setVisibility(View.INVISIBLE); - - 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; - } - }; - - 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); - initializeScrollForRtl(); - updateTileIndices(); - } - }); - - // 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); - - // 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); - - // Select the first item; wait for a layout pass so that we initialize the dimensions of - // cropView or the defaultWallpaperView first - mCropView.addOnLayoutChangeListener(new OnLayoutChangeListener() { - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, - 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)); - setSystemWallpaperVisiblity(false); - } - v.removeOnLayoutChangeListener(this); - } - } - }); - - updateTileIndices(); - - // Update the scroll for RTL - initializeScrollForRtl(); - - // Create smooth layout transitions for when items are deleted - final LayoutTransition transitioner = new LayoutTransition(); - transitioner.setDuration(200); - transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0); - transitioner.setAnimator(LayoutTransition.DISAPPEARING, null); - mWallpapersView.setLayoutTransition(transitioner); - - // Action bar - // Show the custom action bar view - final ActionBar actionBar = getActionBar(); - actionBar.setCustomView(R.layout.actionbar_set_wallpaper); - actionBar.getCustomView().setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View v) { - // Ensure that a tile is slelected and loaded. - if (mSelectedTile != null && mCropView.getTileSource() != null) { - // Prevent user from selecting any new tile. - mWallpaperStrip.setVisibility(View.GONE); - actionBar.hide(); - - WallpaperTileInfo info = (WallpaperTileInfo) mSelectedTile.getTag(); - info.onSave(WallpaperPickerActivity.this); - } else { - // no tile was selected, so we just finish the activity and go back - setResult(Activity.RESULT_OK); - finish(); - } - } - }); - 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 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 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; - } - }; - } - - public void setWallpaperButtonEnabled(boolean enabled) { - mSetWallpaperButton.setEnabled(enabled); - } - - public float getWallpaperParallaxOffset() { - return mWallpaperParallaxOffset; - } - - @Thunk void selectTile(View v) { - if (mSelectedTile != null) { - mSelectedTile.setSelected(false); - mSelectedTile = null; - } - mSelectedTile = v; - v.setSelected(true); - mSelectedIndex = mWallpapersView.indexOfChild(v); - // TODO: Remove this once the accessibility framework and - // services have better support for selection state. - v.announceForAccessibility( - getContext().getString(R.string.announce_selection, v.getContentDescription())); - } - - @Thunk void initializeScrollForRtl() { - if (Utilities.isRtl(getResources())) { - final ViewTreeObserver observer = mWallpaperScrollContainer.getViewTreeObserver(); - observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { - public void onGlobalLayout() { - LinearLayout masterWallpaperList = - (LinearLayout) findViewById(R.id.master_wallpaper_list); - mWallpaperScrollContainer.scrollTo(masterWallpaperList.getWidth(), 0); - mWallpaperScrollContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this); - } - }); - } - } - - 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); - if (mWallpaperStrip.getAlpha() < 1f) { - mWallpaperStrip.setAlpha(1f); - mWallpaperStrip.setVisibility(View.VISIBLE); - } - } - - public void onSaveInstanceState(Bundle outState) { - outState.putParcelableArrayList(TEMP_WALLPAPER_TILES, mTempWallpaperTiles); - outState.putInt(SELECTED_INDEX, mSelectedIndex); - } - - protected void onRestoreInstanceState(Bundle savedInstanceState) { - ArrayList<Uri> uris = savedInstanceState.getParcelableArrayList(TEMP_WALLPAPER_TILES); - for (Uri uri : uris) { - addTemporaryWallpaperTile(uri, true); - } - 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(); - final Resources res = getResources(); - - // Do two passes; the first pass gets the total number of tiles - int numTiles = 0; - for (int passNum = 0; passNum < 2; passNum++) { - int tileIndex = 0; - for (int i = 0; i < childCount; i++) { - View child = masterWallpaperList.getChildAt(i); - LinearLayout subList; - - int subListStart; - int subListEnd; - if (child.getTag() instanceof WallpaperTileInfo) { - subList = masterWallpaperList; - subListStart = i; - subListEnd = i + 1; - } else { // if (child instanceof LinearLayout) { - subList = (LinearLayout) child; - subListStart = 0; - subListEnd = subList.getChildCount(); - } - - for (int j = subListStart; j < subListEnd; j++) { - WallpaperTileInfo info = (WallpaperTileInfo) subList.getChildAt(j).getTag(); - if (info.isNamelessWallpaper()) { - if (passNum == 0) { - numTiles++; - } else { - CharSequence label = res.getString( - R.string.wallpaper_accessibility_name, ++tileIndex, numTiles); - info.onIndexUpdated(label); - } - } - } - } - } - } - - @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; - int indexOfExistingTile = 0; - for (; indexOfExistingTile < mWallpapersView.getChildCount(); indexOfExistingTile++) { - FrameLayout thumbnail = (FrameLayout) mWallpapersView.getChildAt(indexOfExistingTile); - Object tag = thumbnail.getTag(); - if (tag instanceof UriWallpaperInfo && ((UriWallpaperInfo) tag).mUri.equals(uri)) { - existingImageThumbnail = thumbnail; - break; - } - } - final FrameLayout pickedImageThumbnail; - if (existingImageThumbnail != null) { - pickedImageThumbnail = existingImageThumbnail; - // Always move the existing wallpaper to the front so user can see it without scrolling. - mWallpapersView.removeViewAt(indexOfExistingTile); - mWallpapersView.addView(existingImageThumbnail, 0); - } 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); - mTempWallpaperTiles.add(uri); - } - - // 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); - } - } - - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == IMAGE_PICK && resultCode == Activity.RESULT_OK) { - if (data != null && data.getData() != null) { - Uri uri = data.getData(); - addTemporaryWallpaperTile(uri, false); - } - } else if (requestCode == PICK_WALLPAPER_THIRD_PARTY_ACTIVITY - && resultCode == Activity.RESULT_OK) { - // Something was set on the third-party activity. - setResult(Activity.RESULT_OK); - finish(); - } - } - - 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); - - Partner partner = Partner.get(pm); - if (partner != null) { - final Resources partnerRes = partner.getResources(); - final int resId = partnerRes.getIdentifier(Partner.RES_WALLPAPERS, "array", - partner.getPackageName()); - if (resId != 0) { - addWallpapers(bundled, partnerRes, partner.getPackageName(), resId); - } - - // Add system wallpapers - File systemDir = partner.getWallpaperDirectory(); - if (systemDir != null && systemDir.isDirectory()) { - for (File file : systemDir.listFiles()) { - if (!file.isFile()) { - continue; - } - String name = file.getName(); - int dotPos = name.lastIndexOf('.'); - String extension = ""; - if (dotPos >= -1) { - extension = name.substring(dotPos); - name = name.substring(0, dotPos); - } - - if (name.endsWith("_small")) { - // it is a thumbnail - continue; - } - - 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))); - } - } - } - } - - Pair<ApplicationInfo, Integer> r = getWallpaperArrayResourceId(); - if (r != null) { - try { - Resources wallpaperRes = getContext().getPackageManager() - .getResourcesForApplication(r.first); - addWallpapers(bundled, wallpaperRes, r.first.packageName, r.second); - } catch (PackageManager.NameNotFoundException e) { - } - } - - if (partner == null || !partner.hideDefaultWallpaper()) { - // Add an entry for the default wallpaper (stored in system resources) - WallpaperTileInfo defaultWallpaperInfo = Utilities.ATLEAST_KITKAT - ? getDefaultWallpaper() : getPreKKDefaultWallpaperInfo(); - if (defaultWallpaperInfo != null) { - bundled.add(0, defaultWallpaperInfo); - } - } - 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, - // com.android.launcher3. So we ask Resources for what it thinks the - // package name should be. - final String packageName = getResources().getResourcePackageName(R.array.wallpapers); - try { - ApplicationInfo info = getContext().getPackageManager().getApplicationInfo(packageName, 0); - return new Pair<ApplicationInfo, Integer>(info, R.array.wallpapers); - } catch (PackageManager.NameNotFoundException e) { - return null; - } - } - - private void addWallpapers(ArrayList<WallpaperTileInfo> known, Resources res, - String packageName, int listResId) { - final String[] extras = res.getStringArray(listResId); - for (String extra : extras) { - int resId = res.getIdentifier(extra, "drawable", packageName); - if (resId != 0) { - final int thumbRes = res.getIdentifier(extra + "_small", "drawable", packageName); - - if (thumbRes != 0) { - ResourceWallpaperInfo wallpaperInfo = - new ResourceWallpaperInfo(res, resId, res.getDrawable(thumbRes)); - known.add(wallpaperInfo); - // Log.d(TAG, "add: [" + packageName + "]: " + extra + " (" + res + ")"); - } - } else { - Log.e(TAG, "Couldn't find wallpaper " + extra); - } - } - } - - 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 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); - } - } - - public static View createImageTileView(LayoutInflater layoutInflater, - View convertView, ViewGroup parent, Drawable thumb) { - View view; - - if (convertView == null) { - view = layoutInflater.inflate(R.layout.wallpaper_picker_item, parent, false); - } else { - view = convertView; - } - - ImageView image = (ImageView) view.findViewById(R.id.wallpaper_image); - - if (thumb != null) { - image.setImageDrawable(thumb); - thumb.setDither(true); - } - - return view; - } - - public void startActivityForResultSafely(Intent intent, int requestCode) { - Utilities.startActivityForResultSafely(getActivity(), intent, requestCode); - } - - @Override - public boolean enableRotation() { - // Check if rotation is enabled for this device. - if (Utilities.isRotationAllowedForDevice(getContext())) - return true; - - // Check if the user has specifically enabled rotation via preferences. - return Utilities.isAllowRotationPrefEnabled(getApplicationContext(), true); - } -} diff --git a/WallpaperPicker/src/com/android/launcher3/base/BaseActivity.java b/WallpaperPicker/src/com/android/launcher3/base/BaseActivity.java deleted file mode 100644 index f8541188f..000000000 --- a/WallpaperPicker/src/com/android/launcher3/base/BaseActivity.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.android.launcher3.base; - -import android.app.Activity; -import android.content.Context; - -/** - * A wrapper over {@link Activity} which allows to override some methods. - * The base implementation can change from an Activity to a Fragment (or any other custom - * implementation), Callers should not assume that the base class extends Context, instead use - * either {@link #getContext} or {@link #getActivity} - */ -public class BaseActivity extends Activity { - - public Context getContext() { - return this; - } - - public Activity getActivity() { - return this; - } -} diff --git a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java deleted file mode 100644 index 6baac6a6b..000000000 --- a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java +++ /dev/null @@ -1,481 +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.photos; - -import android.annotation.TargetApi; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.BitmapRegionDecoder; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.net.Uri; -import android.opengl.GLUtils; -import android.os.Build; -import android.util.Log; - -import com.android.gallery3d.common.BitmapUtils; -import com.android.gallery3d.common.Utils; -import com.android.gallery3d.exif.ExifInterface; -import com.android.gallery3d.glrenderer.BasicTexture; -import com.android.gallery3d.glrenderer.BitmapTexture; -import com.android.photos.views.TiledImageRenderer; - -import java.io.BufferedInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; - -interface SimpleBitmapRegionDecoder { - int getWidth(); - int getHeight(); - Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options); -} - -class SimpleBitmapRegionDecoderWrapper implements SimpleBitmapRegionDecoder { - BitmapRegionDecoder mDecoder; - private SimpleBitmapRegionDecoderWrapper(BitmapRegionDecoder decoder) { - mDecoder = decoder; - } - public static SimpleBitmapRegionDecoderWrapper newInstance( - String pathName, boolean isShareable) { - try { - BitmapRegionDecoder d = BitmapRegionDecoder.newInstance(pathName, isShareable); - if (d != null) { - return new SimpleBitmapRegionDecoderWrapper(d); - } - } catch (IOException e) { - Log.w("BitmapRegionTileSource", "getting decoder failed for path " + pathName, e); - return null; - } - return null; - } - public static SimpleBitmapRegionDecoderWrapper newInstance( - InputStream is, boolean isShareable) { - try { - BitmapRegionDecoder d = BitmapRegionDecoder.newInstance(is, isShareable); - if (d != null) { - return new SimpleBitmapRegionDecoderWrapper(d); - } - } catch (IOException e) { - Log.w("BitmapRegionTileSource", "getting decoder failed", e); - return null; - } - return null; - } - public int getWidth() { - return mDecoder.getWidth(); - } - public int getHeight() { - return mDecoder.getHeight(); - } - public Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options) { - return mDecoder.decodeRegion(wantRegion, options); - } -} - -class DumbBitmapRegionDecoder implements SimpleBitmapRegionDecoder { - Bitmap mBuffer; - Canvas mTempCanvas; - Paint mTempPaint; - private DumbBitmapRegionDecoder(Bitmap b) { - mBuffer = b; - } - public static DumbBitmapRegionDecoder newInstance(String pathName) { - Bitmap b = BitmapFactory.decodeFile(pathName); - if (b != null) { - return new DumbBitmapRegionDecoder(b); - } - return null; - } - public static DumbBitmapRegionDecoder newInstance(InputStream is) { - Bitmap b = BitmapFactory.decodeStream(is); - if (b != null) { - return new DumbBitmapRegionDecoder(b); - } - return null; - } - public int getWidth() { - return mBuffer.getWidth(); - } - public int getHeight() { - return mBuffer.getHeight(); - } - public Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options) { - if (mTempCanvas == null) { - mTempCanvas = new Canvas(); - mTempPaint = new Paint(); - mTempPaint.setFilterBitmap(true); - } - int sampleSize = Math.max(options.inSampleSize, 1); - Bitmap newBitmap = Bitmap.createBitmap( - wantRegion.width() / sampleSize, - wantRegion.height() / sampleSize, - Bitmap.Config.ARGB_8888); - mTempCanvas.setBitmap(newBitmap); - mTempCanvas.save(); - mTempCanvas.scale(1f / sampleSize, 1f / sampleSize); - mTempCanvas.drawBitmap(mBuffer, -wantRegion.left, -wantRegion.top, mTempPaint); - mTempCanvas.restore(); - mTempCanvas.setBitmap(null); - return newBitmap; - } -} - -/** - * A {@link com.android.photos.views.TiledImageRenderer.TileSource} using - * {@link BitmapRegionDecoder} to wrap a local file - */ -@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) -public class BitmapRegionTileSource implements TiledImageRenderer.TileSource { - - private static final String TAG = "BitmapRegionTileSource"; - - private static final int GL_SIZE_LIMIT = 2048; - // This must be no larger than half the size of the GL_SIZE_LIMIT - // due to decodePreview being allowed to be up to 2x the size of the target - private static final int MAX_PREVIEW_SIZE = GL_SIZE_LIMIT / 2; - - public static abstract class BitmapSource { - private SimpleBitmapRegionDecoder mDecoder; - private Bitmap mPreview; - private int mRotation; - public enum State { NOT_LOADED, LOADED, ERROR_LOADING }; - private State mState = State.NOT_LOADED; - - /** 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()); - } - } - mDecoder = loadBitmapRegionDecoder(); - if (mDecoder == null) { - mState = State.ERROR_LOADING; - return false; - } else { - int width = mDecoder.getWidth(); - int height = mDecoder.getHeight(); - - BitmapFactory.Options opts = new BitmapFactory.Options(); - opts.inPreferredConfig = Bitmap.Config.ARGB_8888; - opts.inPreferQualityOverSpeed = true; - - float scale = (float) MAX_PREVIEW_SIZE / Math.max(width, height); - opts.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale); - opts.inJustDecodeBounds = false; - opts.inMutable = true; - - if (bitmapProvider != null) { - int expectedPixles = (width / opts.inSampleSize) * (height / opts.inSampleSize); - Bitmap reusableBitmap = bitmapProvider.forPixelCount(expectedPixles); - if (reusableBitmap != null) { - // Try loading with reusable bitmap - opts.inBitmap = reusableBitmap; - try { - mPreview = loadPreviewBitmap(opts); - } catch (IllegalArgumentException e) { - Log.d(TAG, "Unable to reuse bitmap", e); - opts.inBitmap = null; - mPreview = null; - } - } - } - if (mPreview == null) { - mPreview = loadPreviewBitmap(opts); - } - if (mPreview == null) { - mState = State.ERROR_LOADING; - return false; - } - - // Verify that the bitmap can be used on GL surface - try { - GLUtils.getInternalFormat(mPreview); - GLUtils.getType(mPreview); - mState = State.LOADED; - } catch (IllegalArgumentException e) { - Log.d(TAG, "Image cannot be rendered on a GL surface", e); - mState = State.ERROR_LOADING; - } - return mState == State.LOADED; - } - } - - public State getLoadingState() { - return mState; - } - - public SimpleBitmapRegionDecoder getBitmapRegionDecoder() { - return mDecoder; - } - - public Bitmap getPreviewBitmap() { - return mPreview; - } - - public int getRotation() { - return mRotation; - } - - public abstract boolean readExif(ExifInterface ei); - public abstract SimpleBitmapRegionDecoder loadBitmapRegionDecoder(); - public abstract Bitmap loadPreviewBitmap(BitmapFactory.Options options); - - public interface InBitmapProvider { - Bitmap forPixelCount(int count); - } - } - - public static class FilePathBitmapSource extends BitmapSource { - private String mPath; - public FilePathBitmapSource(String path) { - mPath = path; - } - @Override - public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() { - SimpleBitmapRegionDecoder d; - d = SimpleBitmapRegionDecoderWrapper.newInstance(mPath, true); - if (d == null) { - d = DumbBitmapRegionDecoder.newInstance(mPath); - } - return d; - } - @Override - public Bitmap loadPreviewBitmap(BitmapFactory.Options options) { - return BitmapFactory.decodeFile(mPath, options); - } - @Override - public boolean readExif(ExifInterface ei) { - try { - ei.readExif(mPath); - return true; - } catch (NullPointerException e) { - Log.w("BitmapRegionTileSource", "reading exif failed", e); - return false; - } catch (IOException e) { - Log.w("BitmapRegionTileSource", "getting decoder failed", e); - return false; - } - } - } - - public static class UriBitmapSource extends BitmapSource { - private Context mContext; - private Uri mUri; - public UriBitmapSource(Context context, Uri uri) { - mContext = context; - mUri = uri; - } - private InputStream regenerateInputStream() throws FileNotFoundException { - InputStream is = mContext.getContentResolver().openInputStream(mUri); - return new BufferedInputStream(is); - } - @Override - public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() { - try { - InputStream is = regenerateInputStream(); - SimpleBitmapRegionDecoder regionDecoder = - SimpleBitmapRegionDecoderWrapper.newInstance(is, false); - Utils.closeSilently(is); - if (regionDecoder == null) { - is = regenerateInputStream(); - regionDecoder = DumbBitmapRegionDecoder.newInstance(is); - Utils.closeSilently(is); - } - return regionDecoder; - } catch (FileNotFoundException e) { - Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e); - return null; - } - } - @Override - public Bitmap loadPreviewBitmap(BitmapFactory.Options options) { - try { - InputStream is = regenerateInputStream(); - Bitmap b = BitmapFactory.decodeStream(is, null, options); - Utils.closeSilently(is); - return b; - } catch (FileNotFoundException | OutOfMemoryError e) { - Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e); - return null; - } - } - @Override - public boolean readExif(ExifInterface ei) { - InputStream is = null; - try { - is = regenerateInputStream(); - ei.readExif(is); - Utils.closeSilently(is); - return true; - } catch (FileNotFoundException e) { - Log.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 static class ResourceBitmapSource extends BitmapSource { - private Resources mRes; - private int mResId; - public ResourceBitmapSource(Resources res, int resId) { - mRes = res; - mResId = resId; - } - private InputStream regenerateInputStream() { - InputStream is = mRes.openRawResource(mResId); - return new BufferedInputStream(is); - } - @Override - public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() { - InputStream is = regenerateInputStream(); - SimpleBitmapRegionDecoder regionDecoder = - SimpleBitmapRegionDecoderWrapper.newInstance(is, false); - Utils.closeSilently(is); - if (regionDecoder == null) { - is = regenerateInputStream(); - regionDecoder = DumbBitmapRegionDecoder.newInstance(is); - Utils.closeSilently(is); - } - return regionDecoder; - } - @Override - public Bitmap loadPreviewBitmap(BitmapFactory.Options options) { - return BitmapFactory.decodeResource(mRes, mResId, options); - } - @Override - public boolean readExif(ExifInterface ei) { - try { - InputStream is = regenerateInputStream(); - ei.readExif(is); - Utils.closeSilently(is); - return true; - } catch (IOException e) { - Log.e("BitmapRegionTileSource", "Error reading resource", e); - return false; - } - } - } - - SimpleBitmapRegionDecoder mDecoder; - int mWidth; - int mHeight; - int mTileSize; - private BasicTexture mPreview; - private final int mRotation; - - // For use only by getTile - private Rect mWantRegion = new Rect(); - private BitmapFactory.Options mOptions; - - public BitmapRegionTileSource(Context context, BitmapSource source, byte[] tempStorage) { - mTileSize = TiledImageRenderer.suggestedTileSize(context); - mRotation = source.getRotation(); - mDecoder = source.getBitmapRegionDecoder(); - if (mDecoder != null) { - mWidth = mDecoder.getWidth(); - mHeight = mDecoder.getHeight(); - mOptions = new BitmapFactory.Options(); - mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888; - mOptions.inPreferQualityOverSpeed = true; - mOptions.inTempStorage = tempStorage; - - Bitmap preview = source.getPreviewBitmap(); - if (preview != null && - preview.getWidth() <= GL_SIZE_LIMIT && preview.getHeight() <= GL_SIZE_LIMIT) { - mPreview = new BitmapTexture(preview); - } else { - Log.w(TAG, String.format( - "Failed to create preview of apropriate size! " - + " in: %dx%d, out: %dx%d", - mWidth, mHeight, - preview == null ? -1 : preview.getWidth(), - preview == null ? -1 : preview.getHeight())); - } - } - } - - public Bitmap getBitmap() { - return mPreview instanceof BitmapTexture ? ((BitmapTexture) mPreview).getBitmap() : null; - } - - @Override - public int getTileSize() { - return mTileSize; - } - - @Override - public int getImageWidth() { - return mWidth; - } - - @Override - public int getImageHeight() { - return mHeight; - } - - @Override - public BasicTexture getPreview() { - return mPreview; - } - - @Override - public int getRotation() { - return mRotation; - } - - @Override - public Bitmap getTile(int level, int x, int y, Bitmap bitmap) { - int tileSize = getTileSize(); - int t = tileSize << level; - mWantRegion.set(x, y, x + t, y + t); - - if (bitmap == null) { - bitmap = Bitmap.createBitmap(tileSize, tileSize, Bitmap.Config.ARGB_8888); - } - - mOptions.inSampleSize = (1 << level); - mOptions.inBitmap = bitmap; - - try { - bitmap = mDecoder.decodeRegion(mWantRegion, mOptions); - } finally { - if (mOptions.inBitmap != bitmap && mOptions.inBitmap != null) { - mOptions.inBitmap = null; - } - } - - if (bitmap == null) { - Log.w("BitmapRegionTileSource", "fail in decoding region"); - } - return bitmap; - } -} diff --git a/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java b/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java deleted file mode 100644 index e57ce70b9..000000000 --- a/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java +++ /dev/null @@ -1,826 +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.photos.views; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Rect; -import android.graphics.RectF; -import android.support.v4.util.Pools.Pool; -import android.support.v4.util.Pools.SynchronizedPool; -import android.util.DisplayMetrics; -import android.util.Log; -import android.util.LongSparseArray; -import android.view.View; -import android.view.WindowManager; - -import com.android.gallery3d.common.Utils; -import com.android.gallery3d.glrenderer.BasicTexture; -import com.android.gallery3d.glrenderer.GLCanvas; -import com.android.gallery3d.glrenderer.UploadedTexture; -import com.android.launcher3.util.Thunk; - -/** - * Handles laying out, decoding, and drawing of tiles in GL - */ -public class TiledImageRenderer { - public static final int SIZE_UNKNOWN = -1; - - private static final String TAG = "TiledImageRenderer"; - private static final int UPLOAD_LIMIT = 1; - - /* - * This is the tile state in the CPU side. - * Life of a Tile: - * ACTIVATED (initial state) - * --> IN_QUEUE - by queueForDecode() - * --> RECYCLED - by recycleTile() - * IN_QUEUE --> DECODING - by decodeTile() - * --> RECYCLED - by recycleTile) - * DECODING --> RECYCLING - by recycleTile() - * --> DECODED - by decodeTile() - * --> DECODE_FAIL - by decodeTile() - * RECYCLING --> RECYCLED - by decodeTile() - * DECODED --> ACTIVATED - (after the decoded bitmap is uploaded) - * DECODED --> RECYCLED - by recycleTile() - * DECODE_FAIL -> RECYCLED - by recycleTile() - * RECYCLED --> ACTIVATED - by obtainTile() - */ - private static final int STATE_ACTIVATED = 0x01; - private static final int STATE_IN_QUEUE = 0x02; - private static final int STATE_DECODING = 0x04; - private static final int STATE_DECODED = 0x08; - private static final int STATE_DECODE_FAIL = 0x10; - private static final int STATE_RECYCLING = 0x20; - private static final int STATE_RECYCLED = 0x40; - - @Thunk static Pool<Bitmap> sTilePool = new SynchronizedPool<Bitmap>(64); - - // TILE_SIZE must be 2^N - @Thunk int mTileSize; - - @Thunk TileSource mModel; - private BasicTexture mPreview; - protected int mLevelCount; // cache the value of mScaledBitmaps.length - - // The mLevel variable indicates which level of bitmap we should use. - // Level 0 means the original full-sized bitmap, and a larger value means - // a smaller scaled bitmap (The width and height of each scaled bitmap is - // half size of the previous one). If the value is in [0, mLevelCount), we - // use the bitmap in mScaledBitmaps[mLevel] for display, otherwise the value - // is mLevelCount - @Thunk int mLevel = 0; - - private int mOffsetX; - private int mOffsetY; - - private int mUploadQuota; - private boolean mRenderComplete; - - private final RectF mSourceRect = new RectF(); - private final RectF mTargetRect = new RectF(); - - private final LongSparseArray<Tile> mActiveTiles = new LongSparseArray<Tile>(); - - // The following three queue are guarded by mQueueLock - @Thunk final Object mQueueLock = new Object(); - private final TileQueue mRecycledQueue = new TileQueue(); - private final TileQueue mUploadQueue = new TileQueue(); - @Thunk final TileQueue mDecodeQueue = new TileQueue(); - - // The width and height of the full-sized bitmap - protected int mImageWidth = SIZE_UNKNOWN; - protected int mImageHeight = SIZE_UNKNOWN; - - protected int mCenterX; - protected int mCenterY; - protected float mScale; - protected int mRotation; - - private boolean mLayoutTiles; - - // Temp variables to avoid memory allocation - private final Rect mTileRange = new Rect(); - private final Rect mActiveRange[] = {new Rect(), new Rect()}; - - private TileDecoder mTileDecoder; - private boolean mBackgroundTileUploaded; - - private int mViewWidth, mViewHeight; - private View mParent; - - /** - * Interface for providing tiles to a {@link TiledImageRenderer} - */ - public static interface TileSource { - - /** - * If the source does not care about the tile size, it should use - * {@link TiledImageRenderer#suggestedTileSize(Context)} - */ - public int getTileSize(); - public int getImageWidth(); - public int getImageHeight(); - public int getRotation(); - - /** - * Return a Preview image if available. This will be used as the base layer - * if higher res tiles are not yet available - */ - public BasicTexture getPreview(); - - /** - * The tile returned by this method can be specified this way: Assuming - * the image size is (width, height), first take the intersection of (0, - * 0) - (width, height) and (x, y) - (x + tileSize, y + tileSize). If - * in extending the region, we found some part of the region is outside - * the image, those pixels are filled with black. - * - * If level > 0, it does the same operation on a down-scaled version of - * the original image (down-scaled by a factor of 2^level), but (x, y) - * still refers to the coordinate on the original image. - * - * The method would be called by the decoder thread. - */ - public Bitmap getTile(int level, int x, int y, Bitmap reuse); - } - - public static int suggestedTileSize(Context context) { - return isHighResolution(context) ? 512 : 256; - } - - private static boolean isHighResolution(Context context) { - DisplayMetrics metrics = new DisplayMetrics(); - WindowManager wm = (WindowManager) - context.getSystemService(Context.WINDOW_SERVICE); - wm.getDefaultDisplay().getMetrics(metrics); - return metrics.heightPixels > 2048 || metrics.widthPixels > 2048; - } - - public TiledImageRenderer(View parent) { - mParent = parent; - mTileDecoder = new TileDecoder(); - mTileDecoder.start(); - } - - public int getViewWidth() { - return mViewWidth; - } - - public int getViewHeight() { - return mViewHeight; - } - - private void invalidate() { - mParent.postInvalidate(); - } - - public void setModel(TileSource model, int rotation) { - if (mModel != model) { - mModel = model; - notifyModelInvalidated(); - } - if (mRotation != rotation) { - mRotation = rotation; - mLayoutTiles = true; - } - } - - private void calculateLevelCount() { - if (mPreview != null) { - mLevelCount = Math.max(0, Utils.ceilLog2( - mImageWidth / (float) mPreview.getWidth())); - } else { - int levels = 1; - int maxDim = Math.max(mImageWidth, mImageHeight); - int t = mTileSize; - while (t < maxDim) { - t <<= 1; - levels++; - } - mLevelCount = levels; - } - } - - public void notifyModelInvalidated() { - invalidateTiles(); - if (mModel == null) { - mImageWidth = 0; - mImageHeight = 0; - mLevelCount = 0; - mPreview = null; - } else { - mImageWidth = mModel.getImageWidth(); - mImageHeight = mModel.getImageHeight(); - mPreview = mModel.getPreview(); - mTileSize = mModel.getTileSize(); - calculateLevelCount(); - } - mLayoutTiles = true; - } - - public void setViewSize(int width, int height) { - mViewWidth = width; - mViewHeight = height; - } - - public void setPosition(int centerX, int centerY, float scale) { - if (mCenterX == centerX && mCenterY == centerY - && mScale == scale) { - return; - } - mCenterX = centerX; - mCenterY = centerY; - mScale = scale; - mLayoutTiles = true; - } - - // Prepare the tiles we want to use for display. - // - // 1. Decide the tile level we want to use for display. - // 2. Decide the tile levels we want to keep as texture (in addition to - // the one we use for display). - // 3. Recycle unused tiles. - // 4. Activate the tiles we want. - private void layoutTiles() { - if (mViewWidth == 0 || mViewHeight == 0 || !mLayoutTiles) { - return; - } - mLayoutTiles = false; - - // The tile levels we want to keep as texture is in the range - // [fromLevel, endLevel). - int fromLevel; - int endLevel; - - // We want to use a texture larger than or equal to the display size. - mLevel = Utils.clamp(Utils.floorLog2(1f / mScale), 0, mLevelCount); - - // We want to keep one more tile level as texture in addition to what - // we use for display. So it can be faster when the scale moves to the - // next level. We choose the level closest to the current scale. - if (mLevel != mLevelCount) { - Rect range = mTileRange; - getRange(range, mCenterX, mCenterY, mLevel, mScale, mRotation); - mOffsetX = Math.round(mViewWidth / 2f + (range.left - mCenterX) * mScale); - mOffsetY = Math.round(mViewHeight / 2f + (range.top - mCenterY) * mScale); - fromLevel = mScale * (1 << mLevel) > 0.75f ? mLevel - 1 : mLevel; - } else { - // Activate the tiles of the smallest two levels. - fromLevel = mLevel - 2; - mOffsetX = Math.round(mViewWidth / 2f - mCenterX * mScale); - mOffsetY = Math.round(mViewHeight / 2f - mCenterY * mScale); - } - - fromLevel = Math.max(0, Math.min(fromLevel, mLevelCount - 2)); - endLevel = Math.min(fromLevel + 2, mLevelCount); - - Rect range[] = mActiveRange; - for (int i = fromLevel; i < endLevel; ++i) { - getRange(range[i - fromLevel], mCenterX, mCenterY, i, mRotation); - } - - // If rotation is transient, don't update the tile. - if (mRotation % 90 != 0) { - return; - } - - synchronized (mQueueLock) { - mDecodeQueue.clean(); - mUploadQueue.clean(); - mBackgroundTileUploaded = false; - - // Recycle unused tiles: if the level of the active tile is outside the - // range [fromLevel, endLevel) or not in the visible range. - int n = mActiveTiles.size(); - for (int i = 0; i < n; i++) { - Tile tile = mActiveTiles.valueAt(i); - int level = tile.mTileLevel; - if (level < fromLevel || level >= endLevel - || !range[level - fromLevel].contains(tile.mX, tile.mY)) { - mActiveTiles.removeAt(i); - i--; - n--; - recycleTile(tile); - } - } - } - - for (int i = fromLevel; i < endLevel; ++i) { - int size = mTileSize << i; - Rect r = range[i - fromLevel]; - for (int y = r.top, bottom = r.bottom; y < bottom; y += size) { - for (int x = r.left, right = r.right; x < right; x += size) { - activateTile(x, y, i); - } - } - } - invalidate(); - } - - private void invalidateTiles() { - synchronized (mQueueLock) { - mDecodeQueue.clean(); - mUploadQueue.clean(); - - // TODO(xx): disable decoder - int n = mActiveTiles.size(); - for (int i = 0; i < n; i++) { - Tile tile = mActiveTiles.valueAt(i); - recycleTile(tile); - } - mActiveTiles.clear(); - } - } - - private void getRange(Rect out, int cX, int cY, int level, int rotation) { - getRange(out, cX, cY, level, 1f / (1 << (level + 1)), rotation); - } - - // If the bitmap is scaled by the given factor "scale", return the - // rectangle containing visible range. The left-top coordinate returned is - // aligned to the tile boundary. - // - // (cX, cY) is the point on the original bitmap which will be put in the - // center of the ImageViewer. - private void getRange(Rect out, - int cX, int cY, int level, float scale, int rotation) { - - double radians = Math.toRadians(-rotation); - double w = mViewWidth; - double h = mViewHeight; - - double cos = Math.cos(radians); - double sin = Math.sin(radians); - int width = (int) Math.ceil(Math.max( - Math.abs(cos * w - sin * h), Math.abs(cos * w + sin * h))); - int height = (int) Math.ceil(Math.max( - Math.abs(sin * w + cos * h), Math.abs(sin * w - cos * h))); - - int left = (int) Math.floor(cX - width / (2f * scale)); - int top = (int) Math.floor(cY - height / (2f * scale)); - int right = (int) Math.ceil(left + width / scale); - int bottom = (int) Math.ceil(top + height / scale); - - // align the rectangle to tile boundary - int size = mTileSize << level; - left = Math.max(0, size * (left / size)); - top = Math.max(0, size * (top / size)); - right = Math.min(mImageWidth, right); - bottom = Math.min(mImageHeight, bottom); - - out.set(left, top, right, bottom); - } - - public void freeTextures() { - mLayoutTiles = true; - - mTileDecoder.finishAndWait(); - synchronized (mQueueLock) { - mUploadQueue.clean(); - mDecodeQueue.clean(); - Tile tile = mRecycledQueue.pop(); - while (tile != null) { - tile.recycle(); - tile = mRecycledQueue.pop(); - } - } - - int n = mActiveTiles.size(); - for (int i = 0; i < n; i++) { - Tile texture = mActiveTiles.valueAt(i); - texture.recycle(); - } - mActiveTiles.clear(); - mTileRange.set(0, 0, 0, 0); - - while (sTilePool.acquire() != null) {} - } - - public boolean draw(GLCanvas canvas) { - layoutTiles(); - uploadTiles(canvas); - - mUploadQuota = UPLOAD_LIMIT; - mRenderComplete = true; - - int level = mLevel; - int rotation = mRotation; - int flags = 0; - if (rotation != 0) { - flags |= GLCanvas.SAVE_FLAG_MATRIX; - } - - if (flags != 0) { - canvas.save(flags); - if (rotation != 0) { - int centerX = mViewWidth / 2, centerY = mViewHeight / 2; - canvas.translate(centerX, centerY); - canvas.rotate(rotation, 0, 0, 1); - canvas.translate(-centerX, -centerY); - } - } - try { - if (level != mLevelCount) { - int size = (mTileSize << level); - float length = size * mScale; - Rect r = mTileRange; - - for (int ty = r.top, i = 0; ty < r.bottom; ty += size, i++) { - float y = mOffsetY + i * length; - for (int tx = r.left, j = 0; tx < r.right; tx += size, j++) { - float x = mOffsetX + j * length; - drawTile(canvas, tx, ty, level, x, y, length); - } - } - } else if (mPreview != null) { - mPreview.draw(canvas, mOffsetX, mOffsetY, - Math.round(mImageWidth * mScale), - Math.round(mImageHeight * mScale)); - } - } finally { - if (flags != 0) { - canvas.restore(); - } - } - - if (mRenderComplete) { - if (!mBackgroundTileUploaded) { - uploadBackgroundTiles(canvas); - } - } else { - invalidate(); - } - return mRenderComplete || mPreview != null; - } - - private void uploadBackgroundTiles(GLCanvas canvas) { - mBackgroundTileUploaded = true; - int n = mActiveTiles.size(); - for (int i = 0; i < n; i++) { - Tile tile = mActiveTiles.valueAt(i); - if (!tile.isContentValid()) { - queueForDecode(tile); - } - } - } - - private void queueForDecode(Tile tile) { - synchronized (mQueueLock) { - if (tile.mTileState == STATE_ACTIVATED) { - tile.mTileState = STATE_IN_QUEUE; - if (mDecodeQueue.push(tile)) { - mQueueLock.notifyAll(); - } - } - } - } - - @Thunk void decodeTile(Tile tile) { - synchronized (mQueueLock) { - if (tile.mTileState != STATE_IN_QUEUE) { - return; - } - tile.mTileState = STATE_DECODING; - } - boolean decodeComplete = tile.decode(); - synchronized (mQueueLock) { - if (tile.mTileState == STATE_RECYCLING) { - tile.mTileState = STATE_RECYCLED; - if (tile.mDecodedTile != null) { - sTilePool.release(tile.mDecodedTile); - tile.mDecodedTile = null; - } - mRecycledQueue.push(tile); - return; - } - tile.mTileState = decodeComplete ? STATE_DECODED : STATE_DECODE_FAIL; - if (!decodeComplete) { - return; - } - mUploadQueue.push(tile); - } - invalidate(); - } - - private Tile obtainTile(int x, int y, int level) { - synchronized (mQueueLock) { - Tile tile = mRecycledQueue.pop(); - if (tile != null) { - tile.mTileState = STATE_ACTIVATED; - tile.update(x, y, level); - return tile; - } - return new Tile(x, y, level); - } - } - - private void recycleTile(Tile tile) { - synchronized (mQueueLock) { - if (tile.mTileState == STATE_DECODING) { - tile.mTileState = STATE_RECYCLING; - return; - } - tile.mTileState = STATE_RECYCLED; - if (tile.mDecodedTile != null) { - sTilePool.release(tile.mDecodedTile); - tile.mDecodedTile = null; - } - mRecycledQueue.push(tile); - } - } - - private void activateTile(int x, int y, int level) { - long key = makeTileKey(x, y, level); - Tile tile = mActiveTiles.get(key); - if (tile != null) { - if (tile.mTileState == STATE_IN_QUEUE) { - tile.mTileState = STATE_ACTIVATED; - } - return; - } - tile = obtainTile(x, y, level); - mActiveTiles.put(key, tile); - } - - @Thunk Tile getTile(int x, int y, int level) { - return mActiveTiles.get(makeTileKey(x, y, level)); - } - - private static long makeTileKey(int x, int y, int level) { - long result = x; - result = (result << 16) | y; - result = (result << 16) | level; - return result; - } - - private void uploadTiles(GLCanvas canvas) { - int quota = UPLOAD_LIMIT; - Tile tile = null; - while (quota > 0) { - synchronized (mQueueLock) { - tile = mUploadQueue.pop(); - } - if (tile == null) { - break; - } - if (!tile.isContentValid()) { - if (tile.mTileState == STATE_DECODED) { - tile.updateContent(canvas); - --quota; - } else { - Log.w(TAG, "Tile in upload queue has invalid state: " + tile.mTileState); - } - } - } - if (tile != null) { - invalidate(); - } - } - - // Draw the tile to a square at canvas that locates at (x, y) and - // has a side length of length. - private void drawTile(GLCanvas canvas, - int tx, int ty, int level, float x, float y, float length) { - RectF source = mSourceRect; - RectF target = mTargetRect; - target.set(x, y, x + length, y + length); - source.set(0, 0, mTileSize, mTileSize); - - Tile tile = getTile(tx, ty, level); - if (tile != null) { - if (!tile.isContentValid()) { - if (tile.mTileState == STATE_DECODED) { - if (mUploadQuota > 0) { - --mUploadQuota; - tile.updateContent(canvas); - } else { - mRenderComplete = false; - } - } else if (tile.mTileState != STATE_DECODE_FAIL){ - mRenderComplete = false; - queueForDecode(tile); - } - } - if (drawTile(tile, canvas, source, target)) { - return; - } - } - if (mPreview != null) { - int size = mTileSize << level; - float scaleX = (float) mPreview.getWidth() / mImageWidth; - float scaleY = (float) mPreview.getHeight() / mImageHeight; - source.set(tx * scaleX, ty * scaleY, (tx + size) * scaleX, - (ty + size) * scaleY); - canvas.drawTexture(mPreview, source, target); - } - } - - private boolean drawTile( - Tile tile, GLCanvas canvas, RectF source, RectF target) { - while (true) { - if (tile.isContentValid()) { - canvas.drawTexture(tile, source, target); - return true; - } - - // Parent can be divided to four quads and tile is one of the four. - Tile parent = tile.getParentTile(); - if (parent == null) { - return false; - } - if (tile.mX == parent.mX) { - source.left /= 2f; - source.right /= 2f; - } else { - source.left = (mTileSize + source.left) / 2f; - source.right = (mTileSize + source.right) / 2f; - } - if (tile.mY == parent.mY) { - source.top /= 2f; - source.bottom /= 2f; - } else { - source.top = (mTileSize + source.top) / 2f; - source.bottom = (mTileSize + source.bottom) / 2f; - } - tile = parent; - } - } - - private class Tile extends UploadedTexture { - public int mX; - public int mY; - public int mTileLevel; - public Tile mNext; - public Bitmap mDecodedTile; - public volatile int mTileState = STATE_ACTIVATED; - - public Tile(int x, int y, int level) { - mX = x; - mY = y; - mTileLevel = level; - } - - @Override - protected void onFreeBitmap(Bitmap bitmap) { - sTilePool.release(bitmap); - } - - boolean decode() { - // Get a tile from the original image. The tile is down-scaled - // by (1 << mTilelevel) from a region in the original image. - try { - Bitmap reuse = sTilePool.acquire(); - if (reuse != null && reuse.getWidth() != mTileSize) { - reuse = null; - } - mDecodedTile = mModel.getTile(mTileLevel, mX, mY, reuse); - } catch (Throwable t) { - Log.w(TAG, "fail to decode tile", t); - } - return mDecodedTile != null; - } - - @Override - protected Bitmap onGetBitmap() { - Utils.assertTrue(mTileState == STATE_DECODED); - - // We need to override the width and height, so that we won't - // draw beyond the boundaries. - int rightEdge = ((mImageWidth - mX) >> mTileLevel); - int bottomEdge = ((mImageHeight - mY) >> mTileLevel); - setSize(Math.min(mTileSize, rightEdge), Math.min(mTileSize, bottomEdge)); - - Bitmap bitmap = mDecodedTile; - mDecodedTile = null; - mTileState = STATE_ACTIVATED; - return bitmap; - } - - // We override getTextureWidth() and getTextureHeight() here, so the - // texture can be re-used for different tiles regardless of the actual - // size of the tile (which may be small because it is a tile at the - // boundary). - @Override - public int getTextureWidth() { - return mTileSize; - } - - @Override - public int getTextureHeight() { - return mTileSize; - } - - public void update(int x, int y, int level) { - mX = x; - mY = y; - mTileLevel = level; - invalidateContent(); - } - - public Tile getParentTile() { - if (mTileLevel + 1 == mLevelCount) { - return null; - } - int size = mTileSize << (mTileLevel + 1); - int x = size * (mX / size); - int y = size * (mY / size); - return getTile(x, y, mTileLevel + 1); - } - - @Override - public String toString() { - return String.format("tile(%s, %s, %s / %s)", - mX / mTileSize, mY / mTileSize, mLevel, mLevelCount); - } - } - - @Thunk static class TileQueue { - private Tile mHead; - - public Tile pop() { - Tile tile = mHead; - if (tile != null) { - mHead = tile.mNext; - } - return tile; - } - - public boolean push(Tile tile) { - if (contains(tile)) { - Log.w(TAG, "Attempting to add a tile already in the queue!"); - return false; - } - boolean wasEmpty = mHead == null; - tile.mNext = mHead; - mHead = tile; - return wasEmpty; - } - - private boolean contains(Tile tile) { - Tile other = mHead; - while (other != null) { - if (other == tile) { - return true; - } - other = other.mNext; - } - return false; - } - - public void clean() { - mHead = null; - } - } - - @Thunk class TileDecoder extends Thread { - - public void finishAndWait() { - interrupt(); - try { - join(); - } catch (InterruptedException e) { - Log.w(TAG, "Interrupted while waiting for TileDecoder thread to finish!"); - } - } - - private Tile waitForTile() throws InterruptedException { - synchronized (mQueueLock) { - while (true) { - Tile tile = mDecodeQueue.pop(); - if (tile != null) { - return tile; - } - mQueueLock.wait(); - } - } - } - - @Override - public void run() { - try { - while (!isInterrupted()) { - Tile tile = waitForTile(); - decodeTile(tile); - } - } catch (InterruptedException ex) { - // We were finished - } - } - - } -} diff --git a/WallpaperPicker/src/com/android/photos/views/TiledImageView.java b/WallpaperPicker/src/com/android/photos/views/TiledImageView.java deleted file mode 100644 index 7e3e1a936..000000000 --- a/WallpaperPicker/src/com/android/photos/views/TiledImageView.java +++ /dev/null @@ -1,312 +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.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; -import android.util.AttributeSet; -import android.view.Choreographer; -import android.view.Choreographer.FrameCallback; -import android.widget.FrameLayout; - -import com.android.gallery3d.glrenderer.BasicTexture; -import com.android.gallery3d.glrenderer.GLES20Canvas; -import com.android.launcher3.util.Thunk; -import com.android.photos.views.TiledImageRenderer.TileSource; - -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; - -/** - * Shows an image using {@link TiledImageRenderer} using either {@link GLSurfaceView}. - */ -public class TiledImageView extends FrameLayout { - - @Thunk GLSurfaceView mGLSurfaceView; - @Thunk boolean mInvalPending = false; - private FrameCallback mFrameCallback; - - protected static class ImageRendererWrapper { - // Guarded by locks - public float scale; - public int centerX, centerY; - public int rotation; - public TileSource source; - Runnable isReadyCallback; - - // GL thread only - TiledImageRenderer image; - } - - private float[] mValues = new float[9]; - - // ------------------------- - // Guarded by mLock - // ------------------------- - protected Object mLock = new Object(); - protected ImageRendererWrapper mRenderer; - - public TiledImageView(Context context) { - this(context, null); - } - - public TiledImageView(Context context, AttributeSet attrs) { - super(context, attrs); - mRenderer = new ImageRendererWrapper(); - mRenderer.image = new TiledImageRenderer(this); - mGLSurfaceView = new GLSurfaceView(context); - mGLSurfaceView.setEGLContextClientVersion(2); - mGLSurfaceView.setRenderer(new TileRenderer()); - mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); - addView(mGLSurfaceView, new LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - //setTileSource(new ColoredTiles()); - } - - @Override - public void setVisibility(int visibility) { - super.setVisibility(visibility); - // need to update inner view's visibility because it seems like we're causing it to draw - // from {@link #dispatchDraw} or {@link #invalidate} even if we are invisible. - mGLSurfaceView.setVisibility(visibility); - } - - public void destroy() { - mGLSurfaceView.queueEvent(mFreeTextures); - } - - private Runnable mFreeTextures = new Runnable() { - - @Override - public void run() { - mRenderer.image.freeTextures(); - } - }; - - public void onPause() { - mGLSurfaceView.onPause(); - } - - public void onResume() { - mGLSurfaceView.onResume(); - } - - public void setTileSource(TileSource source, Runnable isReadyCallback) { - synchronized (mLock) { - mRenderer.source = source; - mRenderer.isReadyCallback = isReadyCallback; - mRenderer.centerX = source != null ? source.getImageWidth() / 2 : 0; - mRenderer.centerY = source != null ? source.getImageHeight() / 2 : 0; - mRenderer.rotation = source != null ? source.getRotation() : 0; - mRenderer.scale = 0; - updateScaleIfNecessaryLocked(mRenderer); - } - invalidate(); - } - - public TileSource getTileSource() { - return mRenderer.source; - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, - int bottom) { - super.onLayout(changed, left, top, right, bottom); - synchronized (mLock) { - updateScaleIfNecessaryLocked(mRenderer); - } - } - - private void updateScaleIfNecessaryLocked(ImageRendererWrapper renderer) { - if (renderer == null || renderer.source == null - || renderer.scale > 0 || getWidth() == 0) { - return; - } - renderer.scale = Math.min( - (float) getWidth() / (float) renderer.source.getImageWidth(), - (float) getHeight() / (float) renderer.source.getImageHeight()); - } - - @Override - public void invalidate() { - invalOnVsync(); - } - - private void invalOnVsync() { - if (!mInvalPending) { - mInvalPending = true; - if (mFrameCallback == null) { - mFrameCallback = new FrameCallback() { - @Override - public void doFrame(long frameTimeNanos) { - mInvalPending = false; - mGLSurfaceView.requestRender(); - } - }; - } - Choreographer.getInstance().postFrameCallback(mFrameCallback); - } - } - - 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; - - @Override - public void onSurfaceCreated(GL10 gl, EGLConfig config) { - mCanvas = new GLES20Canvas(); - BasicTexture.invalidateAllTextures(); - mRenderer.image.setModel(mRenderer.source, mRenderer.rotation); - } - - @Override - public void onSurfaceChanged(GL10 gl, int width, int height) { - mCanvas.setSize(width, height); - mRenderer.image.setViewSize(width, height); - } - - @Override - public void onDrawFrame(GL10 gl) { - mCanvas.clearBuffer(); - Runnable readyCallback; - synchronized (mLock) { - readyCallback = mRenderer.isReadyCallback; - mRenderer.image.setModel(mRenderer.source, mRenderer.rotation); - mRenderer.image.setPosition(mRenderer.centerX, mRenderer.centerY, - mRenderer.scale); - } - boolean complete = mRenderer.image.draw(mCanvas); - if (complete && readyCallback != null) { - synchronized (mLock) { - // Make sure we don't trample on a newly set callback/source - // if it changed while we were rendering - if (mRenderer.isReadyCallback == readyCallback) { - mRenderer.isReadyCallback = null; - } - } - if (readyCallback != null) { - post(readyCallback); - } - } - } - - } - - @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/build.gradle b/build.gradle index 44f5b7c52..ca49c1324 100644 --- a/build.gradle +++ b/build.gradle @@ -32,8 +32,8 @@ android { } sourceSets { main { - res.srcDirs = ['res', 'WallpaperPicker/res'] - java.srcDirs = ['src', 'WallpaperPicker/src'] + res.srcDirs = ['res'] + java.srcDirs = ['src'] manifest.srcFile 'AndroidManifest.xml' proto.srcDirs 'protos/' } @@ -54,6 +54,7 @@ dependencies { compile 'com.android.support:support-v4:23.0.1' compile 'com.android.support:recyclerview-v7:23.0.1' compile 'com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-2' + compile project(":WallpaperPicker-Lib") testCompile 'junit:junit:4.12' androidTestCompile 'com.android.support.test:runner:+' diff --git a/proguard.flags b/proguard.flags index 772580020..19d2f0c50 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); @@ -70,3 +70,10 @@ public float getBackgroundAlpha(); public void setBackgroundAlpha(float); } + +# Proguard will strip new callbacks in LauncherApps.Callback from +# WrappedCallback if compiled against an older SDK. Don't let this happen. +-keep class com.android.launcher3.compat.** { + *; +} + diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto new file mode 100644 index 000000000..a7b6429e4 --- /dev/null +++ b/protos/launcher_log.proto @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2016 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. + */ +syntax = "proto2"; + +option java_package = "com.android.launcher3.userevent.nano"; +option java_outer_classname = "LauncherLogProto"; + +package userevent; + +message Target { + enum Type { + NONE = 0; + ITEM = 1; + CONTROL = 2; + CONTAINER = 3; + } + + optional Type type = 1; + + // For container type and item type + // Used mainly for ContainerType.FOLDER, ItemType.* + optional Target parent = 2; + optional int32 page_index = 3; + optional int32 rank = 4; + optional int32 grid_x = 5; + optional int32 grid_y = 6; + + // For container types only + optional ContainerType container_type = 7; + optional int32 cardinality = 8; + + // For control types only + optional ControlType control_type = 9; + + // For item types only + optional ItemType item_type = 10; + optional int32 package_name_hash = 11; + optional int32 component_hash = 12; // Used for ItemType.WIDGET + optional int32 intent_hash = 13; // Used for ItemType.SHORTCUT + optional int32 span_x = 14 [default = 1];// Used for ItemType.WIDGET + optional int32 span_y = 15 [default = 1];// Used for ItemType.WIDGET +} + +enum ItemType { + APP_ICON = 0; + SHORTCUT = 1; + WIDGET = 2; + FOLDER_ICON = 3; +} + +enum ContainerType { + WORKSPACE = 0; + HOTSEAT = 1; + FOLDER = 2; + ALLAPPS = 3; + WIDGETS = 4; + OVERVIEW = 5; + PREDICTION = 6; + SEARCHRESULT = 7; +} + +enum ControlType { + ALL_APPS_BUTTON = 0; + WIDGETS_BUTTON = 1; + WALLPAPER_BUTTON = 2; + SETTINGS_BUTTON = 3; + REMOVE_TARGET = 4; + UNINSTALL_TARGET = 5; + APPINFO_TARGET = 6; + RESIZE_HANDLE = 7; + FAST_SCROLL_HANDLE = 8; + // HOME, BACK, GO_TO_PLAYSTORE +} + +message Action { + enum Type { + TOUCH = 0; + AUTOMATED = 1; + // SOFT_KEYBOARD, HARD_KEYBOARD, ASSIST + } + enum Touch { + TAP = 0; + LONGPRESS = 1; + DRAGDROP = 2; + SWIPE = 3; + FLING = 4; + PINCH = 5; + } + optional Type type = 1; + optional Touch touch = 2; +} + +// +// Context free grammar of typical user interaction: +// Action (Touch) + Target +// Action (Touch) + Target + Target +// +message LauncherEvent { + + required Action action = 1; + + // List of targets that touch actions can be operated on. + optional Target src_target = 2; + optional Target dest_target = 3; + + optional int64 action_duration_millis = 4; + optional int64 elapsed_container_millis = 5; + optional int64 elapsed_session_millis = 6; +} 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/WallpaperPicker/res/menu/cab_delete_wallpapers.xml b/res/drawable/bg_celllayout.xml index 38ac5c4d6..0ce083e7a 100644 --- a/WallpaperPicker/res/menu/cab_delete_wallpapers.xml +++ b/res/drawable/bg_celllayout.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2013, The Android Open Source Project +** 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. @@ -18,10 +18,18 @@ */ --> -<menu xmlns:android="http://schemas.android.com/apk/res/android" > - <item - android:id="@+id/menu_delete" - android:title="@string/wallpaper_delete" - android:showAsAction="always" - android:icon="@android:drawable/ic_menu_delete" /> -</menu> +<transition xmlns:android="http://schemas.android.com/apk/res/android" > + + <item> + <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > + <solid android:color="#3fffffff" /> + </shape> + </item> + <item> + <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > + <solid android:color="#3fffffff" /> + <stroke android:width="1dp" android:color="#fff" /> + </shape> + </item> + +</transition>
\ No newline at end of file diff --git a/res/drawable/bg_screenpanel.xml b/res/drawable/bg_screenpanel.xml index cdb71dfa1..346fca013 100644 --- a/res/drawable/bg_screenpanel.xml +++ b/res/drawable/bg_screenpanel.xml @@ -18,6 +18,7 @@ */ --> +<!-- TODO(twickham): Remove this file and the screenpanel drawables --> <transition xmlns:android="http://schemas.android.com/apk/res/android" > <item android:drawable="@drawable/screenpanel"/> 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 1a951f1b7..aac0f83fc 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:clipChildren="false" android:clipToPadding="false" @@ -41,8 +41,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" @@ -52,6 +51,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" /> @@ -70,7 +73,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 8bf9d6427..fb015f8ec 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:clipChildren="false" android:clipToPadding="false" @@ -42,7 +42,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> @@ -66,6 +65,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" /> @@ -80,7 +83,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 2fc62c500..a45858c92 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:clipChildren="false" android:clipToPadding="false" @@ -41,7 +41,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> @@ -52,6 +51,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" /> @@ -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-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/all_apps.xml b/res/layout/all_apps.xml index a677fff98..faa88f7cf 100644 --- a/res/layout/all_apps.xml +++ b/res/layout/all_apps.xml @@ -49,7 +49,7 @@ <!-- DO NOT CHANGE THE ID --> <com.android.launcher3.allapps.AllAppsRecyclerView android:id="@+id/apps_list_view" - android:theme="@style/Theme.Light.CustomOverscroll" + android:theme="@style/CustomOverscroll.Light" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center_horizontal|top" diff --git a/res/layout/all_apps_container.xml b/res/layout/all_apps_container.xml index 626edafab..c0525ee2b 100644 --- a/res/layout/all_apps_container.xml +++ b/res/layout/all_apps_container.xml @@ -27,7 +27,7 @@ <!-- DO NOT CHANGE THE ID --> <com.android.launcher3.allapps.AllAppsRecyclerView android:id="@+id/apps_list_view" - android:theme="@style/Theme.Light.CustomOverscroll" + android:theme="@style/CustomOverscroll.Light" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center_horizontal|top" 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/folder_icon.xml b/res/layout/folder_icon.xml index 237af6890..b8d5c608b 100644 --- a/res/layout/folder_icon.xml +++ b/res/layout/folder_icon.xml @@ -14,7 +14,7 @@ limitations under the License. --> -<com.android.launcher3.FolderIcon +<com.android.launcher3.folder.FolderIcon xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" @@ -33,4 +33,4 @@ android:layout_gravity="top" android:layout_width="match_parent" android:layout_height="match_parent" /> -</com.android.launcher3.FolderIcon> +</com.android.launcher3.folder.FolderIcon> 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/user_folder.xml b/res/layout/user_folder.xml index 252ebf01e..87a42145d 100644 --- a/res/layout/user_folder.xml +++ b/res/layout/user_folder.xml @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<com.android.launcher3.Folder xmlns:android="http://schemas.android.com/apk/res/android" +<com.android.launcher3.folder.Folder 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" @@ -34,7 +34,7 @@ android:layout_width="20dp" android:layout_height="20dp" /> - <com.android.launcher3.FolderPagedView + <com.android.launcher3.folder.FolderPagedView android:id="@+id/folder_content" android:layout_width="match_parent" android:layout_height="match_parent" @@ -81,4 +81,4 @@ </LinearLayout> -</com.android.launcher3.Folder>
\ No newline at end of file +</com.android.launcher3.folder.Folder>
\ No newline at end of file diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml index 75b5c4839..d445a7ae3 100644 --- a/res/layout/user_folder_icon_normalized.xml +++ b/res/layout/user_folder_icon_normalized.xml @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<com.android.launcher3.Folder xmlns:android="http://schemas.android.com/apk/res/android" +<com.android.launcher3.folder.Folder 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" @@ -34,7 +34,7 @@ android:layout_width="20dp" android:layout_height="20dp" /> - <com.android.launcher3.FolderPagedView + <com.android.launcher3.folder.FolderPagedView android:id="@+id/folder_content" android:layout_width="match_parent" android:layout_height="match_parent" @@ -82,4 +82,4 @@ </LinearLayout> -</com.android.launcher3.Folder> +</com.android.launcher3.folder.Folder> diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml index 1d593dfd1..b7f76a66a 100644 --- a/res/layout/widgets_list_row_view.xml +++ b/res/layout/widgets_list_row_view.xml @@ -51,7 +51,7 @@ <HorizontalScrollView android:id="@+id/widgets_scroll_container" - android:theme="@style/Theme.Dark.CustomOverscroll" + android:theme="@style/CustomOverscroll.Dark" android:layout_width="wrap_content" android:layout_height="wrap_content" android:scrollbars="none"> diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml index c51ec802b..410d1be3f 100644 --- a/res/layout/widgets_view.xml +++ b/res/layout/widgets_view.xml @@ -44,7 +44,7 @@ <com.android.launcher3.widget.WidgetsRecyclerView android:id="@+id/widgets_list_view" - android:theme="@style/Theme.Dark.CustomOverscroll" + android:theme="@style/CustomOverscroll.Dark" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout> 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 6ea6f4af9..d787c4908 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 ad9c7f942..1b751aa9a 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 9d2c42b40..e5cfa319e 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 52561152f..d8e0480b4 100644 --- a/res/values-az-rAZ/strings.xml +++ b/res/values-az-rAZ/strings.xml @@ -37,9 +37,9 @@ <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> diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index 9c96cd8eb..1a326f481 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 5a81ed482..72aceef9e 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 05932a95e..86aec04b8 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 373ae8340..0943c6232 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 3654b7207..3f9f0da8c 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 a375ecd2e..18126078b 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 12a315771..c5e2df77d 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 f8873a574..17589b6d8 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 f8873a574..17589b6d8 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 f8873a574..17589b6d8 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 cc58b46d9..e501cf4d0 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 c9c68545f..a5611872a 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 053791d52..478937438 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 c85466c43..5d740e42f 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 85a379bfe..59ad6be75 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 c27310b66..151ea7889 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 bb0f1a01c..a3b5e3740 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 f189c636f..0fddd68ca 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 ad25e8498..1d10d032c 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 1cd676830..9f2107cdf 100644 --- a/res/values-gu-rIN/strings.xml +++ b/res/values-gu-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-hi/strings.xml b/res/values-hi/strings.xml index d2de54c19..f4179105c 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 427e5c8f9..4426c1808 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 b9a5b6135..1382b9933 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 950cca206..2538bd289 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 1dda24ba8..9670ea6b8 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 fd6977c3b..cee6c53d6 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 a01bc9db8..95a13b74c 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 137800e70..0cdb6967f 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 e3c3194c7..3df18bfeb 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 288f0ba86..6970f9d6a 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 b633790d2..f48c7a1aa 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 3cf259973..6a9feef4f 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 8766d7176..c033ea199 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 be5b506f3..37fef014a 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 0d7cb486b..e6ab0acf0 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 8ed9c263e..f4294c073 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 f20751a55..49c90697b 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 7e7637450..46b58c7fb 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 73a9d9539..4dbbb24c1 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 1169be2c4..748b367ab 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 46f46fada..b2ae00008 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 5d2919760..bcde7b0c3 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 5da433184..1ebfd0889 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 0264c98df..7b94b71aa 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 7b5f3b6f4..7d0f3f0f1 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 2abf0050c..501aa560f 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 a55aebf8d..8325b58fc 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 6f5acff58..00e5ae34f 100644 --- a/res/values-pa-rIN/strings.xml +++ b/res/values-pa-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-pl/strings.xml b/res/values-pl/strings.xml index ab7026553..e39d7a7bc 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 8a469ac7f..8bc5c0e05 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 d9fbdef58..b51574d7d 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 8c715f8bf..e32e524fb 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 b4ddae7e7..84b9948d3 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 1fd7df05f..405035a8c 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 66772c777..2c0938a98 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 d5e1e4f4e..72e58ce4f 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 62621705d..5d918c03f 100644 --- a/res/values-sq-rAL/strings.xml +++ b/res/values-sq-rAL/strings.xml @@ -37,9 +37,9 @@ <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> diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index f19390924..736222306 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 10f5ce42a..49e924482 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 2abe8244c..11dc79e60 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-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/styles.xml b/res/values-sw720dp/styles.xml index e8b706e23..1fdb267c7 100644 --- a/res/values-sw720dp/styles.xml +++ b/res/values-sw720dp/styles.xml @@ -19,9 +19,15 @@ <resources> - <!-- Workspace --> - <style name="SearchButton"></style> + <style name="BaseLauncherTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar"> + <item name="android:windowBackground">@android:color/transparent</item> + <item name="android:colorBackgroundCacheHint">@null</item> + <item name="android:windowShowWallpaper">true</item> + <item name="android:windowNoTitle">true</item> + <item name="android:windowActionModeOverlay">true</item> + </style> + <!-- Workspace --> <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 399028698..b440d56c2 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 997e1981d..4457b08a7 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 b90599ef1..eb67678b8 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 7da7b1378..f1cf879d1 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 07db87687..0e7637ebf 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 66f2542bb..aa92b09ec 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 b3d4c29ad..247465b92 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 23f82d8f5..0084fc459 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-sw340dp-port/styles.xml b/res/values-v19/styles.xml index 8ac3b5ea1..cfc7c0f9c 100644 --- a/res/values-sw340dp-port/styles.xml +++ b/res/values-v19/styles.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <!-- /* -* Copyright (C) 2011 The Android Open Source Project +* Copyright (C) 2016 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. @@ -16,12 +16,11 @@ * 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 name="LauncherTheme" parent="@style/BaseLauncherTheme"> + <item name="android:windowTranslucentStatus">true</item> + <item name="android:windowTranslucentNavigation">true</item> </style> -</resources> + +</resources>
\ No newline at end of file diff --git a/WallpaperPicker/res/values-sw720dp/styles.xml b/res/values-v21/styles.xml index 0058f7e38..bc8523ecc 100644 --- a/WallpaperPicker/res/values-sw720dp/styles.xml +++ b/res/values-v21/styles.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <!-- /* -* Copyright (C) 2013 The Android Open Source Project +* Copyright (C) 2016 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. @@ -16,13 +16,13 @@ * limitations under the License. */ --> - <resources> - <style name="BaseWallpaperTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar"> - <item name="android:windowBackground">@android:color/transparent</item> - <item name="android:colorBackgroundCacheHint">@null</item> - <item name="android:windowShowWallpaper">true</item> - <item name="android:windowNoTitle">true</item> - <item name="android:windowActionModeOverlay">true</item> + + <style name="LauncherTheme" parent="@style/BaseLauncherTheme"> + <item name="android:statusBarColor">#00000000</item> + <item name="android:navigationBarColor">#00000000</item> + <item name="android:colorControlActivated">@color/launcher_accent_color</item> + <item name="android:colorAccent">@color/launcher_accent_color</item> + <item name="android:colorPrimary">@color/launcher_accent_color</item> </style> -</resources> +</resources>
\ No newline at end of file diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index 1ecc92e73..0df0af371 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 93a7fa7ad..41d55ddb1 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 41679b586..6c8e869c6 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 bc3be0c7f..4cc3467cb 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 a1dec7486..c3eb3f436 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 6392123f4..06454aa5a 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/colors.xml b/res/values/colors.xml index 8a7f62743..b6792704a 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -37,6 +37,7 @@ <color name="quantum_panel_bg_color_dark">#FF374248</color> <color name="outline_color">#FFFFFFFF</color> + <color name="launcher_accent_color">#ff009688</color> <!-- Containers --> <color name="container_fastscroll_thumb_inactive_color">#009688</color> diff --git a/res/values/config.xml b/res/values/config.xml index c846b98ac..90df99787 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -17,17 +17,16 @@ <bool name="enable_backup">false</bool> <!-- DragController --> - <integer name="config_flingToDeleteMinVelocity">-1500</integer> <item type="id" name="drag_event_parity" /> <!-- AllApps & Launcher transitions --> <!-- The alpha of the AppsCustomize bg in spring loaded mode --> - <integer name="config_workspaceScrimAlpha">55</integer> + <integer name="config_workspaceScrimAlpha">30</integer> <integer name="config_allAppsTransitionTime">100</integer> <integer name="config_overviewTransitionTime">250</integer> <!-- Out of 100, the percent to shrink the workspace during spring loaded mode. --> - <integer name="config_workspaceSpringLoadShrinkPercentage">80</integer> + <integer name="config_workspaceSpringLoadShrinkPercentage">90</integer> <!-- Out of 100, the percent to shrink the workspace during overview mode. --> <integer name="config_workspaceOverviewShrinkPercentage">70</integer> @@ -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. --> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index a303daba8..fee62dcb8 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> @@ -52,9 +50,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> @@ -78,7 +73,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">60dp</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 --> @@ -103,16 +97,15 @@ <dimen name="all_apps_search_bar_bg_overflow">-6dp</dimen> <dimen name="all_apps_search_bar_divider_width">1dp</dimen> - <!-- Widget tray --> - <dimen name="widget_container_inset">8dp</dimen> +<!-- Widget tray --> <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> @@ -139,12 +132,14 @@ 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> <!-- Folders --> - <!-- The amount that the preview contents are inset from the preview background --> - <dimen name="folder_preview_padding">4dp</dimen> + <!-- The size of the padding on the preview background drawable --> + <dimen name="folder_preview_padding">6dp</dimen> <!-- Sizes for managed profile badges --> <dimen name="profile_badge_size">24dp</dimen> diff --git a/res/values/strings.xml b/res/values/strings.xml index 2838a22f0..649969491 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -47,7 +47,7 @@ <!-- Widgets --> <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] --> - <string name="long_press_widget_to_add">Touch & hold to pick up a widget.</string> + <string name="long_press_widget_to_add">Tap & hold to pick up a widget.</string> <!-- Accessibility spoken hint message in widget picker, which allows user to add a widget. Custom action is the label for additional accessibility actions available in this mode [CHAR_LIMIT=100] --> <string name="long_accessible_way_to_add">Double-tap & hold to pick up a widget or use custom actions.</string> <!-- The format string for the dimensions of a widget in the drawer --> @@ -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 /> @@ -145,7 +145,7 @@ <!-- The title text for workspace longpress action [CHAR_LIMIT=40] --> <string name="workspace_cling_longpress_title">Wallpapers, widgets, & settings</string> <!-- The description of how to use the workspace [CHAR_LIMIT=70] --> - <string name="workspace_cling_longpress_description">Touch & hold background to customize</string> + <string name="workspace_cling_longpress_description">Tap & hold background to customize</string> <!-- The description of the button to dismiss the cling [CHAR_LIMIT=30] --> <string name="workspace_cling_longpress_dismiss">GOT IT</string> @@ -153,9 +153,9 @@ <!-- The format string for when a folder is opened, speaks the dimensions --> <string name="folder_opened">Folder opened, <xliff:g id="width" example="5">%1$d</xliff:g> by <xliff:g id="height" example="3">%2$d</xliff:g></string> <!-- Instruction that clicking outside will close folder --> - <string name="folder_tap_to_close">Touch to close folder</string> + <string name="folder_tap_to_close">Tap to close folder</string> <!-- Instruction that clicking outside will commit folder rename --> - <string name="folder_tap_to_rename">Touch to save rename</string> + <string name="folder_tap_to_rename">Tap to save rename</string> <!-- Indication that folder closed --> <string name="folder_closed">Folder closed</string> <!-- Folder renamed format --> diff --git a/res/values/styles.xml b/res/values/styles.xml index 2b13159d7..92ec874ba 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -18,14 +18,28 @@ --> <resources> - <style name="Theme.Light.CustomOverscroll" parent="@android:style/Theme.DeviceDefault"> + <!-- Launcher theme --> + <style name="BaseLauncherTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar"> + <item name="android:windowBackground">@android:color/transparent</item> + <item name="android:colorBackgroundCacheHint">@null</item> + <item name="android:windowShowWallpaper">true</item> + <item name="android:windowNoTitle">true</item> + </style> + + <style name="LauncherTheme" parent="@style/BaseLauncherTheme"></style> + + <style name="Theme" parent="@style/LauncherTheme"></style> + + <!-- Overscroll effect --> + <style name="CustomOverscroll.Light" parent="@android:style/Theme.DeviceDefault"> <item name="android:colorEdgeEffect">@color/folder_edge_effect_color</item> </style> - <style name="Theme.Dark.CustomOverscroll" parent="@android:style/Theme.DeviceDefault"> + <style name="CustomOverscroll.Dark" parent="@android:style/Theme.DeviceDefault"> <item name="android:colorEdgeEffect">@color/workspace_edge_effect_color</item> </style> + <!-- Different icons --> <style name="Icon"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">match_parent</item> @@ -56,8 +70,7 @@ <item name="customShadows">false</item> </style> - <style name="SearchButton"></style> - + <!-- Drop targets --> <style name="DropTargetButtonContainer"> <item name="android:layout_width">0dp</item> <item name="android:layout_height">match_parent</item> @@ -83,6 +96,7 @@ <style name="DropTargetButton" parent="DropTargetButtonBase" /> + <!-- Virtual preloaders --> <style name="PreloadIcon"> <item name="ringBackground">@drawable/virtual_preload</item> <item name="indicatorSize">4dp</item> diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 000000000..2cbf914a4 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +include ':WallpaperPicker-Lib' +project(':WallpaperPicker-Lib').projectDir = new File(rootDir, '../WallpaperPicker')
\ No newline at end of file diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java index 3b25dca34..f76c18512 100644 --- a/src/com/android/launcher3/AllAppsList.java +++ b/src/com/android/launcher3/AllAppsList.java @@ -118,6 +118,25 @@ class AllAppsList { } } + /** + * Suspend the apps for the given apk identified by packageName. + */ + public void suspendPackage(String packageName, UserHandleCompat user, boolean suspend) { + final List<AppInfo> data = this.data; + for (int i = data.size() - 1; i >= 0; i--) { + AppInfo info = data.get(i); + final ComponentName component = info.intent.getComponent(); + if (info.user.equals(user) && packageName.equals(component.getPackageName())) { + if (suspend) { + info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED; + } else { + info.isDisabled &= ~ShortcutInfo.FLAG_DISABLED_SUSPENDED; + } + modified.add(info); + } + } + } + public void updateIconsAndLabels(HashSet<String> packages, UserHandleCompat user, ArrayList<AppInfo> outUpdates) { for (AppInfo info : data) { 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..b5b6897cc 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. @@ -58,6 +56,11 @@ public class AppInfo extends ItemInfo { int flags = 0; + /** + * {@see ShortcutInfo#isDisabled} + */ + int isDisabled = ShortcutInfo.DEFAULT; + AppInfo() { itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT; } @@ -75,10 +78,22 @@ public class AppInfo extends ItemInfo { */ public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandleCompat user, IconCache iconCache) { + this(context, info, user, iconCache, + UserManagerCompat.getInstance(context).isQuietModeEnabled(user)); + } + + public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandleCompat user, + IconCache iconCache, boolean quietModeEnabled) { this.componentName = info.getComponentName(); this.container = ItemInfo.NO_ID; - flags = initFlags(info); + if ((info.getApplicationInfo().flags & LauncherActivityInfoCompat.FLAG_SUSPENDED) != 0) { + isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED; + } + if (quietModeEnabled) { + isDisabled |= ShortcutInfo.FLAG_DISABLED_QUIET_USER; + } + iconCache.getTitleAndIcon(this, info, true /* useLowResIcon */); intent = makeLaunchIntent(context, info, user); this.user = user; @@ -103,6 +118,7 @@ public class AppInfo extends ItemInfo { title = Utilities.trim(info.title); intent = new Intent(info.intent); flags = info.flags; + isDisabled = info.isDisabled; iconBitmap = info.iconBitmap; } @@ -111,8 +127,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 +136,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()); } } @@ -143,4 +158,9 @@ public class AppInfo extends ItemInfo { .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) .putExtra(EXTRA_PROFILE, serialNumber); } + + @Override + public boolean isDisabled() { + return isDisabled != 0; + } } diff --git a/src/com/android/launcher3/AppInfoDropTargetBar.java b/src/com/android/launcher3/AppInfoDropTargetBar.java new file mode 100644 index 000000000..e06f94100 --- /dev/null +++ b/src/com/android/launcher3/AppInfoDropTargetBar.java @@ -0,0 +1,80 @@ +/* + * 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) { + resetAnimation(duration); + if (duration > 0) { + animateAlpha(mDropTargetBar, alpha, DEFAULT_INTERPOLATOR); + mCurrentAnimation.start(); + } else { + mDropTargetBar.setAlpha(alpha); + AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled); + } + } + + @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 6b7ff886c..0ba94995b 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.view.KeyEvent; @@ -24,7 +27,10 @@ public class AppWidgetResizeFrame extends FrameLayout implements View.OnKeyListe 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; @@ -358,28 +364,27 @@ public class AppWidgetResizeFrame extends FrameLayout implements View.OnKeyListe } 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; } @@ -458,10 +463,10 @@ public class AppWidgetResizeFrame extends FrameLayout implements View.OnKeyListe 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 7de2774dc..bd4199264 100644 --- a/src/com/android/launcher3/BaseContainerView.java +++ b/src/com/android/launcher3/BaseContainerView.java @@ -26,6 +26,8 @@ import android.util.Log; import android.view.View; import android.widget.FrameLayout; +import com.android.launcher3.config.ProviderConfig; + /** * A base container view, which supports resizing. */ @@ -84,7 +86,7 @@ public abstract class BaseContainerView extends FrameLayout implements Insettabl * 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..9b38623c2 --- /dev/null +++ b/src/com/android/launcher3/BaseDropTargetBar.java @@ -0,0 +1,146 @@ +/* + * 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.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; +import android.content.Context; +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; + +/** + * 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 static final TimeInterpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator(); + + protected View mDropTargetBar; + protected boolean mAccessibilityEnabled = false; + + protected AnimatorSet mCurrentAnimation; + 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); + } + + /** + * Convenience method to animate the alpha of a view. + */ + protected void animateAlpha(View v, float alpha, TimeInterpolator interpolator) { + if (Float.compare(v.getAlpha(), alpha) != 0) { + ObjectAnimator anim = ObjectAnimator.ofFloat(v, View.ALPHA, alpha); + anim.setInterpolator(interpolator); + anim.addListener(new ViewVisiblilyUpdateHandler(v)); + mCurrentAnimation.play(anim); + } + } + + protected void resetAnimation(int newAnimationDuration) { + // Update the accessibility state + AccessibilityManager am = (AccessibilityManager) + getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); + mAccessibilityEnabled = am.isEnabled(); + + // Cancel any existing animation + if (mCurrentAnimation != null) { + mCurrentAnimation.cancel(); + mCurrentAnimation = null; + } + + if (newAnimationDuration > 0) { + mCurrentAnimation = new AnimatorSet(); + mCurrentAnimation.setDuration(newAnimationDuration); + } + } + + /* + * 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); + + private class ViewVisiblilyUpdateHandler extends AnimatorListenerAdapter { + private final View mView; + + ViewVisiblilyUpdateHandler(View v) { + mView = v; + } + + @Override + public void onAnimationStart(Animator animation) { + // Ensure that the view is visible for the animation + mView.setVisibility(View.VISIBLE); + } + + @Override + public void onAnimationEnd(Animator animation){ + AlphaUpdateListener.updateVisibility(mView, mAccessibilityEnabled); + } + + } +} diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index f6c9e3c77..dddd826c4 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -25,7 +25,6 @@ import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Region; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Build; @@ -36,10 +35,12 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; +import android.view.ViewDebug; import android.view.ViewParent; import android.widget.TextView; import com.android.launcher3.IconCache.IconLoadRequest; +import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.model.PackageItemInfo; import java.text.NumberFormat; @@ -80,10 +81,14 @@ public class BubbleTextView extends TextView private final boolean mCustomShadowsEnabled; private final boolean mLayoutHorizontal; private final int mIconSize; + @ViewDebug.ExportedProperty(category = "launcher") private int mTextColor; + @ViewDebug.ExportedProperty(category = "launcher") private boolean mStayPressed; + @ViewDebug.ExportedProperty(category = "launcher") private boolean mIgnorePressedStateChange; + @ViewDebug.ExportedProperty(category = "launcher") private boolean mDisableRelayout = false; private IconLoadRequest mIconLoadRequest; @@ -131,7 +136,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) { @@ -150,7 +155,7 @@ public class BubbleTextView extends TextView Bitmap b = info.getIcon(iconCache); FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(b); - if (info.isDisabled != 0) { + if (info.isDisabled()) { iconDrawable.setState(FastBitmapDrawable.State.DISABLED); } setIcon(iconDrawable, mIconSize); @@ -166,7 +171,11 @@ public class BubbleTextView extends TextView } public void applyFromApplicationInfo(AppInfo info) { - setIcon(mLauncher.createIconDrawable(info.iconBitmap), mIconSize); + FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(info.iconBitmap); + if (info.isDisabled()) { + iconDrawable.setState(FastBitmapDrawable.State.DISABLED); + } + setIcon(iconDrawable, mIconSize); setText(info.title); if (info.contentDescription != null) { setContentDescription(info.contentDescription); @@ -250,11 +259,11 @@ public class BubbleTextView extends TextView private void updateIconState() { if (mIcon instanceof FastBitmapDrawable) { FastBitmapDrawable d = (FastBitmapDrawable) mIcon; - if (isPressed() || mStayPressed) { - d.animateState(FastBitmapDrawable.State.PRESSED); - } else if (getTag() instanceof ShortcutInfo - && ((ShortcutInfo) getTag()).isDisabled != 0) { + if (getTag() instanceof ItemInfo + && ((ItemInfo) getTag()).isDisabled()) { d.animateState(FastBitmapDrawable.State.DISABLED); + } else if (isPressed() || mStayPressed) { + d.animateState(FastBitmapDrawable.State.PRESSED); } else { d.animateState(FastBitmapDrawable.State.NORMAL); } @@ -268,7 +277,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/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 94e3e4141..5832b9f0d 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; @@ -33,6 +32,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.TransitionDrawable; import android.os.Build; import android.os.Parcelable; @@ -48,10 +48,14 @@ import android.view.accessibility.AccessibilityEvent; import android.view.animation.DecelerateInterpolator; 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.FeatureFlags; +import com.android.launcher3.config.ProviderConfig; +import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.folder.FolderIcon.FolderRingAnimator; import com.android.launcher3.util.ParcelableSparseArray; import com.android.launcher3.util.Thunk; @@ -70,17 +74,23 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { private static final boolean LOGD = false; private Launcher mLauncher; + @ViewDebug.ExportedProperty(category = "launcher") @Thunk int mCellWidth; + @ViewDebug.ExportedProperty(category = "launcher") @Thunk int mCellHeight; private int mFixedCellWidth; private int mFixedCellHeight; + @ViewDebug.ExportedProperty(category = "launcher") @Thunk int mCountX; + @ViewDebug.ExportedProperty(category = "launcher") @Thunk int mCountY; private int mOriginalWidthGap; private int mOriginalHeightGap; + @ViewDebug.ExportedProperty(category = "launcher") @Thunk int mWidthGap; + @ViewDebug.ExportedProperty(category = "launcher") @Thunk int mHeightGap; private int mMaxGap; private boolean mDropPending = false; @@ -150,9 +160,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; @@ -206,7 +213,9 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { final Resources res = getResources(); mHotseatScale = (float) grid.hotseatIconSizePx / grid.iconSizePx; - mBackground = (TransitionDrawable) res.getDrawable(R.drawable.bg_screenpanel); + mBackground = (TransitionDrawable) res.getDrawable( + FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ? R.drawable.bg_screenpanel + : R.drawable.bg_celllayout); mBackground.setCallback(this); mBackground.setAlpha((int) (mBackgroundAlpha * 255)); @@ -272,7 +281,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 +343,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,7 +409,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mIsDragTarget = false; } - boolean isDragTarget() { + public boolean isDragTarget() { return mIsDragTarget; } @@ -420,10 +429,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { } } - public boolean getIsDragOverlapping() { - return mIsDragOverlapping; - } - public void disableJailContent() { mJailContent = false; } @@ -450,6 +455,10 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { (ParcelableSparseArray) parcelable : new ParcelableSparseArray(); } + public boolean getIsDragOverlapping() { + return mIsDragOverlapping; + } + @Override protected void onDraw(Canvas canvas) { if (!mIsDragTarget) { @@ -469,12 +478,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 +595,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. @@ -810,7 +816,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { return (float) Math.hypot(x - mTmpPoint[0], y - mTmpPoint[1]); } - int getCellWidth() { + public int getCellWidth() { return mCellWidth; } @@ -1070,53 +1076,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(); @@ -1146,23 +1156,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. @@ -1338,9 +1331,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 @@ -2234,17 +2225,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(); } } @@ -2261,6 +2249,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); @@ -2269,17 +2266,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) { @@ -2623,7 +2624,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { * @return The X, Y cell of a vacant area that can contain this object, * nearest the requested location. */ - int[] findNearestArea(int pixelX, int pixelY, int spanX, int spanY, int[] result) { + public int[] findNearestArea(int pixelX, int pixelY, int spanX, int spanY, int[] result) { return findNearestArea(pixelX, pixelY, spanX, spanY, spanX, spanY, false, result, null); } @@ -2867,10 +2868,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; @@ -2967,7 +2968,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { // cellX and cellY coordinates and which page was clicked. We then set this as a tag on // the CellLayout that was long clicked public static final class CellInfo { - View cell; + public View cell; int cellX = -1; int cellY = -1; int spanX; @@ -2996,6 +2997,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/ClickShadowView.java b/src/com/android/launcher3/ClickShadowView.java index e31d7f7f6..e2bc6bac5 100644 --- a/src/com/android/launcher3/ClickShadowView.java +++ b/src/com/android/launcher3/ClickShadowView.java @@ -22,6 +22,7 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.view.View; +import android.view.ViewDebug; import android.view.ViewGroup; public class ClickShadowView extends View { @@ -32,7 +33,9 @@ public class ClickShadowView extends View { private final Paint mPaint; + @ViewDebug.ExportedProperty(category = "launcher") private final float mShadowOffset; + @ViewDebug.ExportedProperty(category = "launcher") private final float mShadowPadding; private Bitmap mBitmap; diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index edaf525d6..997ded2d3 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -23,6 +23,8 @@ import android.util.AttributeSet; import android.view.View; import android.view.animation.AnimationUtils; +import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.folder.Folder; import com.android.launcher3.util.FlingAnimation; import com.android.launcher3.util.Thunk; @@ -45,20 +47,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 +99,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 380c6b148..b67e07b33 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -33,6 +33,8 @@ import android.view.ViewGroup.MarginLayoutParams; import android.widget.FrameLayout; import android.widget.LinearLayout; +import com.android.launcher3.config.FeatureFlags; + public class DeviceProfile { public final InvariantDeviceProfile inv; @@ -242,7 +244,8 @@ public class DeviceProfile { FontMetrics fm = textPaint.getFontMetrics(); cellWidthPx = iconSizePx; cellHeightPx = iconSizePx + iconDrawablePaddingPx + (int) Math.ceil(fm.bottom - fm.top); - final float scaleDps = res.getDimensionPixelSize(R.dimen.dragViewScale); + final float scaleDps = !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ? 0f + : res.getDimensionPixelSize(R.dimen.dragViewScale); dragViewScale = (iconSizePx + scaleDps) / iconSizePx; // Hotseat @@ -326,8 +329,20 @@ 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) { + public Rect getWorkspacePadding(boolean isLayoutRtl) { Rect searchBarBounds = getSearchBarBounds(isLayoutRtl); Rect padding = new Rect(); if (isVerticalBarLayout()) { @@ -388,13 +403,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)); } } @@ -410,7 +425,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; } @@ -452,27 +467,18 @@ 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(); lp.topMargin = searchBarTopExtraPaddingPx; - 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(); @@ -539,7 +545,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; @@ -548,7 +553,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) { @@ -577,6 +582,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/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java index d7f1d8677..38700805f 100644 --- a/src/com/android/launcher3/FastBitmapDrawable.java +++ b/src/com/android/launcher3/FastBitmapDrawable.java @@ -93,7 +93,7 @@ public class FastBitmapDrawable extends Drawable { private static final ColorMatrix sTempBrightnessMatrix = new ColorMatrix(); private static final ColorMatrix sTempFilterMatrix = new ColorMatrix(); - private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); + private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG); private final Bitmap mBitmap; private State mState = State.NORMAL; diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index d59c64431..f99c08a59 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -22,6 +22,9 @@ import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewGroup; +import com.android.launcher3.config.ProviderConfig; +import com.android.launcher3.folder.Folder; +import com.android.launcher3.folder.FolderPagedView; import com.android.launcher3.util.FocusLogic; import com.android.launcher3.util.Thunk; @@ -90,7 +93,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/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java index aea21c95b..861a9354a 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. @@ -49,7 +48,7 @@ public class FolderInfo extends ItemInfo { /** * Whether this folder has been opened */ - boolean opened; + public boolean opened; public int options; @@ -106,7 +105,7 @@ public class FolderInfo extends ItemInfo { } - void addListener(FolderListener listener) { + public void addListener(FolderListener listener) { listeners.add(listener); } @@ -128,7 +127,7 @@ public class FolderInfo extends ItemInfo { listeners.clear(); } - interface FolderListener { + public interface FolderListener { public void onAdd(ShortcutInfo item); public void onRemove(ShortcutInfo item); public void onTitleChanged(CharSequence title); @@ -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/Hotseat.java b/src/com/android/launcher3/Hotseat.java index 3e838760e..052f7ac90 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -24,6 +24,7 @@ import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; +import android.view.ViewDebug; import android.widget.FrameLayout; import android.widget.TextView; @@ -34,8 +35,10 @@ public class Hotseat extends FrameLayout private Launcher mLauncher; + @ViewDebug.ExportedProperty(category = "launcher") private int mAllAppsButtonRank; + @ViewDebug.ExportedProperty(category = "launcher") private final boolean mHasVerticalHotseat; public Hotseat(Context context) { diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 05ad5388a..d39ae661e 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -108,7 +108,6 @@ public class IconCache { private final BitmapFactory.Options mLowResOptions; private String mSystemState; - private Bitmap mLowResBitmap; private Canvas mLowResCanvas; private Paint mLowResPaint; @@ -119,6 +118,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()); @@ -387,7 +388,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()); } /** @@ -625,16 +627,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)); @@ -674,9 +681,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.insertOrReplace(values); @@ -829,34 +836,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/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java index 7343bf686..f4bfa4549 100644 --- a/src/com/android/launcher3/InsettableFrameLayout.java +++ b/src/com/android/launcher3/InsettableFrameLayout.java @@ -5,12 +5,14 @@ import android.content.res.TypedArray; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; +import android.view.ViewDebug; import android.view.ViewGroup; import android.widget.FrameLayout; public class InsettableFrameLayout extends FrameLayout implements ViewGroup.OnHierarchyChangeListener, Insettable { + @ViewDebug.ExportedProperty(category = "launcher") protected Rect mInsets = new Rect(); public InsettableFrameLayout(Context context, AttributeSet attrs) { diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index b3a8bbc32..d60132270 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -73,17 +73,17 @@ public class InvariantDeviceProfile { /** * Number of icons inside the hotseat area. */ - int numHotseatIcons; + public int numHotseatIcons; float hotseatIconSize; int defaultLayoutId; // Derived invariant properties - int hotseatAllAppsRank; + public int hotseatAllAppsRank; DeviceProfile landscapeProfile; DeviceProfile portraitProfile; - InvariantDeviceProfile() { + public InvariantDeviceProfile() { } public InvariantDeviceProfile(InvariantDeviceProfile p) { diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java index f7e0ea488..1ba09e1eb 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,13 @@ 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 + ")"; + } + + /** + * Whether this item is disabled. + */ + public boolean isDisabled() { + return false; } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 166acd33b..406a6f563 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; @@ -103,19 +102,26 @@ 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.FeatureFlags; +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.folder.Folder; +import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.model.WidgetsModel; +import com.android.launcher3.userevent.Logger; 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; +import com.android.wallpaperpicker.WallpaperUtils; -import java.io.File; import java.io.FileDescriptor; -import java.io.FileOutputStream; -import java.io.IOException; import java.io.PrintWriter; import java.text.DateFormat; import java.util.ArrayList; @@ -132,14 +138,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 @@ -165,8 +170,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"; @@ -179,18 +182,10 @@ 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: long + private static final String RUNTIME_STATE_OPEN_FOLDER_ID = "launcher.open_folder_id"; + // 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 @@ -209,7 +204,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; @@ -233,8 +229,6 @@ public class Launcher extends Activity private final BroadcastReceiver mCloseSystemDialogsReceiver = new CloseSystemDialogsIntentReceiver(); - private LayoutInflater mInflater; - @Thunk Workspace mWorkspace; private View mLauncherView; private View mPageIndicators; @@ -246,7 +240,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; @@ -259,6 +253,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; @@ -267,7 +262,6 @@ public class Launcher extends Activity @Thunk WidgetsContainerView mWidgetsView; @Thunk WidgetsModel mWidgetsModel; - private boolean mAutoAdvanceRunning = false; private AppWidgetHostView mQsb; private Bundle mSavedState; @@ -287,8 +281,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; @@ -305,28 +298,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. @@ -378,7 +370,9 @@ public class Launcher extends Activity } private Stats mStats; - FocusIndicatorView mFocusHandler; + private Logger mUserEventLogger; + + public FocusIndicatorView mFocusHandler; private boolean mRotationEnabled = false; @Thunk void setOrientation() { @@ -433,9 +427,9 @@ public class Launcher extends Activity mIconCache = app.getIconCache(); mDragController = new DragController(this); - mInflater = getLayoutInflater(); mStateTransitionAnimation = new LauncherStateTransitionAnimation(this); + mUserEventLogger = new Logger(this); mStats = new Stats(this); mAppWidgetManager = AppWidgetManagerCompat.getInstance(this); @@ -492,7 +486,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, @@ -552,7 +546,7 @@ public class Launcher extends Activity return; } // The underlying workspace and hotseat are temporarily suppressed by the search - // overlay. So they sholudn't be accessible. + // overlay. So they shouldn't be accessible. if (mWorkspace != null) { mWorkspaceImportanceForAccessibility = mWorkspace.getImportantForAccessibility(); @@ -639,10 +633,7 @@ public class Launcher extends Activity public Stats getStats() { return mStats; } - - public LayoutInflater getInflater() { - return mInflater; - } + public Logger getLogger() {return mUserEventLogger; } public boolean isDraggingEnabled() { // We prevent dragging when we are loading the workspace as it is possible to pick up a view @@ -724,7 +715,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); } @@ -810,24 +801,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(); @@ -968,6 +957,7 @@ public class Launcher extends Activity } super.onResume(); + mUserEventLogger.resetElapsedSessionMillis(); // Restore the previous launcher state if (mOnResumeState == State.WORKSPACE) { @@ -991,12 +981,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; @@ -1344,16 +1328,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 ? @@ -1400,9 +1377,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); @@ -1422,6 +1402,9 @@ public class Launcher extends Activity mSearchDropTargetBar.setup(this, dragController); mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar()); } + if (mAppInfoDropTargetBar != null) { + mAppInfoDropTargetBar.setup(this, dragController); + } if (TestingUtils.MEMORY_DUMP_ENABLED) { TestingUtils.addWeightWatcher(this); @@ -1519,7 +1502,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); @@ -1536,7 +1519,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); @@ -1563,10 +1545,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); } @@ -1762,11 +1740,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); @@ -1783,7 +1761,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() { @@ -1793,7 +1771,7 @@ public class Launcher extends Activity } i++; } - sendAdvanceMessage(mAdvanceInterval); + sendAdvanceMessage(ADVANCE_INTERVAL); } return true; } @@ -1849,6 +1827,10 @@ public class Launcher extends Activity return mSearchDropTargetBar; } + public AppInfoDropTargetBar getAppInfoDropTargetBar() { + return mAppInfoDropTargetBar; + } + public LauncherAppWidgetHost getAppWidgetHost() { return mAppWidgetHost; } @@ -1979,18 +1961,16 @@ public class Launcher extends Activity super.onSaveInstanceState(outState); outState.putInt(RUNTIME_STATE, mState.ordinal()); - // We close any open folder since it will not be re-opened, and we need to make sure - // this state is reflected. - closeFolder(false); + Folder openFolder = mWorkspace.getOpenFolder(); + if (openFolder != null) { + outState.putLong(RUNTIME_STATE_OPEN_FOLDER_ID, openFolder.mInfo.id); + } 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); } @@ -2222,7 +2202,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 @@ -2297,7 +2276,6 @@ public class Launcher extends Activity resetAddInfo(); mPendingAddInfo.container = container; mPendingAddInfo.screenId = screenId; - mPendingAddInfo.dropPos = null; if (cell != null) { mPendingAddInfo.cellX = cell[0]; @@ -2321,7 +2299,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; @@ -2552,8 +2529,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(); @@ -2676,12 +2655,17 @@ public class Launcher extends Activity final ShortcutInfo shortcut = (ShortcutInfo) tag; if (shortcut.isDisabled != 0) { - int error = R.string.activity_not_available; - if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SAFEMODE) != 0) { - error = R.string.safemode_shortcut_error; + if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SUSPENDED) != 0 + || (shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_QUIET_USER) != 0) { + // Launch activity anyway, framework will tell the user why the app is suspended. + } else { + int error = R.string.activity_not_available; + if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SAFEMODE) != 0) { + error = R.string.safemode_shortcut_error; + } + Toast.makeText(this, error, Toast.LENGTH_SHORT).show(); + return; } - Toast.makeText(this, error, Toast.LENGTH_SHORT).show(); - return; } // Check for abandoned promise @@ -2745,38 +2729,10 @@ public class Launcher extends Activity throw new IllegalArgumentException("Input must be a FolderIcon"); } - // TODO(sunnygoyal): Re-evaluate this code. FolderIcon folderIcon = (FolderIcon) v; - final FolderInfo info = folderIcon.getFolderInfo(); - Folder openFolder = mWorkspace.getFolderForTag(info); - - // If the folder info reports that the associated folder is open, then verify that - // it is actually opened. There have been a few instances where this gets out of sync. - if (info.opened && openFolder == null) { - Log.d(TAG, "Folder info marked as open, but associated folder is not open. Screen: " - + info.screenId + " (" + info.cellX + ", " + info.cellY + ")"); - info.opened = false; - } - - if (!info.opened && !folderIcon.getFolder().isDestroyed()) { - // Close any open folder - closeFolder(); + if (!folderIcon.getFolderInfo().opened && !folderIcon.getFolder().isDestroyed()) { // Open the requested folder - openFolder(folderIcon); - } else { - // Find the open folder... - int folderScreen; - if (openFolder != null) { - folderScreen = mWorkspace.getPageForView(openFolder); - // .. and close it - closeFolder(openFolder, true); - if (folderScreen != mWorkspace.getCurrentPage()) { - // Close any folder open on the current screen - closeFolder(); - // Pull the folder onto this screen - openFolder(folderIcon); - } - } + openFolder(folderIcon, true); } if (mLauncherCallbacks != null) { @@ -2809,7 +2765,7 @@ public class Launcher extends Activity int pageScroll = mWorkspace.getScrollForPage(mWorkspace.getPageNearestToCenterOfScreen()); float offset = mWorkspace.mWallpaperOffset.wallpaperOffsetForScroll(pageScroll); startActivityForResult(new Intent(Intent.ACTION_SET_WALLPAPER).setPackage(getPackageName()) - .putExtra(WallpaperPickerActivity.EXTRA_WALLPAPER_OFFSET, offset), + .putExtra(WallpaperUtils.EXTRA_WALLPAPER_OFFSET, offset), REQUEST_PICK_WALLPAPER); if (mLauncherCallbacks != null) { @@ -3083,12 +3039,8 @@ public class Launcher extends Activity } } - private void growAndFadeOutFolderIcon(FolderIcon fi) { + private void growAndFadeOutFolderIcon(FolderIcon fi, boolean animate) { 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(); @@ -3100,32 +3052,26 @@ 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)); } oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration)); oa.start(); - if (Utilities.isPowerSaverOn(this)) { - // Animations are disabled in battery saver mode, so just skip to the end state. + if (!animate) { oa.end(); } } 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 @@ -3149,9 +3095,11 @@ public class Launcher extends Activity * is animated relative to the specified View. If the View is null, no animation * is played. * - * @param folderInfo The FolderInfo describing the folder to open. + * @param folderIcon The FolderIcon describing the folder to open. */ - public void openFolder(FolderIcon folderIcon) { + public void openFolder(FolderIcon folderIcon, boolean animate) { + animate &= !Utilities.isPowerSaverOn(this); + Folder folder = folderIcon.getFolder(); Folder openFolder = mWorkspace != null ? mWorkspace.getOpenFolder() : null; if (openFolder != null && openFolder != folder) { @@ -3170,13 +3118,17 @@ public class Launcher extends Activity // There was a one-off crash where the folder had a parent already. if (folder.getParent() == null) { mDragLayer.addView(folder); - mDragController.addDropTarget((DropTarget) folder); + mDragController.addDropTarget(folder); } else { Log.w(TAG, "Opening folder (" + folder + ") which already has a parent (" + folder.getParent() + ")."); } - folder.animateOpen(); - growAndFadeOutFolderIcon(folderIcon); + if (animate) { + folder.animateOpen(); + } else { + folder.open(); + } + growAndFadeOutFolderIcon(folderIcon, animate); // Notify the accessibility manager that this folder "window" has appeared and occluded // the workspace items @@ -3199,6 +3151,8 @@ public class Launcher extends Activity } public void closeFolder(Folder folder, boolean animate) { + animate &= !Utilities.isPowerSaverOn(this); + folder.getInfo().opened = false; ViewGroup parent = (ViewGroup) folder.getParent().getParent(); @@ -3355,38 +3309,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) { @@ -3406,6 +3339,7 @@ public class Launcher extends Activity getWindow().getDecorView() .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); } + mUserEventLogger.resetElapsedContainerMillis(); return changed; } @@ -3435,9 +3369,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; } @@ -3514,31 +3446,50 @@ 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 if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) { + mState = State.WORKSPACE_SPRING_LOADED; + } else { + mState = State.WORKSPACE; + } } 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 @@ -3558,12 +3509,19 @@ public class Launcher extends Activity }, delay); } + 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); } } @@ -3784,7 +3742,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; } } @@ -3796,11 +3767,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(); @@ -3922,7 +3888,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); @@ -4143,6 +4109,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. * @@ -4161,6 +4142,26 @@ public class Launcher extends Activity if (!mWorkspace.hasFocus()) { mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus(); } + + long folderId = mSavedState.getLong(RUNTIME_STATE_OPEN_FOLDER_ID); + if (folderId != 0) { + View view = mWorkspace.getHomescreenIconByItemId(folderId); + if (view instanceof FolderIcon) { + FolderIcon icon = (FolderIcon) view; + FolderInfo info = icon.getFolderInfo(); + long currentScreenId = mWorkspace.getScreenIdForPageIndex( + mWorkspace.getNextPage()); + if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT + || info.screenId == currentScreenId) { + // We can show the folder + openFolder(icon, false); + } else { + Launcher.addDumpLog(TAG, "Saved state contains folder " + info + + " but current screen is " + currentScreenId); + } + } + } + mSavedState = null; } @@ -4219,10 +4220,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)); @@ -4489,7 +4487,7 @@ public class Launcher extends Activity public void run() { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } - }, mRestoreScreenOrientationDelay); + }, RESTORE_SCREEN_ORIENTATION_DELAY); } } } @@ -4757,7 +4755,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(); @@ -4768,51 +4765,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); } } @@ -4824,53 +4817,6 @@ public class Launcher extends Activity 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); - } - } - public static List<View> getFolderContents(View icon) { if (icon instanceof FolderIcon) { return ((FolderIcon) icon).getFolder().getItemsInReadingOrder(); 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 4ac5ef3d5..e58652b8e 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -17,6 +17,7 @@ package com.android.launcher3; import android.app.SearchManager; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -40,7 +41,7 @@ public class LauncherAppState { private final IconCache mIconCache; private final WidgetPreviewLoader mWidgetCache; - private boolean mWallpaperChangedSinceLastCheck; + @Thunk boolean mWallpaperChangedSinceLastCheck; private static WeakReference<LauncherProvider> sLauncherProvider; private static Context sContext; @@ -66,11 +67,17 @@ public class LauncherAppState { return sContext; } - public static void setApplicationContext(Context context) { - if (sContext != null) { - Log.w(Launcher.TAG, "setApplicationContext called twice! old=" + sContext + " new=" + context); + static void setLauncherProvider(LauncherProvider provider) { + if (sLauncherProvider != null) { + Log.w(Launcher.TAG, "setLauncherProvider called twice! old=" + + sLauncherProvider.get() + " new=" + provider); } - sContext = context.getApplicationContext(); + sLauncherProvider = new WeakReference<>(provider); + + // The content provider exists for the entire duration of the launcher main process and + // is the first component to get created. Initializing application context here ensures + // that LauncherAppState always exists in the main process. + sContext = provider.getContext().getApplicationContext(); } private LauncherAppState() { @@ -100,9 +107,19 @@ public class LauncherAppState { // For handling managed profiles filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED); filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED); + filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED); 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)); + } new ConfigMonitor(sContext).register(); } @@ -126,7 +143,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; @@ -145,22 +162,10 @@ public class LauncherAppState { return mModel; } - static void setLauncherProvider(LauncherProvider provider) { - 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; diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java index 418525758..c78908273 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java @@ -25,10 +25,12 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; +import android.view.ViewDebug; 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; @@ -42,18 +44,20 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc private CheckLongPressHelper mLongPressHelper; private StylusEventHelper mStylusEventHelper; private Context mContext; + @ViewDebug.ExportedProperty(category = "launcher") private int mPreviousOrientation; private DragLayer mDragLayer; private float mSlop; + @ViewDebug.ExportedProperty(category = "launcher") private boolean mChildrenFocused; public LauncherAppWidgetHostView(Context context) { 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()); @@ -101,7 +105,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 2177f527e..c2ab20a62 100644 --- a/src/com/android/launcher3/LauncherBackupAgentHelper.java +++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java @@ -25,7 +25,7 @@ import android.database.Cursor; import android.os.ParcelFileDescriptor; import android.util.Log; -import com.android.launcher3.model.MigrateFromRestoreTask; +import com.android.launcher3.model.GridSizeMigrationTask; import java.io.IOException; @@ -93,7 +93,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 { @@ -110,25 +111,27 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper { } if (hasData && mHelper.restoreSuccessful) { - LauncherAppState.getLauncherProvider().clearFlagEmptyDbCreated(); + LauncherSettings.Settings.call(getContentResolver(), + LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG); LauncherClings.markFirstRunClingDismissed(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()) { - MigrateFromRestoreTask.markForMigration(getApplicationContext(), - (int) mHelper.migrationCompatibleProfileData.desktopCols, - (int) mHelper.migrationCompatibleProfileData.desktopRows, - mHelper.widgetSizes); + if (GridSizeMigrationTask.ENABLED && mHelper.shouldAttemptWorkspaceMigration()) { + GridSizeMigrationTask.markForMigration(getApplicationContext(), + mHelper.widgetSizes, mHelper.migrationCompatibleProfileData); } - 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/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 47d1ce9c0..05d729e78 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -52,7 +52,7 @@ import com.android.launcher3.backup.nano.BackupProtos.Screen; import com.android.launcher3.backup.nano.BackupProtos.Widget; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; -import com.android.launcher3.model.MigrateFromRestoreTask; +import com.android.launcher3.model.GridSizeMigrationTask; import com.android.launcher3.util.Thunk; import com.google.protobuf.nano.InvalidProtocolBufferNanoException; import com.google.protobuf.nano.MessageNano; @@ -315,14 +315,13 @@ public class LauncherBackupHelper implements BackupHelper { return true; } - if (MigrateFromRestoreTask.ENABLED && - (oldProfile.desktopCols - currentProfile.desktopCols <= 1) && - (oldProfile.desktopRows - currentProfile.desktopRows <= 1)) { - // Allow desktop migration when row and/or column count contracts by 1. - + if (GridSizeMigrationTask.ENABLED) { + // One time migrate the workspace when launcher starts. migrationCompatibleProfileData = initDeviceProfileData(mIdp); migrationCompatibleProfileData.desktopCols = oldProfile.desktopCols; migrationCompatibleProfileData.desktopRows = oldProfile.desktopRows; + migrationCompatibleProfileData.hotseatCount = oldProfile.hotseatCount; + migrationCompatibleProfileData.allappsRank = oldProfile.allappsRank; return true; } return false; diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index 772fbf358..1d7575253 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -54,20 +54,33 @@ public interface LauncherCallbacks { */ public void onLauncherProviderChange(); public void finishBindingItems(final boolean upgradePath); - public void onClickAllAppsButton(View v); public void bindAllApplications(ArrayList<AppInfo> apps); + public void onInteractionBegin(); + public void onInteractionEnd(); + + /** + * Extension points for Gel Logging. + */ + @Deprecated + public void onClickAllAppsButton(View v); + @Deprecated public void onClickFolderIcon(View v); + @Deprecated public void onClickAppShortcut(View v); @Deprecated public void onClickPagedViewIcon(View v); + @Deprecated public void onClickWallpaperPicker(View v); + @Deprecated public void onClickSettingsButton(View v); + @Deprecated public void onClickAddWidgetButton(View v); + @Deprecated public void onPageSwitch(View newPage, int newPageIndex); + @Deprecated public void onWorkspaceLockedChanged(); + @Deprecated public void onDragStarted(View view); - public void onInteractionBegin(); - public void onInteractionEnd(); /* * Extension points for replacing the search experience diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java index 5c1e83036..9b8e89429 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 774cca459..f707ec501 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -56,13 +56,17 @@ 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.model.MigrateFromRestoreTask; +import com.android.launcher3.config.ProviderConfig; +import com.android.launcher3.folder.Folder; +import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.model.GridSizeMigrationTask; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.util.ComponentKey; 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 +82,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 +128,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. @@ -189,6 +188,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); @@ -213,7 +213,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 { @@ -486,7 +486,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); @@ -531,8 +533,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; @@ -592,11 +593,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 @@ -627,7 +623,7 @@ public class LauncherModel extends BroadcastReceiver * Adds an item to the DB if it was not created previously, or move it to a new * <container, screen, cellX, cellY> */ - static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container, + public static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container, long screenId, int cellX, int cellY) { if (item.container == ItemInfo.NO_ID) { // From all apps @@ -655,12 +651,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; } @@ -813,7 +804,7 @@ public class LauncherModel extends BroadcastReceiver * Move items in the DB to a new <container, screen, cellX, cellY>. We assume that the * cellX, cellY have already been updated on the ItemInfos. */ - static void moveItemsInDatabase(Context context, final ArrayList<ItemInfo> items, + public static void moveItemsInDatabase(Context context, final ArrayList<ItemInfo> items, final long container, final int screen) { ArrayList<ContentValues> contentValues = new ArrayList<ContentValues>(); @@ -887,7 +878,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)) { @@ -939,51 +930,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. */ @@ -1005,7 +951,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(); @@ -1045,15 +993,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() { @@ -1264,6 +1203,20 @@ public class LauncherModel extends BroadcastReceiver } } + @Override + public void onPackagesSuspended(String[] packageNames, UserHandleCompat user) { + enqueuePackageUpdated(new PackageUpdatedTask( + PackageUpdatedTask.OP_SUSPEND, packageNames, + user)); + } + + @Override + public void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user) { + enqueuePackageUpdated(new PackageUpdatedTask( + PackageUpdatedTask.OP_UNSUSPEND, packageNames, + user)); + } + /** * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and * ACTION_PACKAGE_CHANGED. @@ -1282,7 +1235,8 @@ public class LauncherModel extends BroadcastReceiver callbacks.bindSearchProviderChanged(); } } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action) - || LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) { + || LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action) + || LauncherAppsCompat.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED.equals(action)) { UserManagerCompat.getInstance(context).enableAndResetCache(); forceReload(); } @@ -1349,14 +1303,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); @@ -1371,21 +1327,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) { @@ -1411,8 +1352,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 { @@ -1421,10 +1361,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 @@ -1728,46 +1664,35 @@ 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(); int countX = profile.numColumns; int countY = profile.numRows; - if (MigrateFromRestoreTask.ENABLED && MigrateFromRestoreTask.shouldRunTask(mContext)) { - long migrationStartTime = System.currentTimeMillis(); - Log.v(TAG, "Starting workspace migration after restore"); - try { - MigrateFromRestoreTask task = new MigrateFromRestoreTask(mContext); - // Clear the flags before starting the task, so that we do not run the task - // again, in case there was an uncaught error. - MigrateFromRestoreTask.clearFlags(mContext); - task.execute(); - } catch (Exception e) { - Log.e(TAG, "Error during grid migration", e); - - // Clear workspace. - mFlags = mFlags | LOADER_FLAG_CLEAR_WORKSPACE; - } - Log.v(TAG, "Workspace migration completed in " - + (System.currentTimeMillis() - migrationStartTime)); + if (GridSizeMigrationTask.ENABLED && + !GridSizeMigrationTask.migrateGridIfNeeded(mContext)) { + // Migration failed. Clear workspace. + mFlags = mFlags | LOADER_FLAG_CLEAR_WORKSPACE; } 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) { @@ -1822,8 +1747,11 @@ public class LauncherModel extends BroadcastReceiver final CursorIconInfo cursorIconInfo = new CursorIconInfo(c); final LongSparseArray<UserHandleCompat> allUsers = new LongSparseArray<>(); + final LongSparseArray<Boolean> quietMode = new LongSparseArray<>(); for (UserHandleCompat user : mUserManager.getUserProfiles()) { - allUsers.put(mUserManager.getSerialNumberForUser(user), user); + long serialNo = mUserManager.getSerialNumberForUser(user); + allUsers.put(serialNo, user); + quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user)); } ShortcutInfo info; @@ -1872,6 +1800,14 @@ public class LauncherModel extends BroadcastReceiver restoredRows.add(id); restored = false; } + boolean isSuspended = launcherApps.isPackageSuspendedForProfile( + cn.getPackageName(), user); + if (isSuspended) { + disabledState = ShortcutInfo.FLAG_DISABLED_SUSPENDED; + } + if (quietMode.get(serialNumber)) { + disabledState |= ShortcutInfo.FLAG_DISABLED_QUIET_USER; + } } else if (validPkg) { intent = null; if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) { @@ -1890,8 +1826,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 { @@ -1902,8 +1837,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. @@ -1929,14 +1863,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; } @@ -1949,8 +1881,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>(); @@ -1963,8 +1894,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; } @@ -1974,8 +1904,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; } @@ -1995,9 +1924,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); @@ -2152,11 +2078,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) { @@ -2197,8 +2120,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; } @@ -2250,7 +2172,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 { @@ -2276,8 +2198,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); @@ -2310,7 +2235,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); } @@ -2460,19 +2385,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); + } } }); } @@ -2495,9 +2437,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(); @@ -2514,13 +2454,7 @@ public class LauncherModel extends BroadcastReceiver } } }; - if (postOnMainThread) { - synchronized (deferredBindRunnables) { - deferredBindRunnables.add(r); - } - } else { - runOnMainThread(r); - } + executor.execute(r); } // Bind the folders @@ -2533,13 +2467,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 @@ -2554,11 +2482,7 @@ public class LauncherModel extends BroadcastReceiver } } }; - if (postOnMainThread) { - deferredBindRunnables.add(r); - } else { - runOnMainThread(r); - } + executor.execute(r); } } @@ -2636,6 +2560,7 @@ public class LauncherModel extends BroadcastReceiver public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { + callbacks.clearPendingBinds(); callbacks.startBinding(); } } @@ -2644,28 +2569,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() { @@ -2695,11 +2612,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); } } @@ -2810,12 +2739,12 @@ public class LauncherModel extends BroadcastReceiver if (apps == null || apps.isEmpty()) { return; } - + boolean quietMode = mUserManager.isQuietModeEnabled(user); // Create the ApplicationInfos for (int i = 0; i < apps.size(); i++) { LauncherActivityInfoCompat app = apps.get(i); // This builds the icon bitmaps. - mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache)); + mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, quietMode)); } final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user); @@ -2963,10 +2892,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); } } @@ -2995,6 +2922,8 @@ public class LauncherModel extends BroadcastReceiver public static final int OP_UPDATE = 2; public static final int OP_REMOVE = 3; // uninstlled public static final int OP_UNAVAILABLE = 4; // external media unmounted + public static final int OP_SUSPEND = 5; // package suspended + public static final int OP_UNSUSPEND = 6; // package unsuspended public PackageUpdatedTask(int op, String[] packages, UserHandleCompat user) { @@ -3052,6 +2981,15 @@ public class LauncherModel extends BroadcastReceiver mApp.getWidgetCache().removePackage(packages[i], mUser); } break; + case OP_SUSPEND: + case OP_UNSUSPEND: + boolean suspend = mOp == OP_SUSPEND; + for (int i=0; i<N; i++) { + if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.suspendPackage " + + suspend + " " + packages[i]); + mBgAllAppsList.suspendPackage(packages[i], mUser, suspend); + } + break; } ArrayList<AppInfo> added = null; @@ -3104,7 +3042,7 @@ public class LauncherModel extends BroadcastReceiver } // Update shortcut infos - if (mOp == OP_ADD || mOp == OP_UPDATE) { + if (mOp == OP_ADD || mOp == OP_UPDATE || mOp == OP_UNSUSPEND) { final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<ShortcutInfo>(); final ArrayList<ShortcutInfo> removedShortcuts = new ArrayList<ShortcutInfo>(); final ArrayList<LauncherAppWidgetInfo> widgets = new ArrayList<LauncherAppWidgetInfo>(); @@ -3117,6 +3055,11 @@ public class LauncherModel extends BroadcastReceiver boolean infoUpdated = false; boolean shortcutUpdated = false; + if (mOp == OP_UNSUSPEND) { + si.isDisabled &= ~ ShortcutInfo.FLAG_DISABLED_SUSPENDED; + infoUpdated = true; + } + // Update shortcuts which use iconResource. if ((si.iconResource != null) && packageSet.contains(si.iconResource.packageName)) { @@ -3242,7 +3185,7 @@ public class LauncherModel extends BroadcastReceiver final ArrayList<String> removedPackageNames = new ArrayList<String>(); - if (mOp == OP_REMOVE || mOp == OP_UNAVAILABLE) { + if (mOp == OP_REMOVE || mOp == OP_UNAVAILABLE || mOp == OP_SUSPEND) { // Mark all packages in the broadcast to be removed removedPackageNames.addAll(Arrays.asList(packages)); } else if (mOp == OP_UPDATE) { @@ -3258,6 +3201,8 @@ public class LauncherModel extends BroadcastReceiver final int removeReason; if (mOp == OP_UNAVAILABLE) { removeReason = ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE; + } else if (mOp == OP_SUSPEND) { + removeReason = ShortcutInfo.FLAG_DISABLED_SUSPENDED; } else { // Remove all the components associated with this package for (String pn : removedPackageNames) { @@ -3313,7 +3258,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. @@ -3324,16 +3269,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(); - } - } - }); } } @@ -3370,7 +3305,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 @@ -3439,7 +3374,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)) { /** @@ -3756,10 +3691,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 476093032..a57f68037 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -43,7 +43,6 @@ import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Process; -import android.os.StrictMode; import android.os.UserManager; import android.text.TextUtils; import android.util.Log; @@ -61,7 +60,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"; @@ -77,30 +75,23 @@ public class LauncherProvider extends ContentProvider { private static final String RESTRICTION_PACKAGE_NAME = "workspace.configuration.package.name"; + private static final Object LISTENER_LOCK = new Object(); @Thunk LauncherProviderChangeListener mListener; - @Thunk DatabaseHelper mOpenHelper; + protected DatabaseHelper mOpenHelper; @Override public boolean onCreate() { - final Context context = getContext(); - // The content provider exists for the entire duration of the launcher main process and - // is the first component to get created. Initializing application context here ensures - // that LauncherAppState always exists in the main process. - LauncherAppState.setApplicationContext(context.getApplicationContext()); - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); - mOpenHelper = new DatabaseHelper(context); - StrictMode.setThreadPolicy(oldPolicy); LauncherAppState.setLauncherProvider(this); return true; } - public boolean wasNewDbCreated() { - return mOpenHelper.wasNewDbCreated(); - } - + /** + * Sets a provider listener. + */ public void setLauncherProviderChangeListener(LauncherProviderChangeListener listener) { - mListener = listener; - mOpenHelper.mListener = mListener; + synchronized (LISTENER_LOCK) { + mListener = listener; + } } @Override @@ -113,9 +104,19 @@ public class LauncherProvider extends ContentProvider { } } + /** + * Overridden in tests + */ + protected synchronized void createDbIfNotExists() { + if (mOpenHelper == null) { + mOpenHelper = new DatabaseHelper(getContext(), this); + } + } + @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + createDbIfNotExists(); SqlArguments args = new SqlArguments(uri, selection, selectionArgs); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); @@ -151,6 +152,7 @@ public class LauncherProvider extends ContentProvider { @Override public Uri insert(Uri uri, ContentValues initialValues) { + createDbIfNotExists(); SqlArguments args = new SqlArguments(uri); // In very limited cases, we support system|signature permission apps to modify the db. @@ -185,9 +187,9 @@ public class LauncherProvider extends ContentProvider { return uri; } - @Override public int bulkInsert(Uri uri, ContentValues[] values) { + createDbIfNotExists(); SqlArguments args = new SqlArguments(uri); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); @@ -213,6 +215,7 @@ public class LauncherProvider extends ContentProvider { @Override public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { + createDbIfNotExists(); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); db.beginTransaction(); try { @@ -227,6 +230,7 @@ public class LauncherProvider extends ContentProvider { @Override public int delete(Uri uri, String selection, String[] selectionArgs) { + createDbIfNotExists(); SqlArguments args = new SqlArguments(uri, selection, selectionArgs); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); @@ -239,6 +243,7 @@ public class LauncherProvider extends ContentProvider { @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + createDbIfNotExists(); SqlArguments args = new SqlArguments(uri, selection, selectionArgs); addModifiedTime(values); @@ -255,6 +260,7 @@ public class LauncherProvider extends ContentProvider { if (Binder.getCallingUid() != Process.myUid()) { return null; } + createDbIfNotExists(); switch (method) { case LauncherSettings.Settings.METHOD_GET_BOOLEAN: { @@ -267,8 +273,10 @@ public class LauncherProvider extends ContentProvider { case LauncherSettings.Settings.METHOD_SET_BOOLEAN: { boolean value = extras.getBoolean(LauncherSettings.Settings.EXTRA_VALUE); Utilities.getPrefs(getContext()).edit().putBoolean(arg, value).apply(); - if (mListener != null) { - mListener.onSettingsChanged(arg, value); + synchronized (LISTENER_LOCK) { + if (mListener != null) { + mListener.onSettingsChanged(arg, value); + } } if (extras.getBoolean(LauncherSettings.Settings.NOTIFY_BACKUP)) { LauncherBackupAgentHelper.dataChanged(getContext()); @@ -277,6 +285,51 @@ 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: { + // Are you sure? (y/n) + mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase()); + return null; + } } return null; } @@ -285,8 +338,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 { @@ -303,7 +356,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); } @@ -317,11 +370,16 @@ public class LauncherProvider extends ContentProvider { return folderIds; } - private void notifyListeners() { + /** + * Overridden in tests + */ + protected void notifyListeners() { // always notify the backup agent LauncherBackupAgentHelper.dataChanged(getContext()); - if (mListener != null) { - mListener.onLauncherProviderChange(); + synchronized (LISTENER_LOCK) { + if (mListener != null) { + mListener.onLauncherProviderChange(); + } } } @@ -329,22 +387,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(); } @@ -355,7 +405,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)) { @@ -440,38 +490,41 @@ 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() { - // Are you sure? (y/n) - mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase()); + /** + * Send notification that we've deleted the {@link AppWidgetHost}, + * probably as part of the initial database creation. The receiver may + * want to re-call {@link AppWidgetHost#startListening()} to ensure + * callbacks are correctly set. + */ + @Thunk void notifyAppHostReset() { + new MainThreadExecutor().execute(new Runnable() { + + @Override + public void run() { + synchronized (LISTENER_LOCK) { + if (mListener != null) { + mListener.onAppWidgetHostReset(); + } + } + } + }); } - private static class DatabaseHelper extends SQLiteOpenHelper implements LayoutParserCallback { + /** + * The class is subclassed in tests to create an in-memory db. + */ + protected static class DatabaseHelper extends SQLiteOpenHelper implements LayoutParserCallback { + private final LauncherProvider mProvider; private final Context mContext; @Thunk final AppWidgetHost mAppWidgetHost; private long mMaxItemId = -1; private long mMaxScreenId = -1; - private boolean mNewDbCreated = false; - - @Thunk LauncherProviderChangeListener mListener; - - DatabaseHelper(Context context) { + DatabaseHelper(Context context, LauncherProvider provider) { super(context, LauncherFiles.LAUNCHER_DB, null, DATABASE_VERSION); mContext = context; + mProvider = provider; + mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID); // Table creation sometimes fails silently, which leads to a crash loop. @@ -494,6 +547,19 @@ public class LauncherProvider extends ContentProvider { } } + /** + * Constructor used only in tests. + */ + public DatabaseHelper(Context context, LauncherProvider provider, String tableName) { + super(context, tableName, null, DATABASE_VERSION); + mContext = context; + mProvider = provider; + + mAppWidgetHost = null; + mMaxItemId = initializeMaxItemId(getWritableDatabase()); + mMaxScreenId = initializeMaxScreenId(getWritableDatabase()); + } + private boolean tableExists(String tableName) { Cursor c = getReadableDatabase().query( true, "sqlite_master", new String[] {"tbl_name"}, @@ -506,17 +572,12 @@ public class LauncherProvider extends ContentProvider { } } - public boolean wasNewDbCreated() { - return mNewDbCreated; - } - @Override public void onCreate(SQLiteDatabase db) { if (LOGD) Log.d(TAG, "creating new launcher database"); mMaxItemId = 1; mMaxScreenId = 0; - mNewDbCreated = true; addFavoritesTable(db, false); addWorkspacesTable(db, false); @@ -524,38 +585,33 @@ public class LauncherProvider extends ContentProvider { // Database was just created, so wipe any previous widgets if (mAppWidgetHost != null) { mAppWidgetHost.deleteHost(); - - /** - * Send notification that we've deleted the {@link AppWidgetHost}, - * probably as part of the initial database creation. The receiver may - * want to re-call {@link AppWidgetHost#startListening()} to ensure - * callbacks are correctly set. - */ - new MainThreadExecutor().execute(new Runnable() { - - @Override - public void run() { - if (mListener != null) { - mListener.onAppWidgetHostReset(); - } - } - }); + mProvider.notifyAppHostReset(); } // Fresh and clean launcher DB. mMaxItemId = initializeMaxItemId(db); - setFlagEmptyDbCreated(); + onEmptyDbCreated(); + } + + /** + * Overriden in tests. + */ + protected void onEmptyDbCreated() { + // Set the flag for empty DB + Utilities.getPrefs(mContext).edit().putBoolean(EMPTY_DATABASE_CREATED, true).commit(); // When a new DB is created, remove all previously stored managed profile information. - ManagedProfileHeuristic.processAllUsers(Collections.<UserHandleCompat>emptyList(), mContext); + ManagedProfileHeuristic.processAllUsers(Collections.<UserHandleCompat>emptyList(), + mContext); } - private void addFavoritesTable(SQLiteDatabase db, boolean optional) { - UserManagerCompat userManager = UserManagerCompat.getInstance(mContext); - long userSerialNumber = userManager.getSerialNumberForUser( + protected long getDefaultUserSerial() { + return UserManagerCompat.getInstance(mContext).getSerialNumberForUser( UserHandleCompat.myUserHandle()); - String ifNotExists = optional ? " IF NOT EXISTS " : ""; + } + private void addFavoritesTable(SQLiteDatabase db, boolean optional) { + String ifNotExists = optional ? " IF NOT EXISTS " : ""; db.execSQL("CREATE TABLE " + ifNotExists + TABLE_FAVORITES + " (" + "_id INTEGER PRIMARY KEY," + "title TEXT," + @@ -578,7 +634,7 @@ public class LauncherProvider extends ContentProvider { "appWidgetProvider TEXT," + "modified INTEGER NOT NULL DEFAULT 0," + "restored INTEGER NOT NULL DEFAULT 0," + - "profileId INTEGER DEFAULT " + userSerialNumber + "," + + "profileId INTEGER DEFAULT " + getDefaultUserSerial() + "," + "rank INTEGER NOT NULL DEFAULT 0," + "options INTEGER NOT NULL DEFAULT 0" + ");"); @@ -628,10 +684,6 @@ public class LauncherProvider extends ContentProvider { Utilities.getPrefs(mContext).edit().putBoolean(EMPTY_DATABASE_CREATED, false).commit(); } - private void setFlagEmptyDbCreated() { - Utilities.getPrefs(mContext).edit().putBoolean(EMPTY_DATABASE_CREATED, true).commit(); - } - @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (LOGD) Log.d(TAG, "onUpgrade triggered: " + oldVersion); @@ -1119,10 +1171,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); @@ -1170,17 +1218,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; @@ -1189,22 +1230,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) { @@ -1220,7 +1259,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); @@ -1241,9 +1280,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) { @@ -1363,8 +1399,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/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java index 55a512fb5..104af5280 100644 --- a/src/com/android/launcher3/LauncherRootView.java +++ b/src/com/android/launcher3/LauncherRootView.java @@ -9,11 +9,14 @@ import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; +import android.view.ViewDebug; public class LauncherRootView extends InsettableFrameLayout { private final Paint mOpaquePaint; + @ViewDebug.ExportedProperty(category = "launcher") private boolean mDrawRightInsetBar; + @ViewDebug.ExportedProperty(category = "launcher") private int mRightInsetBarWidth; private View mAlignedView; diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index 01d670d7b..fb5306830 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; @@ -113,7 +115,7 @@ public class LauncherSettings { /** * The content:// style URL for this table */ - static final Uri CONTENT_URI = Uri.parse("content://" + + public static final Uri CONTENT_URI = Uri.parse("content://" + ProviderConfig.AUTHORITY + "/" + TABLE_NAME); /** @@ -321,11 +323,29 @@ 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"; - // Extra for set_boolean method to also notify the backup manager of the change. public static final String NOTIFY_BACKUP = "notify_backup"; + + 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 30cae3114..54945be3d 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,18 +165,17 @@ public class LauncherStateTransitionAnimation { final boolean animated) { final WidgetsContainerView toView = mLauncher.getWidgetsView(); final View buttonView = mLauncher.getWidgetsButton(); - mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState, Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, animated, new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS)); } /** - * 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) { @@ -182,10 +183,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); } } @@ -214,16 +219,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( - toWorkspaceState, animated ? revealDuration : 0, animation); - - Animator updateTransitionStepAnim = dispatchOnLauncherTransitionStepAnim(fromView, toView); + playCommonTransitionAnimations(toWorkspaceState, fromView, toView, + animated, initialized, animation, revealDuration, layerViews); final View contentView = toView.getContentView(); @@ -257,11 +254,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); @@ -328,13 +325,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); @@ -354,7 +344,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(); } } @@ -394,6 +384,34 @@ public class LauncherStateTransitionAnimation { } /** + * Plays animations used by various transitions. + */ + private void playCommonTransitionAnimations( + Workspace.State toWorkspaceState, View fromView, View toView, + 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 + final SearchDropTargetBar.State toSearchBarState = + toWorkspaceState.getSearchDropTargetBarState(); + mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, + animated ? revealDuration : 0, animation); + + 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. * @@ -412,11 +430,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(); // No alpha anim from all apps PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) { @@ -447,16 +465,16 @@ 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, + mLauncher.getAllAppsButton(), appsView, 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(FINAL_REVEAL_ALPHA_FOR_WIDGETS) { @@ -473,16 +491,96 @@ public class LauncherStateTransitionAnimation { }; mCurrentAnimation = startAnimationToWorkspaceFromOverlay( fromWorkspaceState, toWorkspaceState, - toWorkspacePage, mLauncher.getWidgetsButton(), widgetsView, + mLauncher.getWidgetsButton(), widgetsView, 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(toWorkspaceState, fromWorkspace, 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 BaseContainerView fromView, final boolean animated, final Runnable onCompleteRunnable, final PrivateTransitionCallbacks pCb) { @@ -503,24 +601,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( - toWorkspaceState, animated ? revealDuration : 0, animation); - - Animator updateTransitionStepAnim = dispatchOnLauncherTransitionStepAnim(fromView, toView); + playCommonTransitionAnimations(toWorkspaceState, fromView, toView, + animated, initialized, animation, revealDuration, layerViews); if (animated && initialized) { - // Play the workspace animation - if (workspaceAnim != null) { - animation.play(workspaceAnim); - } - - animation.play(updateTransitionStepAnim); final View revealView = fromView.getRevealView(); final View contentView = fromView.getContentView(); @@ -653,6 +737,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 @@ -667,7 +752,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(); } } @@ -677,7 +762,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); @@ -697,16 +782,6 @@ public class LauncherStateTransitionAnimation { } /** - * Coordinates the workspace search bar animation along with the launcher state animation. - */ - private void startWorkspaceSearchBarAnimation( - final Workspace.State toWorkspaceState, int duration, AnimatorSet animation) { - final SearchDropTargetBar.State toSearchBarState = - toWorkspaceState.getSearchDropTargetBarState(); - mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, duration, animation); - } - - /** * Dispatches the prepare-transition event to suitable views. */ void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) { diff --git a/src/com/android/launcher3/PageIndicator.java b/src/com/android/launcher3/PageIndicator.java index 62ea03bcc..8adbf8d01 100644 --- a/src/com/android/launcher3/PageIndicator.java +++ b/src/com/android/launcher3/PageIndicator.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.LayoutInflater; +import android.view.ViewDebug; import android.widget.LinearLayout; import java.util.ArrayList; @@ -37,6 +38,7 @@ public class PageIndicator extends LinearLayout { private ArrayList<PageIndicatorMarker> mMarkers = new ArrayList<PageIndicatorMarker>(); + @ViewDebug.ExportedProperty(category = "launcher") private int mActiveMarkerIndex; public static class PageMarkerResources { diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 2dcff35b5..c826c5fcd 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; @@ -42,16 +42,15 @@ import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; +import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewParent; 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; /** @@ -66,9 +65,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. @@ -92,19 +90,17 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected int mMinFlingVelocity; protected int mMinSnapVelocity; - protected float mDensity; - protected float mSmoothingTime; - protected float mTouchX; - protected boolean mFirstLayout = true; private int mNormalChildHeight; + @ViewDebug.ExportedProperty(category = "launcher") protected int mCurrentPage; protected int mRestorePage = INVALID_RESTORE_PAGE; - protected int mChildCountOnLastLayout; + private int mChildCountOnLastLayout; + @ViewDebug.ExportedProperty(category = "launcher") protected int mNextPage = INVALID_PAGE; - protected int mMaxScrollX; + private int mMaxScrollX; protected LauncherScroller mScroller; private Interpolator mDefaultInterpolator; private VelocityTracker mVelocityTracker; @@ -116,10 +112,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,15 +129,12 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected final static int TOUCH_STATE_REORDERING = 4; 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 boolean mCenterPagesVertically; protected boolean mAllowOverScroll = true; protected int[] mTempVisiblePagesRange = new int[2]; @@ -163,6 +156,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc @Thunk PageIndicator mPageIndicator; // The viewport whether the pages are to be contained (the actual view may be larger than the // viewport) + @ViewDebug.ExportedProperty(category = "launcher") private Rect mViewport = new Rect(); // Reordering @@ -174,7 +168,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() @@ -183,7 +177,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; @@ -218,11 +212,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(); @@ -238,16 +227,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); } @@ -355,7 +343,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc int getViewportWidth() { return mViewport.width(); } - int getViewportHeight() { + public int getViewportHeight() { return mViewport.height(); } @@ -388,7 +376,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; @@ -397,11 +388,11 @@ 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; } - int getPageCount() { + public int getPageCount() { return getChildCount(); } @@ -454,7 +445,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; } @@ -601,9 +592,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); @@ -831,6 +819,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) { @@ -869,9 +858,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(); @@ -1056,13 +1043,12 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc range[1] = -1; if (pageCount > 0) { + int lastVisiblePageIndex = 0; final int visibleLeft = -getLeft(); final int visibleRight = visibleLeft + getViewportWidth(); - int curScreen = 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); // Verify if the page bounds are within the visible range. sTmpRectF.left = 0; @@ -1078,14 +1064,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; @@ -1449,8 +1434,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(); } @@ -1649,8 +1632,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; @@ -1710,16 +1691,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); } @@ -2041,7 +2020,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); } - protected void snapToPageImmediately(int whichPage) { + public void snapToPageImmediately(int whichPage) { snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true, null); } @@ -2111,20 +2090,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc if (getNextPage() < getChildCount() -1) snapToPage(getNextPage() + 1); } - public int getPageForView(View v) { - int result = -1; - if (v != null) { - ViewParent vp = v.getParent(); - int count = getChildCount(); - for (int i = 0; i < count; i++) { - if (vp == getPageAt(i)) { - return i; - } - } - } - return result; - } - @Override public boolean performLongClick() { mCancelTap = true; @@ -2164,13 +2129,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) { @@ -2267,9 +2231,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) { @@ -2329,7 +2292,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/PreloadIconDrawable.java b/src/com/android/launcher3/PreloadIconDrawable.java index a31ae0bb0..a9acf087c 100644 --- a/src/com/android/launcher3/PreloadIconDrawable.java +++ b/src/com/android/launcher3/PreloadIconDrawable.java @@ -12,7 +12,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; -class PreloadIconDrawable extends Drawable { +public class PreloadIconDrawable extends Drawable { private static final float ANIMATION_PROGRESS_STOPPED = -1.0f; private static final float ANIMATION_PROGRESS_STARTED = 0f; @@ -30,7 +30,7 @@ class PreloadIconDrawable extends Drawable { private boolean mIndicatorRectDirty; private final Paint mPaint; - final Drawable mIcon; + public final Drawable mIcon; private Drawable mBgDrawable; private int mRingOutset; diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java index f7288a0a0..171dd8771 100644 --- a/src/com/android/launcher3/SearchDropTargetBar.java +++ b/src/com/android/launcher3/SearchDropTargetBar.java @@ -24,25 +24,23 @@ import android.animation.TimeInterpolator; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; -import android.util.Log; import android.view.View; +import android.view.ViewDebug; import android.view.accessibility.AccessibilityManager; -import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; -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 { private static final TimeInterpolator MOVE_DOWN_INTERPOLATOR = new DecelerateInterpolator(0.6f); private static final TimeInterpolator MOVE_UP_INTERPOLATOR = new DecelerateInterpolator(1.5f); - private static final TimeInterpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator(); /** The different states that the search bar space can be in. */ public enum State { @@ -63,18 +61,12 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D } - private static int DEFAULT_DRAG_FADE_DURATION = 175; - - private AnimatorSet mCurrentAnimation; + @ViewDebug.ExportedProperty(category = "launcher") 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; @@ -86,40 +78,42 @@ 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(); - - // 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); + public void showDropTargets() { + animateToState(State.DROP_TARGET, DEFAULT_DRAG_FADE_DURATION); + } - // Create the various fade animations - mDropTargetBar.setAlpha(0f); - AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled); + @Override + public void hideDropTargets() { + animateToState(State.SEARCH_BAR, DEFAULT_DRAG_FADE_DURATION); } public void setQsbSearchBar(View qsb) { @@ -138,21 +132,8 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D if (mState != newState) { mState = newState; - // Update the accessibility state - AccessibilityManager am = (AccessibilityManager) - getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); - mAccessibilityEnabled = am.isEnabled(); - - if (mCurrentAnimation != null) { - mCurrentAnimation.cancel(); - mCurrentAnimation = null; - } - mCurrentAnimation = null; - + resetAnimation(duration); if (duration > 0) { - mCurrentAnimation = new AnimatorSet(); - mCurrentAnimation.setDuration(duration); - animateAlpha(mDropTargetBar, mState.mDropTargetBarAlpha, DEFAULT_INTERPOLATOR); } else { mDropTargetBar.setAlpha(mState.mDropTargetBarAlpha); @@ -194,40 +175,6 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D } } - private void animateAlpha(View v, float alpha, TimeInterpolator interpolator) { - if (Float.compare(v.getAlpha(), alpha) != 0) { - ObjectAnimator anim = ObjectAnimator.ofFloat(v, View.ALPHA, alpha); - anim.setInterpolator(interpolator); - anim.addListener(new ViewVisiblilyUpdateHandler(v)); - mCurrentAnimation.play(anim); - } - } - - /* - * 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. */ @@ -247,32 +194,12 @@ 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); } - - private class ViewVisiblilyUpdateHandler extends AnimatorListenerAdapter { - private final View mView; - - ViewVisiblilyUpdateHandler(View v) { - mView = v; - } - - @Override - public void onAnimationStart(Animator animation) { - // Ensure that the view is visible for the animation - mView.setVisibility(View.VISIBLE); - } - - @Override - public void onAnimationEnd(Animator animation){ - AlphaUpdateListener.updateVisibility(mView, mAccessibilityEnabled); - } - - } } diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java index 21e72e99a..008dd847e 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(); @@ -236,7 +214,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..128d695eb 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -27,9 +27,9 @@ import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.folder.FolderIcon; import java.util.ArrayList; -import java.util.Arrays; /** * Represents a launchable icon on the workspaces and in folders. @@ -71,7 +71,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) @@ -113,6 +113,16 @@ public class ShortcutInfo extends ItemInfo { public static final int FLAG_DISABLED_NOT_AVAILABLE = 2; /** + * Indicates that the icon is disabled as the app is suspended + */ + public static final int FLAG_DISABLED_SUSPENDED = 4; + + /** + * Indicates that the icon is disabled as the user is in quiet mode. + */ + public static final int FLAG_DISABLED_QUIET_USER = 8; + + /** * Could be disabled, if the the app is installed but unavailable (eg. in safe mode or when * sd-card is not available). */ @@ -178,6 +188,7 @@ public class ShortcutInfo extends ItemInfo { intent = new Intent(info.intent); customIcon = false; flags = info.flags; + isDisabled = info.isDisabled; } public void setIcon(Bitmap b) { @@ -238,16 +249,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() { @@ -288,5 +290,10 @@ public class ShortcutInfo extends ItemInfo { shortcut.flags = AppInfo.initFlags(info); return shortcut; } + + @Override + public boolean isDisabled() { + return isDisabled != 0; + } } 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..287382869 100644 --- a/src/com/android/launcher3/Stats.java +++ b/src/com/android/launcher3/Stats.java @@ -25,6 +25,9 @@ import android.util.Log; import android.view.View; import android.view.ViewParent; +import com.android.launcher3.config.ProviderConfig; +import com.android.launcher3.userevent.Logger; + public class Stats { /** @@ -71,7 +74,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"); } } @@ -143,5 +146,6 @@ public class Stats { LaunchSourceUtils.populateSourceDataFromAncestorProvider(v, sourceExtras); broadcastIntent.putExtra(EXTRA_SOURCE, sourceExtras); mLauncher.sendBroadcast(broadcastIntent, mLaunchBroadcastPermission); + mLauncher.getLogger().logAppLaunch(intent.getComponent().getPackageName(), shortcut, sourceExtras); } } 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 87c9262d7..57b583b32 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -62,10 +62,12 @@ import android.widget.Toast; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.config.FeatureFlags; +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; @@ -140,11 +142,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; } @@ -152,6 +152,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 { @@ -399,15 +411,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); @@ -428,11 +431,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( @@ -575,16 +585,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. @@ -720,7 +720,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(); } @@ -770,6 +770,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/WallpaperPickerActivity.java b/src/com/android/launcher3/WallpaperPickerActivity.java new file mode 100644 index 000000000..62b9be43e --- /dev/null +++ b/src/com/android/launcher3/WallpaperPickerActivity.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2016 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.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.os.Bundle; +import android.util.Pair; + +import com.android.wallpaperpicker.tileinfo.DefaultWallpaperInfo; +import com.android.wallpaperpicker.tileinfo.FileWallpaperInfo; +import com.android.wallpaperpicker.tileinfo.WallpaperTileInfo; + +import java.io.File; +import java.util.ArrayList; + +public class WallpaperPickerActivity extends com.android.wallpaperpicker.WallpaperPickerActivity { + + @Override + public void startActivityForResultSafely(Intent intent, int requestCode) { + Utilities.startActivityForResultSafely(this, intent, requestCode); + } + + @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); + } + + @Override + public ArrayList<WallpaperTileInfo> findBundledWallpapers() { + final PackageManager pm = getPackageManager(); + final ArrayList<WallpaperTileInfo> bundled = new ArrayList<WallpaperTileInfo>(24); + + Partner partner = Partner.get(pm); + if (partner != null) { + final Resources partnerRes = partner.getResources(); + final int resId = partnerRes.getIdentifier(Partner.RES_WALLPAPERS, "array", + partner.getPackageName()); + if (resId != 0) { + addWallpapers(bundled, partnerRes, partner.getPackageName(), resId); + } + + // Add system wallpapers + File systemDir = partner.getWallpaperDirectory(); + if (systemDir != null && systemDir.isDirectory()) { + for (File file : systemDir.listFiles()) { + if (!file.isFile()) { + continue; + } + String name = file.getName(); + int dotPos = name.lastIndexOf('.'); + String extension = ""; + if (dotPos >= -1) { + extension = name.substring(dotPos); + name = name.substring(0, dotPos); + } + + if (name.endsWith("_small")) { + // it is a thumbnail + continue; + } + + File thumbnail = new File(systemDir, name + "_small" + extension); + Bitmap thumb = BitmapFactory.decodeFile(thumbnail.getAbsolutePath()); + if (thumb != null) { + bundled.add(new FileWallpaperInfo( + file, new BitmapDrawable(getResources(), thumb))); + } + } + } + } + + Pair<ApplicationInfo, Integer> r = getWallpaperArrayResourceId(); + if (r != null) { + try { + Resources wallpaperRes = pm.getResourcesForApplication(r.first); + addWallpapers(bundled, wallpaperRes, r.first.packageName, r.second); + } catch (PackageManager.NameNotFoundException e) { + } + } + + if (partner == null || !partner.hideDefaultWallpaper()) { + // Add an entry for the default wallpaper (stored in system resources) + WallpaperTileInfo defaultWallpaperInfo = DefaultWallpaperInfo.get(this); + if (defaultWallpaperInfo != null) { + bundled.add(0, defaultWallpaperInfo); + } + } + return bundled; + } +} diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 7b873d420..840f9f2fa 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; @@ -49,31 +46,37 @@ import android.os.Parcelable; 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.ViewDebug; import android.view.ViewGroup; import android.view.accessibility.AccessibilityManager; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.Interpolator; import android.widget.TextView; -import com.android.launcher3.FolderIcon.FolderRingAnimator; +import com.android.launcher3.folder.Folder; +import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.folder.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.FeatureFlags; +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.WallpaperUtils; +import com.android.launcher3.util.WallpaperOffsetInterpolator; import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; +import com.android.wallpaperpicker.WallpaperUtils; import java.util.ArrayList; import java.util.HashMap; @@ -93,13 +96,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 +114,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 +136,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 +161,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; @@ -197,11 +192,11 @@ public class Workspace extends PagedView } }; + @ViewDebug.ExportedProperty(category = "launcher") private State mState = State.NORMAL; private boolean mIsSwitchingState = false; boolean mAnimatingViewIntoPlace = false; - boolean mIsDragOccuring = false; boolean mChildrenLayersEnabled = true; private boolean mStripScreensOnPageStopMoving = false; @@ -211,20 +206,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 +266,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 +303,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); @@ -381,12 +361,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(); @@ -417,7 +396,6 @@ public class Workspace extends PagedView removeExtraEmptyScreen(true, mDragSourceInternal != null); } - mIsDragOccuring = false; updateChildrenLayersEnabled(false); mLauncher.unlockScreenOrientation(false); @@ -432,7 +410,7 @@ public class Workspace extends PagedView * Initializes various states for this workspace. */ protected void initWorkspace() { - mCurrentPage = mDefaultPage; + mCurrentPage = getDefaultPage(); LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = mLauncher.getDeviceProfile(); mIconCache = app.getIconCache(); @@ -444,10 +422,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 @@ -456,6 +430,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(); @@ -588,9 +566,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; @@ -616,9 +591,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; @@ -697,7 +669,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; } @@ -730,7 +701,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; } @@ -816,7 +786,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; } @@ -825,7 +794,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); @@ -841,8 +812,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) { @@ -872,7 +842,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; } @@ -941,7 +910,7 @@ public class Workspace extends PagedView // At bind time, we use the rank (screenId) to compute x and y for hotseat items. // See implementation for parameter definition. - void addInScreenFromBind(View child, long container, long screenId, int x, int y, + public void addInScreenFromBind(View child, long container, long screenId, int x, int y, int spanX, int spanY) { addInScreen(child, container, screenId, x, y, spanX, spanY, false, true); } @@ -1032,7 +1001,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)) { @@ -1220,7 +1189,7 @@ public class Workspace extends PagedView } } - if (mDelayedResizeRunnable != null) { + if (mDelayedResizeRunnable != null && !mIsSwitchingState) { mDelayedResizeRunnable.run(); mDelayedResizeRunnable = null; } @@ -1323,12 +1292,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); @@ -1354,191 +1323,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(); @@ -1566,18 +1350,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() && @@ -1609,6 +1381,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); } @@ -1680,13 +1453,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 @@ -1698,14 +1470,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() { @@ -1723,10 +1496,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 @@ -1739,14 +1509,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(); @@ -1872,8 +1634,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(); @@ -1937,6 +1709,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 @@ -1992,6 +1776,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())); @@ -2007,19 +1795,35 @@ public class Workspace extends PagedView return -workspaceOffsetTopEdge + overviewOffsetTopEdge; } + int getSpringLoadedTranslationY() { + DeviceProfile grid = mLauncher.getDeviceProfile(); + Rect workspacePadding = grid.getWorkspacePadding(Utilities.isRtl(getResources())); + int scaledHeight = (int) (mSpringLoadedShrinkFactor * getNormalChildHeight()); + int workspaceTop = mInsets.top + workspacePadding.top; + int workspaceBottom = getViewportHeight() - mInsets.bottom - workspacePadding.bottom; + int workspaceHeight = workspaceBottom - workspaceTop; + // Center the spring-loaded pages by translating it up by half of the reduced height. + return -(workspaceHeight - scaledHeight) / 2; + } + /** * 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; } @@ -2093,6 +1897,7 @@ public class Workspace extends PagedView mIsSwitchingState = false; updateChildrenLayersEnabled(false); showCustomContentIfNecessary(); + mForceDrawAdjacentPages = false; } void updateCustomContentVisibility() { @@ -2139,19 +1944,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). @@ -2329,7 +2132,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(); @@ -2340,11 +2144,16 @@ 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(); + + if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) { + mLauncher.enterSpringLoadedDragMode(); + } } public void beginExternalDragShared(View child, DragSource source) { @@ -2377,7 +2186,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(); @@ -2385,12 +2195,17 @@ 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(); + + if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) { + mLauncher.enterSpringLoadedDragMode(); + } } public boolean transitionStateShouldAllowDrop() { @@ -2417,7 +2232,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; @@ -2427,9 +2242,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; @@ -2444,12 +2258,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; } @@ -2518,14 +2332,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.cellY)) { @@ -2630,11 +2444,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], @@ -2643,7 +2457,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); @@ -2677,7 +2490,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) { @@ -2715,7 +2528,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], @@ -2740,21 +2553,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); } } - }); + }; } } @@ -2771,7 +2577,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() { @@ -2779,9 +2584,6 @@ public class Workspace extends PagedView public void run() { mAnimatingViewIntoPlace = false; updateChildrenLayersEnabled(false); - if (finalResizeRunnable != null) { - finalResizeRunnable.run(); - } } }; mAnimatingViewIntoPlace = true; @@ -2795,9 +2597,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); } @@ -2831,19 +2631,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,50 +2645,11 @@ public class Workspace extends PagedView setCurrentDropLayout(layout); setCurrentDragOverlappingLayout(layout); - if (!workspaceInModalState()) { + if (!workspaceInModalState() && FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) { 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 public void onDragExit(DragObject d) { if (ENFORCE_DRAG_EVENT_ORDER) { @@ -3040,41 +2788,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]; } /* @@ -3120,10 +2854,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()) { @@ -3165,11 +2896,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; @@ -3183,7 +2913,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(); } } @@ -3206,7 +2936,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(); } } @@ -3225,7 +2955,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; @@ -3286,7 +3016,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()) { @@ -3407,24 +3137,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. @@ -3432,7 +3144,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 @@ -3442,7 +3154,7 @@ public class Workspace extends PagedView } }; - ItemInfo info = (ItemInfo) dragInfo; + ItemInfo info = dragInfo; int spanX = info.spanX; int spanY = info.spanY; if (mDragInfo != null) { @@ -3469,14 +3181,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; @@ -3789,7 +3501,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. "); }; @@ -3800,6 +3512,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; + } } /** @@ -3809,7 +3528,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 @@ -3836,70 +3555,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; @@ -3912,7 +3567,7 @@ public class Workspace extends PagedView @Override public boolean supportsAppInfoDropTarget() { - return false; + return !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND; } @Override @@ -4078,8 +3733,7 @@ public class Workspace extends PagedView * the hotseat and workspace pages */ ArrayList<ShortcutAndWidgetContainer> getAllShortcutAndWidgetContainers() { - ArrayList<ShortcutAndWidgetContainer> childrenLayouts = - new ArrayList<ShortcutAndWidgetContainer>(); + ArrayList<ShortcutAndWidgetContainer> childrenLayouts = new ArrayList<>(); int screenCount = getChildCount(); for (int screen = 0; screen < screenCount; screen++) { childrenLayouts.add(((CellLayout) getChildAt(screen)).getShortcutsAndWidgets()); @@ -4090,17 +3744,6 @@ public class Workspace extends PagedView return childrenLayouts; } - public Folder getFolderForTag(final Object tag) { - return (Folder) getFirstMatch(new ItemOperator() { - - @Override - public boolean evaluate(ItemInfo info, View v, View parent) { - return (v instanceof Folder) && (((Folder) v).getInfo() == tag) - && ((Folder) v).getInfo().opened; - } - }); - } - public View getHomescreenIconByItemId(final long id) { return getFirstMatch(new ItemOperator() { @@ -4301,7 +3944,7 @@ public class Workspace extends PagedView stripEmptyScreens(); } - interface ItemOperator { + public interface ItemOperator { /** * Process the next itemInfo, possibly with side-effect on {@link ItemOperator#value}. * @@ -4437,7 +4080,7 @@ public class Workspace extends PagedView } void moveToDefaultScreen(boolean animate) { - moveToScreen(mDefaultPage, animate); + moveToScreen(getDefaultPage(), animate); } void moveToCustomContentScreen(boolean animate) { @@ -4493,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..c0eb7eda3 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; @@ -198,6 +198,7 @@ public class WorkspaceStateTransitionAnimation { @Thunk int mAllAppsTransitionTime; @Thunk int mOverviewTransitionTime; @Thunk int mOverlayTransitionTime; + @Thunk int mSpringLoadedTransitionTime; @Thunk boolean mWorkspaceFadeInAdjacentScreens; public WorkspaceStateTransitionAnimation(Launcher launcher, Workspace workspace) { @@ -209,6 +210,7 @@ public class WorkspaceStateTransitionAnimation { mAllAppsTransitionTime = res.getInteger(R.integer.config_allAppsTransitionTime); mOverviewTransitionTime = res.getInteger(R.integer.config_overviewTransitionTime); mOverlayTransitionTime = res.getInteger(R.integer.config_overlayTransitionTime); + mSpringLoadedTransitionTime = mOverlayTransitionTime / 2; mSpringLoadedShrinkFactor = res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100f; mOverviewModeShrinkFactor = @@ -217,14 +219,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; @@ -255,6 +261,9 @@ public class WorkspaceStateTransitionAnimation { return mAllAppsTransitionTime; } else if (states.workspaceToOverview || states.overviewToWorkspace) { return mOverviewTransitionTime; + } else if (mLauncher.mState == Launcher.State.WORKSPACE_SPRING_LOADED + || states.oldStateIsNormal && states.stateIsSpringLoaded) { + return mSpringLoadedTransitionTime; } else { return mOverlayTransitionTime; } @@ -263,7 +272,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 @@ -278,11 +287,16 @@ public class WorkspaceStateTransitionAnimation { // Update the workspace state float finalBackgroundAlpha = (states.stateIsSpringLoaded || states.stateIsOverview) ? 1.0f : 0f; - float finalHotseatAndPageIndicatorAlpha = (states.stateIsNormal || states.stateIsSpringLoaded) ? - 1f : 0f; + float finalHotseatAlpha = (states.stateIsNormal || states.stateIsSpringLoaded) ? 1f : 0f; + float finalPageIndicatorAlpha = states.stateIsNormal ? 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 +317,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 +389,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); @@ -389,7 +398,7 @@ public class WorkspaceStateTransitionAnimation { Animator pageIndicatorAlpha; if (pageIndicator != null) { pageIndicatorAlpha = new LauncherViewPropertyAnimator(pageIndicator) - .alpha(finalHotseatAndPageIndicatorAlpha).withLayer(); + .alpha(finalPageIndicatorAlpha).withLayer(); pageIndicatorAlpha.addListener(new AlphaUpdateListener(pageIndicator, accessibilityEnabled)); } else { @@ -398,7 +407,7 @@ public class WorkspaceStateTransitionAnimation { } LauncherViewPropertyAnimator hotseatAlpha = new LauncherViewPropertyAnimator(hotseat) - .alpha(finalHotseatAndPageIndicatorAlpha); + .alpha(finalHotseatAlpha); hotseatAlpha.addListener(new AlphaUpdateListener(hotseat, accessibilityEnabled)); LauncherViewPropertyAnimator overviewPanelAlpha = @@ -452,10 +461,10 @@ public class WorkspaceStateTransitionAnimation { } else { overviewPanel.setAlpha(finalOverviewPanelAlpha); AlphaUpdateListener.updateVisibility(overviewPanel, accessibilityEnabled); - hotseat.setAlpha(finalHotseatAndPageIndicatorAlpha); + hotseat.setAlpha(finalHotseatAlpha); AlphaUpdateListener.updateVisibility(hotseat, accessibilityEnabled); if (pageIndicator != null) { - pageIndicator.setAlpha(finalHotseatAndPageIndicatorAlpha); + pageIndicator.setAlpha(finalPageIndicatorAlpha); AlphaUpdateListener.updateVisibility(pageIndicator, accessibilityEnabled); } mWorkspace.updateCustomContentVisibility(); @@ -488,8 +497,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/FolderAccessibilityHelper.java b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java index ff9989036..d271f1d4e 100644 --- a/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java +++ b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java @@ -17,7 +17,7 @@ package com.android.launcher3.accessibility; import com.android.launcher3.CellLayout; -import com.android.launcher3.FolderPagedView; +import com.android.launcher3.folder.FolderPagedView; import com.android.launcher3.R; /** diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index 2306b776e..8560b2167 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -20,9 +20,8 @@ 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.folder.Folder; import com.android.launcher3.FolderInfo; import com.android.launcher3.InfoDropTarget; import com.android.launcher3.ItemInfo; @@ -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 57da6c57c..013bd8ccc 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -40,13 +40,14 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget; import com.android.launcher3.ExtendedEditText; -import com.android.launcher3.Folder; +import com.android.launcher3.folder.Folder; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherTransitionable; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; +import com.android.launcher3.userevent.Logger; import com.android.launcher3.util.ComponentKey; import java.nio.charset.Charset; @@ -312,6 +313,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc icon.getMeasuredHeight()); updateBackgroundAndPaddings(); + mLauncher.getLogger().resetElapsedContainerMillis(); } @Override @@ -501,7 +503,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 cfaf19515..d9313f8ec 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/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 26e923152..9d2fe54db 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; } @@ -422,7 +422,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/compat/LauncherActivityInfoCompat.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java index 0bc9588aa..aaf756eda 100644 --- a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java +++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java @@ -24,6 +24,8 @@ import android.graphics.drawable.Drawable; public abstract class LauncherActivityInfoCompat { + public static final int FLAG_SUSPENDED = 1<<30; + LauncherActivityInfoCompat() { } diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java index 95e3ba902..db5b89e81 100644 --- a/src/com/android/launcher3/compat/LauncherAppsCompat.java +++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java @@ -35,6 +35,8 @@ public abstract class LauncherAppsCompat { "android.intent.action.MANAGED_PROFILE_ADDED"; public static final String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED"; + public static final String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = + "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED"; public interface OnAppsChangedCallbackCompat { void onPackageRemoved(String packageName, UserHandleCompat user); @@ -42,6 +44,8 @@ public abstract class LauncherAppsCompat { void onPackageChanged(String packageName, UserHandleCompat user); void onPackagesAvailable(String[] packageNames, UserHandleCompat user, boolean replacing); void onPackagesUnavailable(String[] packageNames, UserHandleCompat user, boolean replacing); + void onPackagesSuspended(String[] packageNames, UserHandleCompat user); + void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user); } protected LauncherAppsCompat() { @@ -53,7 +57,9 @@ public abstract class LauncherAppsCompat { public static LauncherAppsCompat getInstance(Context context) { synchronized (sInstanceLock) { if (sInstance == null) { - if (Utilities.ATLEAST_LOLLIPOP) { + if (Utilities.isNycOrAbove()) { + sInstance = new LauncherAppsCompatVN(context.getApplicationContext()); + } else if (Utilities.ATLEAST_LOLLIPOP) { sInstance = new LauncherAppsCompatVL(context.getApplicationContext()); } else { sInstance = new LauncherAppsCompatV16(context.getApplicationContext()); @@ -75,6 +81,7 @@ public abstract class LauncherAppsCompat { public abstract boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user); public abstract boolean isActivityEnabledForProfile(ComponentName component, UserHandleCompat user); + public abstract boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user); public boolean isAppEnabled(PackageManager pm, String packageName, int flags) { try { @@ -84,4 +91,4 @@ public abstract class LauncherAppsCompat { return false; } } -}
\ No newline at end of file +} diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java index 339c457e1..2d0778d30 100644 --- a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java +++ b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java @@ -126,6 +126,10 @@ public class LauncherAppsCompatV16 extends LauncherAppsCompat { } } + public boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user) { + return false; + } + private void unregisterForPackageIntents() { mContext.unregisterReceiver(mPackageMonitor); } diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java index fbf91b548..7270d023b 100644 --- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java +++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java @@ -36,7 +36,7 @@ import java.util.Map; @TargetApi(Build.VERSION_CODES.LOLLIPOP) public class LauncherAppsCompatVL extends LauncherAppsCompat { - private LauncherApps mLauncherApps; + protected LauncherApps mLauncherApps; private Map<OnAppsChangedCallbackCompat, WrappedCallback> mCallbacks = new HashMap<OnAppsChangedCallbackCompat, WrappedCallback>(); @@ -106,6 +106,10 @@ public class LauncherAppsCompatVL extends LauncherAppsCompat { return mLauncherApps.isActivityEnabled(component, user.getUser()); } + public boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user) { + return false; + } + private static class WrappedCallback extends LauncherApps.Callback { private LauncherAppsCompat.OnAppsChangedCallbackCompat mCallback; @@ -134,6 +138,14 @@ public class LauncherAppsCompatVL extends LauncherAppsCompat { mCallback.onPackagesUnavailable(packageNames, UserHandleCompat.fromUser(user), replacing); } + + public void onPackagesSuspended(String[] packageNames, UserHandle user) { + mCallback.onPackagesSuspended(packageNames, UserHandleCompat.fromUser(user)); + } + + public void onPackagesUnsuspended(String[] packageNames, UserHandle user) { + mCallback.onPackagesUnsuspended(packageNames, UserHandleCompat.fromUser(user)); + } } } diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVN.java b/src/com/android/launcher3/compat/LauncherAppsCompatVN.java new file mode 100644 index 000000000..0d883b6fd --- /dev/null +++ b/src/com/android/launcher3/compat/LauncherAppsCompatVN.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 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.compat; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.LauncherApps; +import android.os.UserHandle; +import android.util.Log; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +//TODO: Once gogole3 SDK is updated to N, add @TargetApi(Build.VERSION_CODES.N) +public class LauncherAppsCompatVN extends LauncherAppsCompatVL { + + private static final String TAG = "LauncherAppsCompatVN"; + + LauncherAppsCompatVN(Context context) { + super(context); + } + + @Override + public boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user) { + if (user != null && packageName != null) { + try { + //TODO: Replace with proper API call once google3 SDK is updated. + Method getApplicationInfoMethod = LauncherApps.class.getMethod("getApplicationInfo", + String.class, int.class, UserHandle.class); + + ApplicationInfo info = (ApplicationInfo) getApplicationInfoMethod.invoke( + mLauncherApps, packageName, 0, user.getUser()); + if (info != null) { + return (info.flags & LauncherActivityInfoCompat.FLAG_SUSPENDED) != 0; + } + } catch (NoSuchMethodError | NoSuchMethodException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException e) { + Log.e(TAG, "Running on N without getApplicationInfo", e); + } + } + return false; + } +} diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java index 6b7cba840..978f9229d 100644 --- a/src/com/android/launcher3/compat/UserManagerCompat.java +++ b/src/com/android/launcher3/compat/UserManagerCompat.java @@ -32,7 +32,9 @@ public abstract class UserManagerCompat { public static UserManagerCompat getInstance(Context context) { synchronized (sInstanceLock) { if (sInstance == null) { - if (Utilities.ATLEAST_LOLLIPOP) { + if (Utilities.isNycOrAbove()) { + sInstance = new UserManagerCompatVN(context.getApplicationContext()); + } else if (Utilities.ATLEAST_LOLLIPOP) { sInstance = new UserManagerCompatVL(context.getApplicationContext()); } else if (Utilities.ATLEAST_JB_MR1) { sInstance = new UserManagerCompatV17(context.getApplicationContext()); @@ -54,4 +56,5 @@ public abstract class UserManagerCompat { public abstract UserHandleCompat getUserForSerialNumber(long serialNumber); public abstract CharSequence getBadgedLabelForUser(CharSequence label, UserHandleCompat user); public abstract long getUserCreationTime(UserHandleCompat user); + public abstract boolean isQuietModeEnabled(UserHandleCompat user); } diff --git a/src/com/android/launcher3/compat/UserManagerCompatV16.java b/src/com/android/launcher3/compat/UserManagerCompatV16.java index fcd755521..a006efd50 100644 --- a/src/com/android/launcher3/compat/UserManagerCompatV16.java +++ b/src/com/android/launcher3/compat/UserManagerCompatV16.java @@ -50,4 +50,9 @@ public class UserManagerCompatV16 extends UserManagerCompat { @Override public void enableAndResetCache() { } + + @Override + public boolean isQuietModeEnabled(UserHandleCompat user) { + return false; + } } diff --git a/src/com/android/launcher3/compat/UserManagerCompatVN.java b/src/com/android/launcher3/compat/UserManagerCompatVN.java new file mode 100644 index 000000000..ae41e68a3 --- /dev/null +++ b/src/com/android/launcher3/compat/UserManagerCompatVN.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 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.compat; + +import android.content.Context; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.Log; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +//TODO: Once gogole3 SDK is updated to N, add @TargetApi(Build.VERSION_CODES.N) +public class UserManagerCompatVN extends UserManagerCompatVL { + + private static final String TAG = "UserManagerCompatVN"; + + UserManagerCompatVN(Context context) { + super(context); + } + + @Override + public boolean isQuietModeEnabled(UserHandleCompat user) { + if (user != null) { + try { + //TODO: Replace with proper API call once google3 SDK is updated. + Method isQuietModeEnabledMethod = UserManager.class.getMethod("isQuietModeEnabled", + UserHandle.class); + return (boolean) isQuietModeEnabledMethod.invoke(mUserManager, user.getUser()); + } catch (NoSuchMethodError | NoSuchMethodException | IllegalAccessException + | InvocationTargetException e) { + Log.e(TAG, "Running on N without isQuietModeEnabled", e); + } catch (IllegalArgumentException e) { + // TODO remove this when API is fixed to not throw this + // when called on user that isn't a managed profile. + } + } + return false; + } +} + diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 50127085a..1988c2d81 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -24,7 +24,6 @@ package com.android.launcher3.config; * Use LAUNCHER3_ prefix for prevent namespace conflicts. */ public final class FeatureFlags { - private FeatureFlags() {} public static boolean IS_DEV_BUILD = false; @@ -32,6 +31,9 @@ public final class FeatureFlags { public static boolean IS_RELEASE_BUILD = true; // Custom flags go below this - public static boolean LAUNCHER3_ICON_NORMALIZATION = false; - + // As opposed to the new spring-loaded workspace. + public static boolean LAUNCHER3_LEGACY_WORKSPACE_DND = false; + public static boolean LAUNCHER3_ICON_NORMALIZATION = true; + public static boolean LAUNCHER3_CLIPPED_FOLDER_ICON = false; + public static boolean LAUNCHER3_LEGACY_LOGGING = false; } 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 eefb287cb..80f9e4665 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,24 @@ 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.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.Utilities; +import com.android.launcher3.Workspace; import com.android.launcher3.accessibility.DragViewStateAnnouncer; import com.android.launcher3.util.Thunk; @@ -44,7 +53,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 +70,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; @@ -75,10 +84,13 @@ public class DragController { 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. */ @@ -90,7 +102,7 @@ public class DragController { /** the area at the edge of the screen that makes the workspace go left * or right while you're dragging. */ - private int mScrollZone; + private final int mScrollZone; private DropTarget.DragObject mDragObject; @@ -122,7 +134,7 @@ public class DragController { private int mTmpPoint[] = new int[2]; private Rect mDragLayerRect = new Rect(); - protected int mFlingToDeleteThresholdVelocity; + protected final int mFlingToDeleteThresholdVelocity; private VelocityTracker mVelocityTracker; /** @@ -137,16 +149,18 @@ 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. + * + * @param context The application's context. */ public DragController(Launcher launcher) { Resources r = launcher.getResources(); @@ -155,16 +169,11 @@ public class DragController { mScrollZone = r.getDimensionPixelSize(R.dimen.scroll_zone); mVelocityTracker = VelocityTracker.obtain(); - float density = r.getDisplayMetrics().density; mFlingToDeleteThresholdVelocity = - (int) (r.getInteger(R.integer.config_flingToDeleteMinVelocity) * density); + r.getDimensionPixelSize(R.dimen.drag_flingToDeleteMinVelocity); mIsRtl = Utilities.isRtl(r); } - public boolean dragging() { - return mDragging; - } - /** * Starts a drag. * @@ -172,13 +181,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 +218,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 +241,14 @@ 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(); 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); mDragObject.dragComplete = false; if (mIsAccessibleDrag) { @@ -252,6 +260,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 +281,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 +291,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 +313,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) { @@ -363,8 +336,8 @@ public class DragController { } private void endDrag() { - if (mDragging) { - mDragging = false; + if (isDragging()) { + mDragDriver = null; mIsAccessibleDrag = false; clearScrollRunnable(); boolean isDeferred = false; @@ -415,18 +388,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. */ @@ -434,8 +452,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) { @@ -451,41 +469,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; } @@ -556,7 +572,7 @@ public class DragController { if (mScrollState == SCROLL_OUTSIDE_ZONE) { mScrollState = SCROLL_WAITING_IN_ZONE; if (mDragScroller.onEnterScrollArea(x, y, forwardDirection)) { - dragLayer.onEnterScrollArea(forwardDirection); + dragLayer.onEnterScrollArea(); mScrollRunnable.setDirection(forwardDirection); mHandler.postDelayed(mScrollRunnable, delay); } @@ -565,7 +581,7 @@ public class DragController { if (mScrollState == SCROLL_OUTSIDE_ZONE) { mScrollState = SCROLL_WAITING_IN_ZONE; if (mDragScroller.onEnterScrollArea(x, y, backwardsDirection)) { - dragLayer.onEnterScrollArea(backwardsDirection); + dragLayer.onEnterScrollArea(); mScrollRunnable.setDirection(backwardsDirection); mHandler.postDelayed(mScrollRunnable, delay); } @@ -579,7 +595,7 @@ public class DragController { * Call this from a drag source view. */ public boolean onTouchEvent(MotionEvent ev) { - if (!mDragging || mIsAccessibleDrag) { + if (mDragDriver == null || mIsAccessibleDrag) { return false; } @@ -592,47 +608,25 @@ public class DragController { final int dragLayerY = dragLayerPos[1]; switch (action) { - case MotionEvent.ACTION_DOWN: - // Remember where the motion event started - mMotionDownX = dragLayerX; - mMotionDownY = dragLayerY; + 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); + if ((dragLayerX < mScrollZone) || (dragLayerX > mScrollView.getWidth() - mScrollZone)) { + mScrollState = SCROLL_WAITING_IN_ZONE; + mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY); } else { - drop(dragLayerX, dragLayerY); + mScrollState = SCROLL_OUTSIDE_ZONE; } - } - endDrag(); - break; - case MotionEvent.ACTION_CANCEL: - mHandler.removeCallbacks(mScrollRunnable); - cancelDrag(); - break; + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mHandler.removeCallbacks(mScrollRunnable); + break; } - return true; + return mDragDriver.onTouchEvent(ev); } /** @@ -642,7 +636,6 @@ public class DragController { public void prepareAccessibleDrag(int x, int y) { mMotionDownX = x; mMotionDownY = y; - mLastDropTarget = null; } /** @@ -660,7 +653,7 @@ public class DragController { dropTarget.prepareAccessibilityDrop(); // Perform the drop - drop(location[0], location[1]); + drop(dropTarget, location[0], location[1], null); endDrag(); } @@ -675,64 +668,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) { @@ -825,10 +818,6 @@ public class DragController { mScrollView = v; } - DragView getDragView() { - return mDragObject.dragView; - } - private class ScrollRunnable implements Runnable { private int mDirection; 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 ad9063c25..3128db21e 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,7 +40,21 @@ 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.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.R; +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.folder.Folder; +import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.util.Thunk; import java.util.ArrayList; @@ -163,6 +178,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 +342,7 @@ public class DragLayer extends InsettableFrameLayout { if (isInAccessibleDrag()) { childrenForAccessibility.add(mLauncher.getSearchDropTargetBar()); + childrenForAccessibility.add(mLauncher.getAppInfoDropTargetBar()); } } else { super.addChildrenForAccessibility(childrenForAccessibility); @@ -375,6 +396,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 * @@ -760,7 +786,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) { @@ -870,7 +896,7 @@ public class DragLayer extends InsettableFrameLayout { } } - void onEnterScrollArea(int direction) { + void onEnterScrollArea() { mInScrollArea = true; invalidate(); } @@ -880,7 +906,7 @@ public class DragLayer extends InsettableFrameLayout { invalidate(); } - void showPageHints() { + public void showPageHints() { mShowPageHints = true; Workspace workspace = mLauncher.getWorkspace(); getDescendantRectRelativeToSelf(workspace.getChildAt(workspace.numCustomPages()), @@ -888,7 +914,7 @@ public class DragLayer extends InsettableFrameLayout { invalidate(); } - void hidePageHints() { + public void hidePageHints() { mShowPageHints = false; invalidate(); } 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 a584667ca..b1df41b9d 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,14 @@ 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.config.FeatureFlags; import com.android.launcher3.util.Thunk; +import com.android.launcher3.R; + import java.util.Arrays; public class DragView extends View { @@ -45,19 +54,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 +78,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. @@ -81,10 +88,11 @@ public class DragView extends View { int left, int top, int width, int height, final float initialScale) { super(launcher); mDragLayer = launcher.getDragLayer(); - mInitialScale = initialScale; + mDragController = launcher.getDragController(); final Resources res = getResources(); - final float scaleDps = res.getDimensionPixelSize(R.dimen.dragViewScale); + final float scaleDps = !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ? 0f + : res.getDimensionPixelSize(R.dimen.dragViewScale); final float scale = (width + scaleDps) / width; // Set the initial scale to avoid any jumps @@ -99,11 +107,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 +115,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 +154,6 @@ public class DragView extends View { return mIntrinsicIconScale; } - public float getOffsetY() { - return mOffsetY; - } - public int getDragRegionLeft() { return mDragRegion.left; } @@ -181,30 +186,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 +222,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 +243,7 @@ public class DragView extends View { @Override public void onAnimationUpdate(ValueAnimator animation) { mCrossFadeProgress = animation.getAnimatedFraction(); + invalidate(); } }); va.start(); @@ -328,16 +337,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 +350,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/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java new file mode 100644 index 000000000..44d7ac6e9 --- /dev/null +++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java @@ -0,0 +1,128 @@ +package com.android.launcher3.folder; + +import android.graphics.Path; +import android.graphics.Point; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.Utilities; + +public class ClippedFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule { + + static final int MAX_NUM_ITEMS_IN_PREVIEW = 4; + private static final int MIN_NUM_ITEMS_IN_PREVIEW = 2; + + final float MIN_SCALE = 0.48f; + final float MAX_SCALE = 0.58f; + final float MAX_RADIUS_DILATION = 0.15f; + + private float[] mTmpPoint = new float[2]; + + private float mAvailableSpace; + private float mRadius; + private float mIconSize; + private boolean mIsRtl; + private Path mClipPath = new Path(); + + @Override + public void init(int availableSpace, int intrinsicIconSize, boolean rtl) { + mAvailableSpace = availableSpace; + mRadius = 0.66f * availableSpace; + mIconSize = intrinsicIconSize; + mIsRtl = rtl; + + // We make the clip radius just slightly smaller than the background drawable + // TODO(adamcohen): this is hacky, needs cleanup (likely through programmatic drawing). + int clipRadius = (int) mAvailableSpace / 2 - 1; + + mClipPath.addCircle(mAvailableSpace / 2, mAvailableSpace / 2, clipRadius, Path.Direction.CW); + } + + @Override + public FolderIcon.PreviewItemDrawingParams computePreviewItemDrawingParams(int index, + int curNumItems, FolderIcon.PreviewItemDrawingParams params) { + + getPosition(index, curNumItems, mTmpPoint); + + float transX = mTmpPoint[0]; + float transY = mTmpPoint[1]; + float totalScale = scaleForNumItems(curNumItems); + float overlayAlpha = 0; + + if (params == null) { + params = new FolderIcon.PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha); + } else { + params.transX = transX; + params.transY = transY; + params.scale = totalScale; + params.overlayAlpha = overlayAlpha; + } + + return params; + } + + private void getPosition(int index, int curNumItems, float[] result) { + // The case of two items is homomorphic to the case of one. + curNumItems = Math.max(curNumItems, 2); + + + // We model the preview as a circle of items starting in the appropriate piece of the + // upper left quadrant (to achieve horizontal and vertical symmetry). + double theta0 = mIsRtl ? 0 : Math.PI; + + // In RTL we go counterclockwise + int direction = mIsRtl ? 1 : -1; + + double thetaShift = 0; + if (curNumItems == 3) { + thetaShift = Math.PI / 6; + } else if (curNumItems == 4) { + thetaShift = Math.PI / 4; + } + theta0 += direction * thetaShift; + + // We want the items to appear in reading order. For the case of 1, 2 and 3 items, this + // is natural for the circular model. With 4 items, however, we need to swap the 3rd and + // 4th indices to achieve reading order. + if (curNumItems == 4 && index == 3) { + index = 2; + } else if (curNumItems == 4 && index == 2) { + index = 3; + } + + // We bump the radius up between 0 and MAX_RADIUS_DILATION % as the number of items increase + float radius = mRadius * (1 + MAX_RADIUS_DILATION * (curNumItems - + MIN_NUM_ITEMS_IN_PREVIEW) / (MAX_NUM_ITEMS_IN_PREVIEW - MIN_NUM_ITEMS_IN_PREVIEW)); + double theta = theta0 + index * (2 * Math.PI / curNumItems) * direction; + + float halfIconSize = (mIconSize * scaleForNumItems(curNumItems)) / 2; + + // Map the location along the circle, and offset the coordinates to represent the center + // of the icon, and to be based from the top / left of the preview area. The y component + // is inverted to match the coordinate system. + result[0] = mAvailableSpace / 2 + (float) (radius * Math.cos(theta) / 2) - halfIconSize; + result[1] = mAvailableSpace / 2 + (float) (- radius * Math.sin(theta) / 2) - halfIconSize; + + } + + private float scaleForNumItems(int numItems) { + if (numItems <= 2) { + return MAX_SCALE; + } else if (numItems == 3) { + return (MAX_SCALE + MIN_SCALE) / 2; + } else { + return MIN_SCALE; + } + } + + @Override + public int numItems() { + return MAX_NUM_ITEMS_IN_PREVIEW; + } + + @Override + public Path getClipPath() { + return mClipPath; + } + +} diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/folder/Folder.java index 471c324fe..a411c481c 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.launcher3.folder; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -36,12 +36,13 @@ 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; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; +import android.view.ViewDebug; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.animation.AccelerateInterpolator; @@ -51,13 +52,34 @@ import android.view.inputmethod.InputMethodManager; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.launcher3.Alarm; +import com.android.launcher3.CellLayout; import com.android.launcher3.CellLayout.CellInfo; -import com.android.launcher3.DragController.DragListener; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.DragSource; +import com.android.launcher3.DropTarget; +import com.android.launcher3.ExtendedEditText; +import com.android.launcher3.FolderInfo; import com.android.launcher3.FolderInfo.FolderListener; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAnimUtils; +import com.android.launcher3.LauncherModel; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.LogDecelerateInterpolator; +import com.android.launcher3.OnAlarmListener; +import com.android.launcher3.R; +import com.android.launcher3.ShortcutInfo; +import com.android.launcher3.Stats; import com.android.launcher3.UninstallDropTarget.UninstallSource; +import com.android.launcher3.Utilities; +import com.android.launcher3.Workspace; import com.android.launcher3.Workspace.ItemOperator; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource; import com.android.launcher3.config.FeatureFlags; +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; @@ -119,13 +141,13 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList protected final Launcher mLauncher; protected DragController mDragController; - protected FolderInfo mInfo; + public FolderInfo mInfo; @Thunk FolderIcon mFolderIcon; @Thunk FolderPagedView mContent; @Thunk View mContentWrapper; - ExtendedEditText mFolderName; + public ExtendedEditText mFolderName; private View mFooter; private int mFooterHeight; @@ -133,7 +155,15 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // Cell ranks used for drag and drop @Thunk int mTargetRank, mPrevTargetRank, mEmptyCellRank; + @ViewDebug.ExportedProperty(category = "launcher", + mapping = { + @ViewDebug.IntToString(from = STATE_NONE, to = "STATE_NONE"), + @ViewDebug.IntToString(from = STATE_SMALL, to = "STATE_SMALL"), + @ViewDebug.IntToString(from = STATE_ANIMATING, to = "STATE_ANIMATING"), + @ViewDebug.IntToString(from = STATE_OPEN, to = "STATE_OPEN"), + }) @Thunk int mState = STATE_NONE; + @ViewDebug.ExportedProperty(category = "launcher") private boolean mRearrangeOnClose = false; boolean mItemsInvalidated = false; private ShortcutInfo mCurrentDragInfo; @@ -148,6 +178,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList @Thunk float mFolderIconPivotY; private boolean mIsEditingName = false; + @ViewDebug.ExportedProperty(category = "launcher") private boolean mDestroyed; @Thunk Runnable mDeferredAction; @@ -208,8 +239,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() | @@ -224,23 +273,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) { @@ -289,7 +321,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } mFooter.setImportantForAccessibility(enable ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS : - IMPORTANT_FOR_ACCESSIBILITY_AUTO); + IMPORTANT_FOR_ACCESSIBILITY_AUTO); mLauncher.getWorkspace().setAddNewPageOnDrag(!enable); } @@ -317,7 +349,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 @@ -358,11 +390,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 */ @@ -466,11 +513,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; @@ -493,8 +536,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 +613,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) @@ -597,8 +641,30 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mDragController.forceTouchMove(); } - FolderPagedView pages = (FolderPagedView) mContent; - pages.verifyVisibleHighResIcons(pages.getNextPage()); + mContent.verifyVisibleHighResIcons(mContent.getNextPage()); + } + + /** + * Opens the folder without any animation + */ + public void open() { + if (!(getParent() instanceof DragLayer)) return; + + mContent.completePendingPageChanges(); + if (!mDragInProgress) { + // Open on the first page. + mContent.snapToPageImmediately(0); + } + centerAboutIcon(); + mFolderName.setTranslationX(0); + mContent.setMarkerScale(1); + + // Make sure the folder picks up the last drag move even if the finger doesn't move. + if (mDragController.isDragging()) { + mDragController.forceTouchMove(); + } + + mContent.verifyVisibleHighResIcons(mContent.getNextPage()); } public void beginExternalDrag(ShortcutInfo item) { @@ -613,7 +679,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 +702,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 +750,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) && @@ -905,6 +966,12 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // Show the animation, next time something is added to the folder. 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 @@ -933,7 +1000,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList @Override public boolean supportsAppInfoDropTarget() { - return false; + return !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND; } @Override @@ -964,16 +1031,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; @@ -1036,10 +1093,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList lp.y = top; } - float getPivotXForIconAnimation() { + public float getPivotXForIconAnimation() { return mFolderIconPivotX; } - float getPivotYForIconAnimation() { + public float getPivotYForIconAnimation() { return mFolderIconPivotY; } @@ -1110,11 +1167,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(); } @@ -1167,7 +1219,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mDestroyed = true; } - boolean isDestroyed() { + public boolean isDestroyed() { return mDestroyed; } @@ -1363,10 +1415,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) { if (hasFocus) { diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index d7b55b3a2..5c084d949 100644 --- a/src/com/android/launcher3/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.launcher3.folder; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -24,6 +24,7 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -41,26 +42,55 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; +import com.android.launcher3.Alarm; +import com.android.launcher3.AppInfo; +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.CellLayout; +import com.android.launcher3.CheckLongPressHelper; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.DropTarget.DragObject; +import com.android.launcher3.FastBitmapDrawable; +import com.android.launcher3.FolderInfo; import com.android.launcher3.FolderInfo.FolderListener; +import com.android.launcher3.IconCache; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAnimUtils; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.OnAlarmListener; +import com.android.launcher3.PreloadIconDrawable; +import com.android.launcher3.R; +import com.android.launcher3.ShortcutInfo; +import com.android.launcher3.SimpleOnStylusPressListener; +import com.android.launcher3.StylusEventHelper; +import com.android.launcher3.Utilities; +import com.android.launcher3.Workspace; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.util.Thunk; import java.util.ArrayList; /** - * An icon that can appear on in the workspace representing an {@link UserFolder}. + * An icon that can appear on in the workspace representing an {@link Folder}. */ public class FolderIcon extends FrameLayout implements FolderListener { - @Thunk Launcher mLauncher; + @Thunk + Launcher mLauncher; @Thunk Folder mFolder; private FolderInfo mInfo; @Thunk static boolean sStaticValuesDirty = true; + public static final int NUM_ITEMS_IN_PREVIEW = FeatureFlags.LAUNCHER3_CLIPPED_FOLDER_ICON ? + ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW : + StackFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; + private CheckLongPressHelper mLongPressHelper; private StylusEventHelper mStylusEventHelper; // The number of icons to display in the - public static final int NUM_ITEMS_IN_PREVIEW = 3; private static final int CONSUMPTION_ANIMATION_DURATION = 100; private static final int DROP_IN_ANIMATION_DURATION = 400; private static final int INITIAL_ITEM_ANIMATION_DURATION = 350; @@ -72,39 +102,33 @@ public class FolderIcon extends FrameLayout implements FolderListener { // The degree to which the outer ring is scaled in its natural state private static final float OUTER_RING_GROWTH_FACTOR = 0.3f; - // The amount of vertical spread between items in the stack [0...1] - private static final float PERSPECTIVE_SHIFT_FACTOR = 0.18f; - // Flag as to whether or not to draw an outer ring. Currently none is designed. public static final boolean HAS_OUTER_RING = true; // Flag whether the folder should open itself when an item is dragged over is enabled. public static final boolean SPRING_LOADING_ENABLED = true; - // The degree to which the item in the back of the stack is scaled [0...1] - // (0 means it's not scaled at all, 1 means it's scaled to nothing) - private static final float PERSPECTIVE_SCALE_FACTOR = 0.35f; - // Delay when drag enters until the folder opens, in miliseconds. private static final int ON_OPEN_DELAY = 800; public static Drawable sSharedFolderLeaveBehind = null; @Thunk ImageView mPreviewBackground; - @Thunk BubbleTextView mFolderName; + @Thunk + BubbleTextView mFolderName; FolderRingAnimator mFolderRingAnimator = null; // These variables are all associated with the drawing of the preview; they are stored // as member variables for shared usage and to avoid computation on each frame private int mIntrinsicIconSize; - private float mBaselineIconScale; - private int mBaselineIconSize; private int mAvailableSpaceInPreview; - private int mTotalWidth = -1; private int mPreviewOffsetX; private int mPreviewOffsetY; - private float mMaxPerspectiveShift; + private int mTotalWidth; + + private PreviewLayoutRule mPreviewLayoutRule; + boolean mAnimating = false; private Rect mOldBounds = new Rect(); @@ -115,7 +139,8 @@ public class FolderIcon extends FrameLayout implements FolderListener { @Thunk ArrayList<ShortcutInfo> mHiddenItems = new ArrayList<ShortcutInfo>(); private Alarm mOpenAlarm = new Alarm(); - @Thunk ItemInfo mDragInfo; + @Thunk + ItemInfo mDragInfo; public FolderIcon(Context context, AttributeSet attrs) { super(context, attrs); @@ -129,7 +154,11 @@ 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); + mPreviewLayoutRule = FeatureFlags.LAUNCHER3_CLIPPED_FOLDER_ICON ? + new ClippedFolderIconLayoutRule() : + new StackFolderIconLayoutRule(); + setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); } @@ -140,7 +169,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { return !workspace.workspaceInModalState(); } - static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group, + public static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group, FolderInfo folderInfo, IconCache iconCache) { @SuppressWarnings("all") // suppress dead code warning final boolean error = INITIAL_ITEM_ANIMATION_DURATION >= DROP_IN_ANIMATION_DURATION; @@ -171,8 +200,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); @@ -195,7 +223,8 @@ public class FolderIcon extends FrameLayout implements FolderListener { public static class FolderRingAnimator { public int mCellX; public int mCellY; - @Thunk CellLayout mCellLayout; + @Thunk + CellLayout mCellLayout; public float mOuterRingSize; public float mInnerRingSize; public FolderIcon mFolderIcon = null; @@ -319,7 +348,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { return mFolder; } - FolderInfo getFolderInfo() { + public FolderInfo getFolderInfo() { return mInfo; } @@ -330,8 +359,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 +368,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 +385,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() { @@ -375,7 +401,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { item = (ShortcutInfo) mDragInfo; } mFolder.beginExternalDrag(item); - mLauncher.openFolder(FolderIcon.this); + mLauncher.openFolder(FolderIcon.this, true); } }; @@ -448,14 +474,14 @@ public class FolderIcon extends FrameLayout implements FolderListener { } int[] center = new int[2]; - float scale = getLocalCenterForIndex(index, center); + float scale = getLocalCenterForIndex(index, index + 1, center); center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]); center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]); to.offset(center[0] - animateView.getMeasuredWidth() / 2, center[1] - animateView.getMeasuredHeight() / 2); - float finalAlpha = index < NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f; + float finalAlpha = index < mPreviewLayoutRule.numItems() ? 0.5f : 0f; float finalScale = scale * scaleRelativeToDragLayer; dragLayer.animateView(animateView, from, to, finalAlpha, @@ -496,22 +522,16 @@ public class FolderIcon extends FrameLayout implements FolderListener { mIntrinsicIconSize = drawableSize; mTotalWidth = totalSize; - final int previewSize = mPreviewBackground.getLayoutParams().height; + final int previewSize = FolderRingAnimator.sPreviewSize; final int previewPadding = FolderRingAnimator.sPreviewPadding; mAvailableSpaceInPreview = (previewSize - 2 * previewPadding); - // cos(45) = 0.707 + ~= 0.1) = 0.8f - int adjustedAvailableSpace = (int) ((mAvailableSpaceInPreview / 2) * (1 + 0.8f)); - - int unscaledHeight = (int) (mIntrinsicIconSize * (1 + PERSPECTIVE_SHIFT_FACTOR)); - - mBaselineIconScale = (1.0f * adjustedAvailableSpace / unscaledHeight); - - mBaselineIconSize = (int) (mIntrinsicIconSize * mBaselineIconScale); - mMaxPerspectiveShift = mBaselineIconSize * PERSPECTIVE_SHIFT_FACTOR; mPreviewOffsetX = (mTotalWidth - mAvailableSpaceInPreview) / 2; - mPreviewOffsetY = previewPadding + grid.folderBackgroundOffset; + mPreviewOffsetY = previewPadding + grid.folderBackgroundOffset + getPaddingTop(); + + mPreviewLayoutRule.init(mAvailableSpaceInPreview, mIntrinsicIconSize, + Utilities.isRtl(getResources())); } } @@ -519,7 +539,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { computePreviewDrawingParams(d.getIntrinsicWidth(), getMeasuredWidth()); } - class PreviewItemDrawingParams { + static class PreviewItemDrawingParams { PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) { this.transX = transX; this.transY = transY; @@ -533,8 +553,9 @@ public class FolderIcon extends FrameLayout implements FolderListener { Drawable drawable; } - private float getLocalCenterForIndex(int index, int[] center) { - mParams = computePreviewItemDrawingParams(Math.min(NUM_ITEMS_IN_PREVIEW, index), mParams); + private float getLocalCenterForIndex(int index, int curNumItems, int[] center) { + mParams = computePreviewItemDrawingParams(Math.min(mPreviewLayoutRule.numItems(), index), + curNumItems, mParams); mParams.transX += mPreviewOffsetX; mParams.transY += mPreviewOffsetY; @@ -546,37 +567,14 @@ public class FolderIcon extends FrameLayout implements FolderListener { return mParams.scale; } - private PreviewItemDrawingParams computePreviewItemDrawingParams(int index, + private PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems, PreviewItemDrawingParams params) { - index = NUM_ITEMS_IN_PREVIEW - index - 1; - float r = (index * 1.0f) / (NUM_ITEMS_IN_PREVIEW - 1); - float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r)); - - float offset = (1 - r) * mMaxPerspectiveShift; - float scaledSize = scale * mBaselineIconSize; - float scaleOffsetCorrection = (1 - scale) * mBaselineIconSize; - - // We want to imagine our coordinates from the bottom left, growing up and to the - // right. This is natural for the x-axis, but for the y-axis, we have to invert things. - float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection) + getPaddingTop(); - float transX = (mAvailableSpaceInPreview - scaledSize) / 2; - float totalScale = mBaselineIconScale * scale; - final float overlayAlpha = (80 * (1 - r)) / 255f; - - if (params == null) { - params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha); - } else { - params.transX = transX; - params.transY = transY; - params.scale = totalScale; - params.overlayAlpha = overlayAlpha; - } - return params; + return mPreviewLayoutRule.computePreviewItemDrawingParams(index, curNumItems, params); } private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) { canvas.save(); - canvas.translate(params.transX + mPreviewOffsetX, params.transY + mPreviewOffsetY); + canvas.translate(params.transX, params.transY); canvas.scale(params.scale, params.scale); Drawable d = params.drawable; @@ -620,13 +618,20 @@ public class FolderIcon extends FrameLayout implements FolderListener { computePreviewDrawingParams(d); } - int nItemsInPreview = Math.min(items.size(), NUM_ITEMS_IN_PREVIEW); + canvas.save(); + canvas.translate(mPreviewOffsetX, mPreviewOffsetY); + Path clipPath = mPreviewLayoutRule.getClipPath(); + if (clipPath != null) { + canvas.clipPath(clipPath); + } + + int nItemsInPreview = Math.min(items.size(), mPreviewLayoutRule.numItems()); if (!mAnimating) { for (int i = nItemsInPreview - 1; i >= 0; i--) { v = (TextView) items.get(i); if (!mHiddenItems.contains(v.getTag())) { d = getTopDrawable(v); - mParams = computePreviewItemDrawingParams(i, mParams); + mParams = computePreviewItemDrawingParams(i, nItemsInPreview, mParams); mParams.drawable = d; drawPreviewItem(canvas, mParams); } @@ -634,6 +639,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { } else { drawPreviewItem(canvas, mAnimParams); } + canvas.restore(); } private Drawable getTopDrawable(TextView v) { @@ -643,12 +649,14 @@ public class FolderIcon extends FrameLayout implements FolderListener { private void animateFirstItem(final Drawable d, int duration, final boolean reverse, final Runnable onCompleteRunnable) { - final PreviewItemDrawingParams finalParams = computePreviewItemDrawingParams(0, null); + + final PreviewItemDrawingParams finalParams = + computePreviewItemDrawingParams(0, reverse ? 1 : 2, null); float iconSize = mLauncher.getDeviceProfile().iconSizePx; final float scale0 = iconSize / d.getIntrinsicWidth() ; final float transX0 = (mAvailableSpaceInPreview - iconSize) / 2; - final float transY0 = (mAvailableSpaceInPreview - iconSize) / 2 + getPaddingTop(); + final float transY0 = (mAvailableSpaceInPreview - iconSize) / 2; mAnimParams.drawable = d; ValueAnimator va = LauncherAnimUtils.ofFloat(this, 0f, 1.0f); @@ -712,8 +720,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 +730,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; } @@ -754,7 +761,16 @@ public class FolderIcon extends FrameLayout implements FolderListener { @Override public void cancelLongPress() { super.cancelLongPress(); - mLongPressHelper.cancelLongPress(); } + + public interface PreviewLayoutRule { + public PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems, + PreviewItemDrawingParams params); + + public void init(int availableSpace, int intrinsicIconSize, boolean rtl); + + public int numItems(); + public Path getClipPath(); + } } diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java index d503d2cf5..c25444e06 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/folder/FolderPagedView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.launcher3.folder; import android.annotation.SuppressLint; import android.content.Context; @@ -23,13 +23,31 @@ import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewDebug; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.view.animation.OvershootInterpolator; +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.CellLayout; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener; +import com.android.launcher3.FocusIndicatorView; +import com.android.launcher3.IconCache; +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherModel; +import com.android.launcher3.PageIndicator; import com.android.launcher3.PageIndicator.PageMarkerResources; +import com.android.launcher3.PagedView; +import com.android.launcher3.R; +import com.android.launcher3.ShortcutAndWidgetContainer; +import com.android.launcher3.ShortcutInfo; +import com.android.launcher3.Utilities; import com.android.launcher3.Workspace.ItemOperator; +import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.util.Thunk; import java.util.ArrayList; @@ -68,12 +86,17 @@ public class FolderPagedView extends PagedView { @Thunk final HashMap<View, Runnable> mPendingAnimations = new HashMap<>(); + @ViewDebug.ExportedProperty(category = "launcher") private final int mMaxCountX; + @ViewDebug.ExportedProperty(category = "launcher") private final int mMaxCountY; + @ViewDebug.ExportedProperty(category = "launcher") private final int mMaxItemsPerPage; private int mAllocatedContentSize; + @ViewDebug.ExportedProperty(category = "launcher") private int mGridCountX; + @ViewDebug.ExportedProperty(category = "launcher") private int mGridCountY; private Folder mFolder; @@ -226,12 +249,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 +464,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/folder/StackFolderIconLayoutRule.java b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java new file mode 100644 index 000000000..87f5f897b --- /dev/null +++ b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java @@ -0,0 +1,93 @@ +/** + * 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.folder; + +import android.graphics.Path; + +import com.android.launcher3.folder.FolderIcon.PreviewItemDrawingParams; + +public class StackFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule { + + static final int MAX_NUM_ITEMS_IN_PREVIEW = 3; + + // The degree to which the item in the back of the stack is scaled [0...1] + // (0 means it's not scaled at all, 1 means it's scaled to nothing) + private static final float PERSPECTIVE_SCALE_FACTOR = 0.35f; + + // The amount of vertical spread between items in the stack [0...1] + private static final float PERSPECTIVE_SHIFT_FACTOR = 0.18f; + + private float mBaselineIconScale; + private int mBaselineIconSize; + private int mAvailableSpaceInPreview; + private float mMaxPerspectiveShift; + + @Override + public void init(int availableSpace, int intrinsicIconSize, boolean rtl) { + mAvailableSpaceInPreview = availableSpace; + + // cos(45) = 0.707 + ~= 0.1) = 0.8f + int adjustedAvailableSpace = (int) ((mAvailableSpaceInPreview / 2) * (1 + 0.8f)); + + int unscaledHeight = (int) (intrinsicIconSize * (1 + PERSPECTIVE_SHIFT_FACTOR)); + + mBaselineIconScale = (1.0f * adjustedAvailableSpace / unscaledHeight); + + mBaselineIconSize = (int) (intrinsicIconSize * mBaselineIconScale); + mMaxPerspectiveShift = mBaselineIconSize * PERSPECTIVE_SHIFT_FACTOR; + } + + @Override + public PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems, + PreviewItemDrawingParams params) { + + index = MAX_NUM_ITEMS_IN_PREVIEW - index - 1; + float r = (index * 1.0f) / (MAX_NUM_ITEMS_IN_PREVIEW - 1); + float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r)); + + float offset = (1 - r) * mMaxPerspectiveShift; + float scaledSize = scale * mBaselineIconSize; + float scaleOffsetCorrection = (1 - scale) * mBaselineIconSize; + + // We want to imagine our coordinates from the bottom left, growing up and to the + // right. This is natural for the x-axis, but for the y-axis, we have to invert things. + float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection); + float transX = (mAvailableSpaceInPreview - scaledSize) / 2; + float totalScale = mBaselineIconScale * scale; + final float overlayAlpha = (80 * (1 - r)) / 255f; + + if (params == null) { + params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha); + } else { + params.transX = transX; + params.transY = transY; + params.scale = totalScale; + params.overlayAlpha = overlayAlpha; + } + return params; + } + + @Override + public int numItems() { + return MAX_NUM_ITEMS_IN_PREVIEW; + } + + @Override + public Path getClipPath() { + return null; + } +} diff --git a/src/com/android/launcher3/model/MigrateFromRestoreTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java index 786ab6009..19ec3ed64 100644 --- a/src/com/android/launcher3/model/MigrateFromRestoreTask.java +++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java @@ -9,6 +9,7 @@ import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.database.Cursor; import android.graphics.Point; +import android.net.Uri; import android.text.TextUtils; import android.util.Log; @@ -21,32 +22,37 @@ import com.android.launcher3.LauncherProvider; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.Utilities; +import com.android.launcher3.backup.nano.BackupProtos; import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.util.LongArrayMap; -import com.android.launcher3.util.Thunk; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Locale; /** * This class takes care of shrinking the workspace (by maximum of one row and one column), as a - * result of restoring from a larger device. + * result of restoring from a larger device or device density change. */ -public class MigrateFromRestoreTask { +public class GridSizeMigrationTask { - public static boolean ENABLED = false; + public static boolean ENABLED = Utilities.isNycOrAbove(); - private static final String TAG = "MigrateFromRestoreTask"; + private static final String TAG = "GridSizeMigrationTask"; private static final boolean DEBUG = true; - private static final String KEY_MIGRATION_SOURCE_SIZE = "migration_restore_src_size"; + private static final String KEY_MIGRATION_SRC_WORKSPACE_SIZE = "migration_src_workspace_size"; + private static final String KEY_MIGRATION_SRC_HOTSEAT_SIZE = "migration_src_hotseat_size"; + + // Set of entries indicating minimum size a widget can be resized to. This is used during + // restore in case the widget has not been installed yet. private static final String KEY_MIGRATION_WIDGET_MINSIZE = "migration_widget_min_size"; // These are carefully selected weights for various item types (Math.random?), to allow for - // the lease absurd migration experience. + // the least absurd migration experience. private static final float WT_SHORTCUT = 1; private static final float WT_APPLICATION = 0.8f; private static final float WT_WIDGET_MIN = 2; @@ -54,59 +60,141 @@ public class MigrateFromRestoreTask { private static final float WT_FOLDER_FACTOR = 0.5f; private final Context mContext; - private final ContentValues mTempValues = new ContentValues(); - private final HashMap<String, Point> mWidgetMinSize; private final InvariantDeviceProfile mIdp; - private HashSet<String> mValidPackages; - public ArrayList<Long> mEntryToRemove; - private ArrayList<ContentProviderOperation> mUpdateOperations; - - private ArrayList<DbEntry> mCarryOver; + private final HashMap<String, Point> mWidgetMinSize = new HashMap<>(); + private final ContentValues mTempValues = new ContentValues(); + private final ArrayList<Long> mEntryToRemove = new ArrayList<>(); + private final ArrayList<ContentProviderOperation> mUpdateOperations = new ArrayList<>(); + private final ArrayList<DbEntry> mCarryOver = new ArrayList<>(); + private final HashSet<String> mValidPackages; private final int mSrcX, mSrcY; - @Thunk final int mTrgX, mTrgY; + private final int mTrgX, mTrgY; private final boolean mShouldRemoveX, mShouldRemoveY; - public MigrateFromRestoreTask(Context context) { + private final int mSrcHotseatSize; + private final int mSrcAllAppsRank; + private final int mDestHotseatSize; + private final int mDestAllAppsRank; + + protected GridSizeMigrationTask(Context context, InvariantDeviceProfile idp, + HashSet<String> validPackages, HashMap<String, Point> widgetMinSize, + Point sourceSize, Point targetSize) { mContext = context; + mValidPackages = validPackages; + mWidgetMinSize.putAll(widgetMinSize); + mIdp = idp; - SharedPreferences prefs = Utilities.getPrefs(context); - Point sourceSize = parsePoint(prefs.getString(KEY_MIGRATION_SOURCE_SIZE, "")); mSrcX = sourceSize.x; mSrcY = sourceSize.y; - mWidgetMinSize = new HashMap<String, Point>(); - for (String s : prefs.getStringSet(KEY_MIGRATION_WIDGET_MINSIZE, - Collections.<String>emptySet())) { - String[] parts = s.split("#"); - mWidgetMinSize.put(parts[0], parsePoint(parts[1])); - } + mTrgX = targetSize.x; + mTrgY = targetSize.y; - mIdp = LauncherAppState.getInstance().getInvariantDeviceProfile(); - mTrgX = mIdp.numColumns; - mTrgY = mIdp.numRows; mShouldRemoveX = mTrgX < mSrcX; mShouldRemoveY = mTrgY < mSrcY; + + // Non-used variables + mSrcHotseatSize = mSrcAllAppsRank = mDestHotseatSize = mDestAllAppsRank = -1; } - public void execute() throws Exception { - mEntryToRemove = new ArrayList<>(); - mCarryOver = new ArrayList<>(); - mUpdateOperations = new ArrayList<>(); + protected GridSizeMigrationTask(Context context, + InvariantDeviceProfile idp, HashSet<String> validPackages, + int srcHotseatSize, int srcAllAppsRank, + int destHotseatSize, int destAllAppsRank) { + mContext = context; + mIdp = idp; + mValidPackages = validPackages; + + mSrcHotseatSize = srcHotseatSize; + mSrcAllAppsRank = srcAllAppsRank; + + mDestHotseatSize = destHotseatSize; + mDestAllAppsRank = destAllAppsRank; + + // Non-used variables + mSrcX = mSrcY = mTrgX = mTrgY = -1; + mShouldRemoveX = mShouldRemoveY = false; + } + + /** + * Applied all the pending DB operations + * @return true if any DB operation was commited. + */ + private boolean applyOperations() throws Exception { + // Update items + if (!mUpdateOperations.isEmpty()) { + mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, mUpdateOperations); + } - // Initialize list of valid packages. This contain all the packages which are already on - // the device and packages which are being installed. Any item which doesn't belong to - // this set is removed. - // Since the loader removes such items anyway, removing these items here doesn't cause any - // extra data loss and gives us more free space on the grid for better migration. - mValidPackages = new HashSet<>(); - for (PackageInfo info : mContext.getPackageManager().getInstalledPackages(0)) { - mValidPackages.add(info.packageName); + if (!mEntryToRemove.isEmpty()) { + if (DEBUG) { + Log.d(TAG, "Removing items: " + TextUtils.join(", ", mEntryToRemove)); + } + mContext.getContentResolver().delete(LauncherSettings.Favorites.CONTENT_URI, + Utilities.createDbSelectionQuery( + LauncherSettings.Favorites._ID, mEntryToRemove), null); } - mValidPackages.addAll(PackageInstallerCompat.getInstance(mContext) - .updateAndGetActiveSessionCache().keySet()); + return !mUpdateOperations.isEmpty() || !mEntryToRemove.isEmpty(); + } + + /** + * To migrate hotseat, we load all the entries in order (LTR or RTL) and arrange them + * in the order in the new hotseat while keeping an empty space for all-apps. If the number of + * entries is more than what can fit in the new hotseat, we drop the entries with least weight. + * For weight calculation {@see #WT_SHORTCUT}, {@see #WT_APPLICATION} + * & {@see #WT_FOLDER_FACTOR}. + * @return true if any DB change was made + */ + protected boolean migrateHotseat() throws Exception { + ArrayList<DbEntry> items = loadHotseatEntries(); + + int requiredCount = mDestHotseatSize - 1; + + while (items.size() > requiredCount) { + // Pick the center item by default. + DbEntry toRemove = items.get(items.size() / 2); + + // Find the item with least weight. + for (DbEntry entry : items) { + if (entry.weight < toRemove.weight) { + toRemove = entry; + } + } + + mEntryToRemove.add(toRemove.id); + items.remove(toRemove); + } + + // Update screen IDS + int newScreenId = 0; + for (DbEntry entry : items) { + if (entry.screenId != newScreenId) { + entry.screenId = newScreenId; + + // These values does not affect the item position, but we should set them + // to something other than -1. + entry.cellX = newScreenId; + entry.cellY = 0; + + update(entry); + } + + newScreenId++; + if (newScreenId == mDestAllAppsRank) { + newScreenId++; + } + } + + return applyOperations(); + } + + /** + * @return true if any DB change was made + */ + protected boolean migrateWorkspace() throws Exception { ArrayList<Long> allScreens = LauncherModel.loadWorkspaceScreensDb(mContext); if (allScreens.isEmpty()) { throw new Exception("Unable to get workspace screens"); @@ -135,7 +223,11 @@ 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))) { @@ -150,31 +242,19 @@ public class MigrateFromRestoreTask { } while (!mCarryOver.isEmpty()); - - LauncherAppState.getInstance().getModel() - .updateWorkspaceScreenOrder(mContext, allScreens); - } - - // Update items - mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, mUpdateOperations); - - if (!mEntryToRemove.isEmpty()) { - if (DEBUG) { - Log.d(TAG, "Removing items: " + TextUtils.join(", ", mEntryToRemove)); + // Update screens + final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI; + mUpdateOperations.add(ContentProviderOperation.newDelete(uri).build()); + int count = allScreens.size(); + for (int i = 0; i < count; i++) { + ContentValues v = new ContentValues(); + long screenId = allScreens.get(i); + v.put(LauncherSettings.WorkspaceScreens._ID, screenId); + v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i); + mUpdateOperations.add(ContentProviderOperation.newInsert(uri).withValues(v).build()); } - mContext.getContentResolver().delete(LauncherSettings.Favorites.CONTENT_URI, - Utilities.createDbSelectionQuery( - LauncherSettings.Favorites._ID, mEntryToRemove), null); - } - - // Make sure we haven't removed everything. - final Cursor c = mContext.getContentResolver().query( - LauncherSettings.Favorites.CONTENT_URI, null, null, null, null); - boolean hasData = c.moveToNext(); - c.close(); - if (!hasData) { - throw new Exception("Removed every thing during grid resize"); } + return applyOperations(); } /** @@ -188,7 +268,7 @@ public class MigrateFromRestoreTask { * (otherwise they are placed on a new screen). */ private void migrateScreen(long screenId) { - ArrayList<DbEntry> items = loadEntries(screenId); + ArrayList<DbEntry> items = loadWorkspaceEntries(screenId); int removedCol = Integer.MAX_VALUE; int removedRow = Integer.MAX_VALUE; @@ -326,7 +406,7 @@ public class MigrateFromRestoreTask { return finalItems; } - @Thunk void markCells(boolean[][] occupied, DbEntry item, boolean val) { + private void markCells(boolean[][] occupied, DbEntry item, boolean val) { for (int i = item.cellX; i < (item.cellX + item.spanX); i++) { for (int j = item.cellY; j < (item.cellY + item.spanY); j++) { occupied[i][j] = val; @@ -334,7 +414,7 @@ public class MigrateFromRestoreTask { } } - @Thunk boolean isVacant(boolean[][] occupied, int x, int y, int w, int h) { + private boolean isVacant(boolean[][] occupied, int x, int y, int w, int h) { if (x + w > mTrgX) return false; if (y + h > mTrgY) return false; @@ -542,100 +622,161 @@ public class MigrateFromRestoreTask { } } + private ArrayList<DbEntry> loadHotseatEntries() { + Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, + new String[]{ + Favorites._ID, // 0 + Favorites.ITEM_TYPE, // 1 + Favorites.INTENT, // 2 + Favorites.SCREEN}, // 3 + Favorites.CONTAINER + " = " + Favorites.CONTAINER_HOTSEAT, null, null, null); + + final int indexId = c.getColumnIndexOrThrow(Favorites._ID); + final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE); + final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT); + final int indexScreen = c.getColumnIndexOrThrow(Favorites.SCREEN); + + ArrayList<DbEntry> entries = new ArrayList<>(); + while (c.moveToNext()) { + DbEntry entry = new DbEntry(); + entry.id = c.getLong(indexId); + entry.itemType = c.getInt(indexItemType); + entry.screenId = c.getLong(indexScreen); + + if (entry.screenId >= mSrcHotseatSize) { + mEntryToRemove.add(entry.id); + continue; + } + + try { + // calculate weight + switch (entry.itemType) { + case Favorites.ITEM_TYPE_SHORTCUT: + case Favorites.ITEM_TYPE_APPLICATION: { + verifyIntent(c.getString(indexIntent)); + entry.weight = entry.itemType == Favorites.ITEM_TYPE_SHORTCUT + ? WT_SHORTCUT : WT_APPLICATION; + break; + } + case Favorites.ITEM_TYPE_FOLDER: { + int total = getFolderItemsCount(entry.id); + if (total == 0) { + throw new Exception("Folder is empty"); + } + entry.weight = WT_FOLDER_FACTOR * total; + break; + } + default: + throw new Exception("Invalid item type"); + } + } catch (Exception e) { + if (DEBUG) { + Log.d(TAG, "Removing item " + entry.id, e); + } + mEntryToRemove.add(entry.id); + continue; + } + entries.add(entry); + } + c.close(); + return entries; + } + + /** * Loads entries for a particular screen id. */ - public ArrayList<DbEntry> loadEntries(long screen) { - Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, - new String[] { - Favorites._ID, // 0 - Favorites.ITEM_TYPE, // 1 - Favorites.CELLX, // 2 - Favorites.CELLY, // 3 - Favorites.SPANX, // 4 - Favorites.SPANY, // 5 - Favorites.INTENT, // 6 - Favorites.APPWIDGET_PROVIDER}, // 7 + private ArrayList<DbEntry> loadWorkspaceEntries(long screen) { + Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, + new String[]{ + Favorites._ID, // 0 + Favorites.ITEM_TYPE, // 1 + Favorites.CELLX, // 2 + Favorites.CELLY, // 3 + Favorites.SPANX, // 4 + Favorites.SPANY, // 5 + Favorites.INTENT, // 6 + Favorites.APPWIDGET_PROVIDER}, // 7 Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP - + " AND " + Favorites.SCREEN + " = " + screen, null, null, null); - - final int indexId = c.getColumnIndexOrThrow(Favorites._ID); - final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE); - final int indexCellX = c.getColumnIndexOrThrow(Favorites.CELLX); - final int indexCellY = c.getColumnIndexOrThrow(Favorites.CELLY); - final int indexSpanX = c.getColumnIndexOrThrow(Favorites.SPANX); - final int indexSpanY = c.getColumnIndexOrThrow(Favorites.SPANY); - final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT); - final int indexAppWidgetProvider = c.getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER); - - ArrayList<DbEntry> entries = new ArrayList<>(); - while (c.moveToNext()) { - DbEntry entry = new DbEntry(); - entry.id = c.getLong(indexId); - entry.itemType = c.getInt(indexItemType); - entry.cellX = c.getInt(indexCellX); - entry.cellY = c.getInt(indexCellY); - entry.spanX = c.getInt(indexSpanX); - entry.spanY = c.getInt(indexSpanY); - entry.screenId = screen; - - try { - // calculate weight - switch (entry.itemType) { - case Favorites.ITEM_TYPE_SHORTCUT: - case Favorites.ITEM_TYPE_APPLICATION: { - verifyIntent(c.getString(indexIntent)); - entry.weight = entry.itemType == Favorites.ITEM_TYPE_SHORTCUT - ? WT_SHORTCUT : WT_APPLICATION; - break; - } - case Favorites.ITEM_TYPE_APPWIDGET: { - String provider = c.getString(indexAppWidgetProvider); - ComponentName cn = ComponentName.unflattenFromString(provider); - verifyPackage(cn.getPackageName()); - entry.weight = Math.max(WT_WIDGET_MIN, WT_WIDGET_FACTOR - * entry.spanX * entry.spanY); - - // Migration happens for current user only. - LauncherAppWidgetProviderInfo pInfo = LauncherModel.getProviderInfo( - mContext, cn, UserHandleCompat.myUserHandle()); - Point spans = pInfo == null ? - mWidgetMinSize.get(provider) : pInfo.getMinSpans(mIdp, mContext); - if (spans != null) { - entry.minSpanX = spans.x > 0 ? spans.x : entry.spanX; - entry.minSpanY = spans.y > 0 ? spans.y : entry.spanY; - } else { - // Assume that the widget be resized down to 2x2 - entry.minSpanX = entry.minSpanY = 2; - } - - if (entry.minSpanX > mTrgX || entry.minSpanY > mTrgY) { - throw new Exception("Widget can't be resized down to fit the grid"); - } - break; - } - case Favorites.ITEM_TYPE_FOLDER: { - int total = getFolderItemsCount(entry.id); - if (total == 0) { - throw new Exception("Folder is empty"); - } - entry.weight = WT_FOLDER_FACTOR * total; - break; - } - default: - throw new Exception("Invalid item type"); - } - } catch (Exception e) { - if (DEBUG) { - Log.d(TAG, "Removing item " + entry.id, e); - } - mEntryToRemove.add(entry.id); - continue; - } - entries.add(entry); - } - c.close(); - return entries; + + " AND " + Favorites.SCREEN + " = " + screen, null, null, null); + + final int indexId = c.getColumnIndexOrThrow(Favorites._ID); + final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE); + final int indexCellX = c.getColumnIndexOrThrow(Favorites.CELLX); + final int indexCellY = c.getColumnIndexOrThrow(Favorites.CELLY); + final int indexSpanX = c.getColumnIndexOrThrow(Favorites.SPANX); + final int indexSpanY = c.getColumnIndexOrThrow(Favorites.SPANY); + final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT); + final int indexAppWidgetProvider = c.getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER); + + ArrayList<DbEntry> entries = new ArrayList<>(); + while (c.moveToNext()) { + DbEntry entry = new DbEntry(); + entry.id = c.getLong(indexId); + entry.itemType = c.getInt(indexItemType); + entry.cellX = c.getInt(indexCellX); + entry.cellY = c.getInt(indexCellY); + entry.spanX = c.getInt(indexSpanX); + entry.spanY = c.getInt(indexSpanY); + entry.screenId = screen; + + try { + // calculate weight + switch (entry.itemType) { + case Favorites.ITEM_TYPE_SHORTCUT: + case Favorites.ITEM_TYPE_APPLICATION: { + verifyIntent(c.getString(indexIntent)); + entry.weight = entry.itemType == Favorites.ITEM_TYPE_SHORTCUT + ? WT_SHORTCUT : WT_APPLICATION; + break; + } + case Favorites.ITEM_TYPE_APPWIDGET: { + String provider = c.getString(indexAppWidgetProvider); + ComponentName cn = ComponentName.unflattenFromString(provider); + verifyPackage(cn.getPackageName()); + entry.weight = Math.max(WT_WIDGET_MIN, WT_WIDGET_FACTOR + * entry.spanX * entry.spanY); + + // Migration happens for current user only. + LauncherAppWidgetProviderInfo pInfo = LauncherModel.getProviderInfo( + mContext, cn, UserHandleCompat.myUserHandle()); + Point spans = pInfo == null ? + mWidgetMinSize.get(provider) : pInfo.getMinSpans(mIdp, mContext); + if (spans != null) { + entry.minSpanX = spans.x > 0 ? spans.x : entry.spanX; + entry.minSpanY = spans.y > 0 ? spans.y : entry.spanY; + } else { + // Assume that the widget be resized down to 2x2 + entry.minSpanX = entry.minSpanY = 2; + } + + if (entry.minSpanX > mTrgX || entry.minSpanY > mTrgY) { + throw new Exception("Widget can't be resized down to fit the grid"); + } + break; + } + case Favorites.ITEM_TYPE_FOLDER: { + int total = getFolderItemsCount(entry.id); + if (total == 0) { + throw new Exception("Folder is empty"); + } + entry.weight = WT_FOLDER_FACTOR * total; + break; + } + default: + throw new Exception("Invalid item type"); + } + } catch (Exception e) { + if (DEBUG) { + Log.d(TAG, "Removing item " + entry.id, e); + } + mEntryToRemove.add(entry.id); + continue; + } + entries.add(entry); + } + c.close(); + return entries; } /** @@ -643,7 +784,7 @@ public class MigrateFromRestoreTask { */ private int getFolderItemsCount(long folderId) { Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, - new String[] {Favorites._ID, Favorites.INTENT}, + new String[]{Favorites._ID, Favorites.INTENT}, Favorites.CONTAINER + " = " + folderId, null, null, null); int total = 0; @@ -730,7 +871,7 @@ public class MigrateFromRestoreTask { } } - @Thunk static ArrayList<DbEntry> deepCopy(ArrayList<DbEntry> src) { + private static ArrayList<DbEntry> deepCopy(ArrayList<DbEntry> src) { ArrayList<DbEntry> dup = new ArrayList<DbEntry>(src.size()); for (DbEntry e : src) { dup.add(e.copy()); @@ -743,21 +884,147 @@ public class MigrateFromRestoreTask { return new Point(Integer.parseInt(split[0]), Integer.parseInt(split[1])); } - public static void markForMigration(Context context, int srcX, int srcY, - HashSet<String> widgets) { + private static String getPointString(int x, int y) { + return String.format(Locale.ENGLISH, "%d,%d", x, y); + } + + public static void markForMigration( + Context context, HashSet<String> widgets, BackupProtos.DeviceProfieData srcProfile) { Utilities.getPrefs(context).edit() - .putString(KEY_MIGRATION_SOURCE_SIZE, srcX + "," + srcY) + .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, + getPointString((int) srcProfile.desktopCols, (int) srcProfile.desktopRows)) + .putString(KEY_MIGRATION_SRC_HOTSEAT_SIZE, + getPointString((int) srcProfile.hotseatCount, srcProfile.allappsRank)) .putStringSet(KEY_MIGRATION_WIDGET_MINSIZE, widgets) .apply(); } - public static boolean shouldRunTask(Context context) { - return !TextUtils.isEmpty(Utilities.getPrefs(context).getString(KEY_MIGRATION_SOURCE_SIZE, "")); - } + /** + * Migrates the workspace and hotseat in case their sizes changed. + * @return false if the migration failed. + */ + public static boolean migrateGridIfNeeded(Context context) { + SharedPreferences prefs = Utilities.getPrefs(context); + InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile(); - public static void clearFlags(Context context) { - Utilities.getPrefs(context).edit().remove(KEY_MIGRATION_SOURCE_SIZE) - .remove(KEY_MIGRATION_WIDGET_MINSIZE).commit(); - } + String gridSizeString = getPointString(idp.numColumns, idp.numRows); + String hotseatSizeString = getPointString(idp.numHotseatIcons, idp.hotseatAllAppsRank); + + if (gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, "")) && + hotseatSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_HOTSEAT_SIZE, ""))) { + // Skip if workspace and hotseat sizes have not changed. + return true; + } + long migrationStartTime = System.currentTimeMillis(); + try { + boolean dbChanged = false; + + // Initialize list of valid packages. This contain all the packages which are already on + // the device and packages which are being installed. Any item which doesn't belong to + // this set is removed. + // Since the loader removes such items anyway, removing these items here doesn't cause + // any extra data loss and gives us more free space on the grid for better migration. + HashSet validPackages = new HashSet<>(); + for (PackageInfo info : context.getPackageManager().getInstalledPackages(0)) { + validPackages.add(info.packageName); + } + validPackages.addAll(PackageInstallerCompat.getInstance(context) + .updateAndGetActiveSessionCache().keySet()); + + // Hotseat + Point srcHotseatSize = parsePoint(prefs.getString( + KEY_MIGRATION_SRC_HOTSEAT_SIZE, hotseatSizeString)); + if (srcHotseatSize.x != idp.numHotseatIcons || + srcHotseatSize.y != idp.hotseatAllAppsRank) { + // Migrate hotseat. + + dbChanged = new GridSizeMigrationTask(context, + LauncherAppState.getInstance().getInvariantDeviceProfile(), + validPackages, + srcHotseatSize.x, srcHotseatSize.y, + idp.numHotseatIcons, idp.hotseatAllAppsRank).migrateHotseat(); + } + + // Grid size + Point targetSize = new Point(idp.numColumns, idp.numRows); + Point sourceSize = parsePoint(prefs.getString( + KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)); + + if (!targetSize.equals(sourceSize)) { + + // The following list defines all possible grid sizes (and intermediate steps + // during migration). Note that at each step, dx <= 1 && dy <= 1. Any grid size + // which is not in this list is not migrated. + ArrayList<Point> gridSizeSteps = new ArrayList<>(); + gridSizeSteps.add(new Point(2, 3)); + gridSizeSteps.add(new Point(3, 3)); + gridSizeSteps.add(new Point(3, 4)); + gridSizeSteps.add(new Point(4, 4)); + gridSizeSteps.add(new Point(5, 5)); + gridSizeSteps.add(new Point(5, 6)); + gridSizeSteps.add(new Point(6, 6)); + gridSizeSteps.add(new Point(7, 7)); + + int sourceSizeIndex = gridSizeSteps.indexOf(sourceSize); + int targetSizeIndex = gridSizeSteps.indexOf(targetSize); + + if (sourceSizeIndex <= -1 || targetSizeIndex <= -1) { + throw new Exception("Unable to migrate grid size from " + sourceSize + + " to " + targetSize); + } + + // Min widget sizes + HashMap<String, Point> widgetMinSize = new HashMap<>(); + for (String s : Utilities.getPrefs(context).getStringSet(KEY_MIGRATION_WIDGET_MINSIZE, + Collections.<String>emptySet())) { + String[] parts = s.split("#"); + widgetMinSize.put(parts[0], parsePoint(parts[1])); + } + + // Migrate the workspace grid, step by step. + while (targetSizeIndex < sourceSizeIndex ) { + // We only need to migrate the grid if source size is greater + // than the target size. + Point stepTargetSize = gridSizeSteps.get(sourceSizeIndex - 1); + Point stepSourceSize = gridSizeSteps.get(sourceSizeIndex); + + if (new GridSizeMigrationTask(context, + LauncherAppState.getInstance().getInvariantDeviceProfile(), + validPackages, widgetMinSize, + stepSourceSize, stepTargetSize).migrateWorkspace()) { + dbChanged = true; + } + sourceSizeIndex--; + } + } + + if (dbChanged) { + // Make sure we haven't removed everything. + final Cursor c = context.getContentResolver().query( + LauncherSettings.Favorites.CONTENT_URI, null, null, null, null); + boolean hasData = c.moveToNext(); + c.close(); + if (!hasData) { + throw new Exception("Removed every thing during grid resize"); + } + } + + return true; + } catch (Exception e) { + Log.e(TAG, "Error during grid migration", e); + + return false; + } finally { + Log.v(TAG, "Workspace migration completed in " + + (System.currentTimeMillis() - migrationStartTime)); + + // Save current configuration, so that the migration does not run again. + prefs.edit() + .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString) + .putString(KEY_MIGRATION_SRC_HOTSEAT_SIZE, hotseatSizeString) + .remove(KEY_MIGRATION_WIDGET_MINSIZE) + .apply(); + } + } } 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/userevent/Logger.java b/src/com/android/launcher3/userevent/Logger.java new file mode 100644 index 000000000..ae9041a4b --- /dev/null +++ b/src/com/android/launcher3/userevent/Logger.java @@ -0,0 +1,208 @@ +package com.android.launcher3.userevent; + +import com.android.launcher3.ShortcutInfo; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.Stats; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.userevent.nano.LauncherLogProto; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action; +import com.android.launcher3.userevent.nano.LauncherLogProto.Target; +import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent; + +import android.content.Context; +import android.os.Bundle; +import android.util.Log; + +import java.util.Locale; + +/** + * Creates {@LauncherLogProto} nano protobuf object that can be used for user event + * metrics analysis. + */ +public class Logger { + + private static final String TAG = "UserEventLogger"; + private static final boolean DEBUG = false; + + private long mActionDurationMillis; + private long mElapsedContainerMillis; + private long mElapsedSessionMillis; + + private Context mContext; + + public Logger(Context context) { + mContext = context; + } + + public void logAppLaunch(String provider, ShortcutInfo shortcut, Bundle bundle) { + LauncherEvent event = new LauncherEvent(); + event.action = new Action(); + event.action.type = Action.TOUCH; + event.action.touch = Action.TAP; + + event.srcTarget = new Target(); + event.srcTarget.type = Target.ITEM; + event.srcTarget.itemType = LauncherLogProto.APP_ICON; + // TODO: package hash name should be different per device. + event.srcTarget.packageNameHash = provider.hashCode(); + + event.srcTarget.parent = new Target(); + String subContainer = bundle.getString(Stats.SOURCE_EXTRA_SUB_CONTAINER); + + if (shortcut != null) { + event.srcTarget.parent.containerType = getContainerType(shortcut); + event.srcTarget.pageIndex = (int) shortcut.screenId; + event.srcTarget.gridX = shortcut.cellX; + event.srcTarget.gridX = shortcut.cellY; + } + if (subContainer != null) { + event.srcTarget.parent.type = Target.CONTAINER; + if (subContainer.equals(Stats.SUB_CONTAINER_FOLDER)) { + event.srcTarget.parent.containerType = LauncherLogProto.FOLDER; + } else if (subContainer.equals(Stats.SUB_CONTAINER_ALL_APPS_A_Z)) { + event.srcTarget.parent.containerType = LauncherLogProto.ALLAPPS; + } else if (subContainer.equals(Stats.CONTAINER_HOTSEAT)) { + event.srcTarget.parent.containerType = LauncherLogProto.HOTSEAT; + } else if (subContainer.equals(Stats.SUB_CONTAINER_ALL_APPS_PREDICTION)) { + event.srcTarget.parent.containerType = LauncherLogProto.PREDICTION; + } + + if (DEBUG) { + Log.d(TAG, String.format("parent bundle: %s %s %s %s", + bundle.getString(Stats.SOURCE_EXTRA_CONTAINER), + bundle.getString(Stats.SOURCE_EXTRA_CONTAINER_PAGE), + bundle.getString(Stats.SOURCE_EXTRA_SUB_CONTAINER), + bundle.getString(Stats.SOURCE_EXTRA_SUB_CONTAINER_PAGE))); + } + } + + + // Assign timeToAction + event.elapsedContainerMillis = System.currentTimeMillis() - mElapsedContainerMillis; + event.elapsedSessionMillis = System.currentTimeMillis() - mElapsedSessionMillis; + + // Debug + processLauncherEvent(event); + } + + public void resetElapsedContainerMillis() { + mElapsedContainerMillis = System.currentTimeMillis(); + } + + public void resetElapsedSessionMillis() { + mElapsedSessionMillis = System.currentTimeMillis(); + } + + // + // Debugging helper methods. + // toString() cannot be overriden inside auto generated {@link LauncherLogProto}. + // Note: switch statement cannot be replaced with reflection as proguard strips the constants + + private static void processLauncherEvent(LauncherEvent ev) { + if (DEBUG) { + if (ev.action.touch == Action.TAP && ev.srcTarget.itemType == LauncherLogProto.APP_ICON) { + Log.d(TAG, String.format(Locale.US, "action:%s target:%s\n\telapsed container %d ms session %d ms", + getActionStr(ev.action), + getTargetStr(ev.srcTarget), + ev.elapsedContainerMillis, + ev.elapsedSessionMillis)); + } + } + } + + private static int getContainerType(ShortcutInfo shortcut) { + switch ((int) shortcut.container) { + case LauncherSettings.Favorites.CONTAINER_DESKTOP: return LauncherLogProto.WORKSPACE; + case LauncherSettings.Favorites.CONTAINER_HOTSEAT: return LauncherLogProto.HOTSEAT; + default: + return (int) shortcut.container; + } + } + + private static String getActionStr(Action action) { + switch(action.touch) { + case Action.TAP: return "TAP"; + case Action.LONGPRESS: return "LONGPRESS"; + case Action.DRAGDROP: return "DRAGDROP"; + case Action.PINCH: return "PINCH"; + default: return "UNKNOWN"; + } + } + + private static String getTargetStr(Target t) { + String typeStr; + switch (t.type) { + case LauncherLogProto.Target.ITEM: + return getItemStr(t); + case LauncherLogProto.Target.CONTROL: + return getControlStr(t); + case LauncherLogProto.Target.CONTAINER: + return getContainerStr(t); + default: + return "UNKNOWN TARGET TYPE"; + } + } + + private static String getItemStr(Target t) { + String typeStr = ""; + switch(t.itemType){ + case LauncherLogProto.APP_ICON: typeStr = "ICON"; break; + case LauncherLogProto.SHORTCUT: typeStr = "SHORTCUT"; break; + case LauncherLogProto.WIDGET: typeStr = "WIDGET"; break; + default: typeStr = "UNKNOWN"; + } + + return typeStr + " " + t.packageNameHash + " grid=(" + t.gridX + "," + t.gridY + ") " + + getContainerStr(t.parent); + } + + private static String getControlStr(Target t) { + switch(t.controlType) { + case LauncherLogProto.ALL_APPS_BUTTON: return "ALL_APPS_BUTTON"; + case LauncherLogProto.WIDGETS_BUTTON: return "WIDGETS_BUTTON"; + case LauncherLogProto.WALLPAPER_BUTTON: return "WALLPAPER_BUTTON"; + case LauncherLogProto.SETTINGS_BUTTON: return "SETTINGS_BUTTON"; + case LauncherLogProto.REMOVE_TARGET: return "REMOVE_TARGET"; + case LauncherLogProto.UNINSTALL_TARGET: return "UNINSTALL_TARGET"; + case LauncherLogProto.APPINFO_TARGET: return "APPINFO_TARGET"; + case LauncherLogProto.RESIZE_HANDLE: return "RESIZE_HANDLE"; + case LauncherLogProto.FAST_SCROLL_HANDLE: return "FAST_SCROLL_HANDLE"; + default: return "UNKNOWN"; + } + } + + private static String getContainerStr(Target t) { + String str; + Log.d(TAG, "t.containerType" + t.containerType); + switch (t.containerType) { + case LauncherLogProto.WORKSPACE: + str = "WORKSPACE"; + break; + case LauncherLogProto.HOTSEAT: + str = "HOTSEAT"; + break; + case LauncherLogProto.FOLDER: + str = "FOLDER"; + break; + case LauncherLogProto.ALLAPPS: + str = "ALLAPPS"; + break; + case LauncherLogProto.WIDGETS: + str = "WIDGETS"; + break; + case LauncherLogProto.OVERVIEW: + str = "OVERVIEW"; + break; + case LauncherLogProto.PREDICTION: + str = "PREDICTION"; + break; + case LauncherLogProto.SEARCHRESULT: + str = "SEARCHRESULT"; + break; + default: + str = "UNKNOWN"; + } + return str + " id=" + t.pageIndex; + } +} + diff --git a/src/com/android/launcher3/util/ConfigMonitor.java b/src/com/android/launcher3/util/ConfigMonitor.java index c61fa8859..bdb16390b 100644 --- a/src/com/android/launcher3/util/ConfigMonitor.java +++ b/src/com/android/launcher3/util/ConfigMonitor.java @@ -23,26 +23,30 @@ import android.content.IntentFilter; import android.content.res.Configuration; import android.util.Log; +import com.android.launcher3.Utilities; + /** * {@link BroadcastReceiver} which watches configuration changes and - * restarts the process in case changes which affect the device profile. + * restarts the process in case changes which affect the device profile occur. */ public class ConfigMonitor extends BroadcastReceiver { private final Context mContext; private final float mFontScale; + private final int mDensity; public ConfigMonitor(Context context) { mContext = context; Configuration config = context.getResources().getConfiguration(); mFontScale = config.fontScale; + mDensity = getDensity(config); } @Override public void onReceive(Context context, Intent intent) { Configuration config = context.getResources().getConfiguration(); - if (mFontScale != config.fontScale) { + if (mFontScale != config.fontScale || mDensity != getDensity(config)) { Log.d("ConfigMonitor", "Configuration changed, restarting launcher"); mContext.unregisterReceiver(this); android.os.Process.killProcess(android.os.Process.myPid()); @@ -52,4 +56,8 @@ public class ConfigMonitor extends BroadcastReceiver { public void register() { mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED)); } + + private static int getDensity(Configuration config) { + return Utilities.ATLEAST_JB_MR1 ? config.densityDpi : 0; + } } 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 deleted file mode 100644 index b9fccbcfd..000000000 --- a/src/com/android/launcher3/util/WallpaperUtils.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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.annotation.TargetApi; -import android.app.WallpaperManager; -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.Utilities; - -/** - * Utility methods for wallpaper management. - */ -public final class WallpaperUtils { - - public static final String WALLPAPER_WIDTH_KEY = "wallpaper.width"; - 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 - - int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, -1); - int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, -1); - - if (savedWidth == -1 || savedHeight == -1) { - if (!fallBackToDefaults) { - return; - } else { - savedWidth = defaultWallpaperSize.x; - savedHeight = defaultWallpaperSize.y; - } - } - - if (savedWidth != wallpaperManager.getDesiredMinimumWidth() || - savedHeight != wallpaperManager.getDesiredMinimumHeight()) { - wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight); - } - } - - /** - * 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) { - float aspectRatio = width / (float) height; - - // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width - // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width - // We will use these two data points to extrapolate how much the wallpaper parallax effect - // to span (ie travel) at any aspect ratio: - - final float ASPECT_RATIO_LANDSCAPE = 16/10f; - final float ASPECT_RATIO_PORTRAIT = 10/16f; - final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f; - final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f; - - // To find out the desired width at different aspect ratios, we use the following two - // formulas, where the coefficient on x is the aspect ratio (width/height): - // (16/10)x + y = 1.5 - // (10/16)x + y = 1.2 - // We solve for x and y and end up with a final formula: - final float x = - (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) / - (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT); - final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT; - return x * aspectRatio + y; - } - - private static Point sDefaultWallpaperSize; - - @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); - } - - // We need to ensure that there is enough extra space in the wallpaper - // for the intended parallax effects - final int defaultWidth, defaultHeight; - if (res.getConfiguration().smallestScreenWidthDp >= 720) { - defaultWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim)); - defaultHeight = maxDim; - } else { - defaultWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim); - defaultHeight = maxDim; - } - sDefaultWallpaperSize = new Point(defaultWidth, defaultHeight); - } - return sDefaultWallpaperSize; - } -} diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index 75c84c32f..ec2bd70b1 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(); @@ -202,7 +203,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 0173eb771..4c901c2be 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -20,6 +20,7 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.graphics.drawable.InsetDrawable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView.State; import android.util.AttributeSet; @@ -31,10 +32,9 @@ 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; +import com.android.launcher3.folder.Folder; import com.android.launcher3.IconCache; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; @@ -44,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; @@ -85,7 +86,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"); @@ -239,9 +240,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; @@ -253,7 +256,7 @@ public class WidgetsContainerView extends BaseContainerView @Override public boolean supportsFlingToDelete() { - return false; + return true; } @Override @@ -306,7 +309,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 f2d00271e..33f2ae76d 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -64,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) { @@ -202,12 +199,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 { diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java new file mode 100644 index 000000000..46dac0aab --- /dev/null +++ b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java @@ -0,0 +1,321 @@ +package com.android.launcher3.model; + +import android.content.ContentValues; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Point; +import android.test.ProviderTestCase2; + +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.LauncherModel; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.config.ProviderConfig; +import com.android.launcher3.util.TestLauncherProvider; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; + +/** + * Unit tests for {@link GridSizeMigrationTask} + */ +public class GridSizeMigrationTaskTest extends ProviderTestCase2<TestLauncherProvider> { + + private static final long DESKTOP = LauncherSettings.Favorites.CONTAINER_DESKTOP; + private static final long HOTSEAT = LauncherSettings.Favorites.CONTAINER_HOTSEAT; + + private static final int APPLICATION = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; + private static final int SHORTCUT = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; + + private static final String TEST_PACKAGE = "com.android.launcher3.validpackage"; + private static final String VALID_INTENT = + new Intent(Intent.ACTION_MAIN).setPackage(TEST_PACKAGE).toUri(0); + + private HashSet<String> mValidPackages; + private InvariantDeviceProfile mIdp; + + public GridSizeMigrationTaskTest() { + super(TestLauncherProvider.class, ProviderConfig.AUTHORITY); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mValidPackages = new HashSet<>(); + mValidPackages.add(TEST_PACKAGE); + + mIdp = new InvariantDeviceProfile(); + } + + public void testHotseatMigration_apps_dropped() throws Exception { + long[] hotseatItems = { + addItem(APPLICATION, 0, HOTSEAT, 0, 0), + addItem(SHORTCUT, 1, HOTSEAT, 0, 0), + -1, + addItem(SHORTCUT, 3, HOTSEAT, 0, 0), + addItem(APPLICATION, 4, HOTSEAT, 0, 0), + }; + + new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, 5, 2, 3, 1) + .migrateHotseat(); + // First & last items are dropped as they have the least weight. + verifyHotseat(hotseatItems[1], -1, hotseatItems[3]); + } + + public void testHotseatMigration_shortcuts_dropped() throws Exception { + long[] hotseatItems = { + addItem(APPLICATION, 0, HOTSEAT, 0, 0), + addItem(30, 1, HOTSEAT, 0, 0), + -1, + addItem(SHORTCUT, 3, HOTSEAT, 0, 0), + addItem(10, 4, HOTSEAT, 0, 0), + }; + + new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, 5, 2, 3, 1) + .migrateHotseat(); + // First & third items are dropped as they have the least weight. + verifyHotseat(hotseatItems[1], -1, hotseatItems[4]); + } + + private void verifyHotseat(long... sortedIds) { + int screenId = 0; + int total = 0; + + for (long id : sortedIds) { + Cursor c = getMockContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, + new String[]{LauncherSettings.Favorites._ID}, + "container=-101 and screen=" + screenId, null, null, null); + + if (id == -1) { + assertEquals(0, c.getCount()); + } else { + assertEquals(1, c.getCount()); + c.moveToNext(); + assertEquals(id, c.getLong(0)); + total ++; + } + c.close(); + + screenId++; + } + + // Verify that not other entry exist in the DB. + Cursor c = getMockContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, + new String[]{LauncherSettings.Favorites._ID}, + "container=-101", null, null, null); + assertEquals(total, c.getCount()); + c.close(); + } + + public void testWorkspace_empty_row_column_removed() throws Exception { + long[][][] ids = createGrid(new int[][][]{{ + { 0, 0, -1, 1}, + { 3, 1, -1, 4}, + { -1, -1, -1, -1}, + { 5, 2, -1, 6}, + }}); + + new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(), + new Point(4, 4), new Point(3, 3)).migrateWorkspace(); + + // Column 2 and row 2 got removed. + verifyWorkspace(new long[][][] {{ + {ids[0][0][0], ids[0][0][1], ids[0][0][3]}, + {ids[0][1][0], ids[0][1][1], ids[0][1][3]}, + {ids[0][3][0], ids[0][3][1], ids[0][3][3]}, + }}); + } + + public void testWorkspace_new_screen_created() throws Exception { + long[][][] ids = createGrid(new int[][][]{{ + { 0, 0, 0, 1}, + { 3, 1, 0, 4}, + { -1, -1, -1, -1}, + { 5, 2, -1, 6}, + }}); + + new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(), + new Point(4, 4), new Point(3, 3)).migrateWorkspace(); + + // Items in the second column get moved to new screen + verifyWorkspace(new long[][][] {{ + {ids[0][0][0], ids[0][0][1], ids[0][0][3]}, + {ids[0][1][0], ids[0][1][1], ids[0][1][3]}, + {ids[0][3][0], ids[0][3][1], ids[0][3][3]}, + }, { + {ids[0][0][2], ids[0][1][2], -1}, + }}); + } + + public void testWorkspace_items_merged_in_next_screen() throws Exception { + long[][][] ids = createGrid(new int[][][]{{ + { 0, 0, 0, 1}, + { 3, 1, 0, 4}, + { -1, -1, -1, -1}, + { 5, 2, -1, 6}, + },{ + { 0, 0, -1, 1}, + { 3, 1, -1, 4}, + }}); + + new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(), + new Point(4, 4), new Point(3, 3)).migrateWorkspace(); + + // Items in the second column of the first screen should get placed on the 3rd + // row of the second screen + verifyWorkspace(new long[][][] {{ + {ids[0][0][0], ids[0][0][1], ids[0][0][3]}, + {ids[0][1][0], ids[0][1][1], ids[0][1][3]}, + {ids[0][3][0], ids[0][3][1], ids[0][3][3]}, + }, { + {ids[1][0][0], ids[1][0][1], ids[1][0][3]}, + {ids[1][1][0], ids[1][1][1], ids[1][1][3]}, + {ids[0][0][2], ids[0][1][2], -1}, + }}); + } + + public void testWorkspace_items_not_merged_in_next_screen() throws Exception { + // First screen has 2 items that need to be moved, but second screen has only one + // empty space after migration (top-left corner) + long[][][] ids = createGrid(new int[][][]{{ + { 0, 0, 0, 1}, + { 3, 1, 0, 4}, + { -1, -1, -1, -1}, + { 5, 2, -1, 6}, + },{ + { -1, 0, -1, 1}, + { 3, 1, -1, 4}, + { -1, -1, -1, -1}, + { 5, 2, -1, 6}, + }}); + + new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(), + new Point(4, 4), new Point(3, 3)).migrateWorkspace(); + + // Items in the second column of the first screen should get placed on a new screen. + verifyWorkspace(new long[][][] {{ + {ids[0][0][0], ids[0][0][1], ids[0][0][3]}, + {ids[0][1][0], ids[0][1][1], ids[0][1][3]}, + {ids[0][3][0], ids[0][3][1], ids[0][3][3]}, + }, { + { -1, ids[1][0][1], ids[1][0][3]}, + {ids[1][1][0], ids[1][1][1], ids[1][1][3]}, + {ids[1][3][0], ids[1][3][1], ids[1][3][3]}, + }, { + {ids[0][0][2], ids[0][1][2], -1}, + }}); + } + + /** + * Initializes the DB with dummy elements to represent the provided grid structure. + * @param typeArray A 3d array of item types. {@see #addItem(int, long, long, int, int)} for + * type definitions. The first dimension represents the screens and the next + * two represent the workspace grid. + * @return the same grid representation where each entry is the corresponding item id. + */ + private long[][][] createGrid(int[][][] typeArray) throws Exception { + long[][][] ids = new long[typeArray.length][][]; + + for (int i = 0; i < typeArray.length; i++) { + // Add screen to DB + long screenId = LauncherSettings.Settings.call(getMockContentResolver(), + LauncherSettings.Settings.METHOD_NEW_SCREEN_ID) + .getLong(LauncherSettings.Settings.EXTRA_VALUE); + + ContentValues v = new ContentValues(); + v.put(LauncherSettings.WorkspaceScreens._ID, screenId); + v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i); + getMockContentResolver().insert(LauncherSettings.WorkspaceScreens.CONTENT_URI, v); + + ids[i] = new long[typeArray[i].length][]; + for (int y = 0; y < typeArray[i].length; y++) { + ids[i][y] = new long[typeArray[i][y].length]; + for (int x = 0; x < typeArray[i][y].length; x++) { + if (typeArray[i][y][x] < 0) { + // Empty cell + ids[i][y][x] = -1; + } else { + ids[i][y][x] = addItem(typeArray[i][y][x], screenId, DESKTOP, x, y); + } + } + } + } + return ids; + } + + /** + * Verifies that the workspace items are arranged in the provided order. + * @param ids A 3d array where the first dimension represents the screen, and the rest two + * represent the workspace grid. + */ + private void verifyWorkspace(long[][][] ids) { + ArrayList<Long> allScreens = LauncherModel.loadWorkspaceScreensDb(getMockContext()); + assertEquals(ids.length, allScreens.size()); + int total = 0; + + for (int i = 0; i < ids.length; i++) { + long screenId = allScreens.get(i); + for (int y = 0; y < ids[i].length; y++) { + for (int x = 0; x < ids[i][y].length; x++) { + long id = ids[i][y][x]; + + Cursor c = getMockContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, + new String[]{LauncherSettings.Favorites._ID}, + "container=-100 and screen=" + screenId + + " and cellX=" + x + " and cellY=" + y, null, null, null); + if (id == -1) { + assertEquals(0, c.getCount()); + } else { + assertEquals(1, c.getCount()); + c.moveToNext(); + assertEquals(id, c.getLong(0)); + total++; + } + c.close(); + } + } + } + + // Verify that not other entry exist in the DB. + Cursor c = getMockContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, + new String[]{LauncherSettings.Favorites._ID}, + "container=-100", null, null, null); + assertEquals(total, c.getCount()); + c.close(); + } + + /** + * Adds a dummy item in the DB. + * @param type {@link #APPLICATION} or {@link #SHORTCUT} or >= 2 for + * folder (where the type represents the number of items in the folder). + */ + private long addItem(int type, long screen, long container, int x, int y) throws Exception { + long id = LauncherSettings.Settings.call(getMockContentResolver(), + LauncherSettings.Settings.METHOD_NEW_ITEM_ID) + .getLong(LauncherSettings.Settings.EXTRA_VALUE); + + ContentValues values = new ContentValues(); + values.put(LauncherSettings.Favorites._ID, id); + values.put(LauncherSettings.Favorites.CONTAINER, container); + values.put(LauncherSettings.Favorites.SCREEN, screen); + values.put(LauncherSettings.Favorites.CELLX, x); + values.put(LauncherSettings.Favorites.CELLY, y); + values.put(LauncherSettings.Favorites.SPANX, 1); + values.put(LauncherSettings.Favorites.SPANY, 1); + + if (type == APPLICATION || type == SHORTCUT) { + values.put(LauncherSettings.Favorites.ITEM_TYPE, type); + values.put(LauncherSettings.Favorites.INTENT, VALID_INTENT); + } else { + values.put(LauncherSettings.Favorites.ITEM_TYPE, + LauncherSettings.Favorites.ITEM_TYPE_FOLDER); + // Add folder items. + for (int i = 0; i < type; i++) { + addItem(APPLICATION, 0, id, 0, 0); + } + } + + getMockContentResolver().insert(LauncherSettings.Favorites.CONTENT_URI, values); + return id; + } +} diff --git a/tests/src/com/android/launcher3/util/TestLauncherProvider.java b/tests/src/com/android/launcher3/util/TestLauncherProvider.java new file mode 100644 index 000000000..aef3240ca --- /dev/null +++ b/tests/src/com/android/launcher3/util/TestLauncherProvider.java @@ -0,0 +1,40 @@ +package com.android.launcher3.util; + +import android.content.Context; + +import com.android.launcher3.LauncherProvider; + +/** + * An extension of LauncherProvider backed up by in-memory database. + */ +public class TestLauncherProvider extends LauncherProvider { + + @Override + public boolean onCreate() { + return true; + } + + @Override + protected synchronized void createDbIfNotExists() { + if (mOpenHelper == null) { + mOpenHelper = new MyDatabaseHelper(getContext(), this); + } + } + + @Override + protected void notifyListeners() { } + + private static class MyDatabaseHelper extends DatabaseHelper { + public MyDatabaseHelper(Context context, LauncherProvider provider) { + super(context, provider, null); + } + + @Override + protected long getDefaultUserSerial() { + return 0; + } + + @Override + protected void onEmptyDbCreated() { } + } +}
\ No newline at end of file |