diff options
author | Chilun <chilunhuang@google.com> | 2018-11-29 20:38:22 +0800 |
---|---|---|
committer | Chilun Huang <chilunhuang@google.com> | 2018-12-13 09:21:47 +0000 |
commit | 3516bd89d9665787fdb3db73e4922361bb307380 (patch) | |
tree | 70ab33b412ec5fa1c67cd955b31836b0720138a5 /SecondaryDisplayLauncher | |
parent | 4d64da3259d83fa85b6db16a926276a5e283ac3f (diff) | |
download | android_packages_apps_Trebuchet-3516bd89d9665787fdb3db73e4922361bb307380.tar.gz android_packages_apps_Trebuchet-3516bd89d9665787fdb3db73e4922361bb307380.tar.bz2 android_packages_apps_Trebuchet-3516bd89d9665787fdb3db73e4922361bb307380.zip |
Add a secondary launcher activity (1/2)
This secondary launcher activity with new category SECONDARY_HOME and
multiple instance supporrted could be used on sencondary display.
Bug: 118206886
Bug: 114329798
Test: Manual
Change-Id: Ibaecf8ef7614389760d0fcc547ef6d378a921583
Diffstat (limited to 'SecondaryDisplayLauncher')
23 files changed, 1240 insertions, 0 deletions
diff --git a/SecondaryDisplayLauncher/Android.mk b/SecondaryDisplayLauncher/Android.mk new file mode 100644 index 000000000..4fb5eba17 --- /dev/null +++ b/SecondaryDisplayLauncher/Android.mk @@ -0,0 +1,39 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_USE_AAPT2 := true +LOCAL_AAPT2_ONLY := true +LOCAL_MODULE_TAGS := optional + +LOCAL_STATIC_ANDROID_LIBRARIES := androidx.design_design + +LOCAL_STATIC_JAVA_LIBRARIES := LauncherPluginLib + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_PROGUARD_ENABLED := disabled + +LOCAL_SDK_VERSION := current +LOCAL_MIN_SDK_VERSION := 21 +LOCAL_MODULE := SecondaryDisplayLauncherLib +LOCAL_PRIVILEGED_MODULE := true + +include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/SecondaryDisplayLauncher/AndroidManifest.xml b/SecondaryDisplayLauncher/AndroidManifest.xml new file mode 100644 index 000000000..ebf6b02cb --- /dev/null +++ b/SecondaryDisplayLauncher/AndroidManifest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<manifest + xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.launcher3"> + + <application> + + <activity + android:name="com.android.launcher3.SecondaryDisplayLauncher" + android:theme="@style/SecondaryLauncherTheme" + android:launchMode="singleTop" + android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density" + android:enabled="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.SECONDARY_HOME" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + + </application> +</manifest> diff --git a/SecondaryDisplayLauncher/res/drawable/ic_apps.xml b/SecondaryDisplayLauncher/res/drawable/ic_apps.xml new file mode 100644 index 000000000..db779c2d1 --- /dev/null +++ b/SecondaryDisplayLauncher/res/drawable/ic_apps.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT 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 android:height="24dp" android:tint="#FFFFFF" + android:viewportHeight="24.0" android:viewportWidth="24.0" + android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="#FF000000" android:pathData="M4,8h4L8,4L4,4v4zM10,20h4v-4h-4v4zM4,20h4v-4L4,16v4zM4,14h4v-4L4,10v4zM10,14h4v-4h-4v4zM16,4v4h4L20,4h-4zM10,8h4L14,4h-4v4zM16,14h4v-4h-4v4zM16,20h4v-4h-4v4z"/> +</vector> diff --git a/SecondaryDisplayLauncher/res/drawable/ic_settings.xml b/SecondaryDisplayLauncher/res/drawable/ic_settings.xml new file mode 100644 index 000000000..c269c3baf --- /dev/null +++ b/SecondaryDisplayLauncher/res/drawable/ic_settings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT 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 android:height="24dp" android:tint="#FFFFFF" + android:viewportHeight="24.0" android:viewportWidth="24.0" + android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="#FF000000" android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/> +</vector> diff --git a/SecondaryDisplayLauncher/res/layout-sw600dp/secondary_display_launcher.xml b/SecondaryDisplayLauncher/res/layout-sw600dp/secondary_display_launcher.xml new file mode 100644 index 000000000..46f1674e4 --- /dev/null +++ b/SecondaryDisplayLauncher/res/layout-sw600dp/secondary_display_launcher.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<androidx.coordinatorlayout.widget.CoordinatorLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/RootView" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/launcher_bg_color" + android:fitsSystemWindows="true" > + + <GridView + android:id="@+id/pinned_app_grid" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="@dimen/app_grid_margin_top" + android:layout_marginStart="@dimen/app_grid_margin_left" + android:layout_marginEnd="@dimen/app_grid_margin_right" + android:columnWidth="@dimen/app_list_col_width" + android:verticalSpacing="@dimen/app_list_horizontal_spacing" + android:horizontalSpacing="@dimen/app_list_vertical_spacing" + android:numColumns="auto_fit" /> + + <ImageButton + android:id="@+id/OptionsButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom|start" + android:layout_marginStart="@dimen/options_button_margin" + android:layout_marginBottom="@dimen/options_button_margin" + android:src="@drawable/ic_settings" + android:background="@null"/> + + <FrameLayout + android:layout_width="@dimen/app_picker_width" + android:layout_height="@dimen/app_picker_height" + android:layout_gravity="bottom|end" + android:layout_margin="@dimen/app_picker_fab_margin"> + + <include layout="@layout/app_picker_layout"/> + </FrameLayout> +</androidx.coordinatorlayout.widget.CoordinatorLayout> diff --git a/SecondaryDisplayLauncher/res/layout-sw720dp/secondary_display_launcher.xml b/SecondaryDisplayLauncher/res/layout-sw720dp/secondary_display_launcher.xml new file mode 100644 index 000000000..6653a7796 --- /dev/null +++ b/SecondaryDisplayLauncher/res/layout-sw720dp/secondary_display_launcher.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<androidx.coordinatorlayout.widget.CoordinatorLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/RootView" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/launcher_bg_color" + android:fitsSystemWindows="true" > + + <GridView + android:id="@+id/pinned_app_grid" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="@dimen/app_grid_margin_top" + android:layout_marginStart="@dimen/app_grid_margin_left" + android:layout_marginEnd="@dimen/app_grid_margin_right" + android:columnWidth="@dimen/app_list_col_width" + android:verticalSpacing="@dimen/app_list_horizontal_spacing" + android:horizontalSpacing="@dimen/app_list_vertical_spacing" + android:numColumns="auto_fit" /> + + <ImageButton + android:id="@+id/OptionsButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom|start" + android:layout_marginStart="@dimen/options_button_margin" + android:layout_marginBottom="@dimen/options_button_margin" + android:src="@drawable/ic_settings" + android:background="@null"/> + + <FrameLayout + android:layout_width="@dimen/app_picker_width" + android:layout_height="@dimen/app_picker_height" + android:layout_gravity="bottom|end" + android:layout_marginEnd="@dimen/app_picker_fab_margin" + android:layout_marginBottom="@dimen/app_picker_fab_margin"> + + <include layout="@layout/app_picker_layout"/> + </FrameLayout> +</androidx.coordinatorlayout.widget.CoordinatorLayout> diff --git a/SecondaryDisplayLauncher/res/layout/app_grid_item.xml b/SecondaryDisplayLauncher/res/layout/app_grid_item.xml new file mode 100644 index 000000000..ee5158ad3 --- /dev/null +++ b/SecondaryDisplayLauncher/res/layout/app_grid_item.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center"> + + <ImageView + android:id="@+id/app_icon" + android:layout_width="@dimen/app_icon_width" + android:layout_height="@dimen/app_icon_height" /> + + <TextView + android:id="@+id/app_name" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:maxLines="1" /> +</LinearLayout> diff --git a/SecondaryDisplayLauncher/res/layout/app_picker_dialog.xml b/SecondaryDisplayLauncher/res/layout/app_picker_dialog.xml new file mode 100644 index 000000000..563a1afa7 --- /dev/null +++ b/SecondaryDisplayLauncher/res/layout/app_picker_dialog.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT 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" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <GridView + android:id="@+id/picker_app_grid" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="@dimen/app_grid_margin_top" + android:layout_marginStart="@dimen/app_grid_margin_left" + android:layout_marginEnd="@dimen/app_grid_margin_right" + android:columnWidth="@dimen/app_list_col_width" + android:verticalSpacing="@dimen/app_list_horizontal_spacing" + android:horizontalSpacing="@dimen/app_list_vertical_spacing" + android:numColumns="auto_fit" /> +</FrameLayout> diff --git a/SecondaryDisplayLauncher/res/layout/app_picker_layout.xml b/SecondaryDisplayLauncher/res/layout/app_picker_layout.xml new file mode 100644 index 000000000..fbaeac460 --- /dev/null +++ b/SecondaryDisplayLauncher/res/layout/app_picker_layout.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<androidx.coordinatorlayout.widget.CoordinatorLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <android.support.design.circularreveal.cardview.CircularRevealCardView + android:id="@+id/FloatingSheet" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="invisible"> + + <GridView + android:id="@+id/app_grid" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="@dimen/app_grid_margin_top" + android:columnWidth="@dimen/app_list_col_width" + android:verticalSpacing="@dimen/app_list_horizontal_spacing" + android:horizontalSpacing="@dimen/app_list_vertical_spacing" + android:numColumns="auto_fit" /> + </android.support.design.circularreveal.cardview.CircularRevealCardView> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/FloatingActionButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom|end" + android:src="@drawable/ic_apps"/> +</androidx.coordinatorlayout.widget.CoordinatorLayout> diff --git a/SecondaryDisplayLauncher/res/layout/secondary_display_launcher.xml b/SecondaryDisplayLauncher/res/layout/secondary_display_launcher.xml new file mode 100644 index 000000000..49cd499a5 --- /dev/null +++ b/SecondaryDisplayLauncher/res/layout/secondary_display_launcher.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<androidx.coordinatorlayout.widget.CoordinatorLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/RootView" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/launcher_bg_color" + android:fitsSystemWindows="true" > + + <GridView + android:id="@+id/pinned_app_grid" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="@dimen/app_grid_margin_top" + android:layout_marginStart="@dimen/app_grid_margin_left" + android:layout_marginEnd="@dimen/app_grid_margin_right" + android:columnWidth="@dimen/app_list_col_width" + android:verticalSpacing="@dimen/app_list_horizontal_spacing" + android:horizontalSpacing="@dimen/app_list_vertical_spacing" + android:numColumns="auto_fit" /> + + <ImageButton + android:id="@+id/OptionsButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom|start" + android:layout_marginStart="@dimen/options_button_margin" + android:layout_marginBottom="@dimen/options_button_margin" + android:src="@drawable/ic_settings" + android:background="@null"/> + + <FrameLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="bottom|end" + android:layout_margin="@dimen/app_picker_fab_margin"> + + <include layout="@layout/app_picker_layout"/> + </FrameLayout> +</androidx.coordinatorlayout.widget.CoordinatorLayout> diff --git a/SecondaryDisplayLauncher/res/menu/context_menu.xml b/SecondaryDisplayLauncher/res/menu/context_menu.xml new file mode 100644 index 000000000..626384229 --- /dev/null +++ b/SecondaryDisplayLauncher/res/menu/context_menu.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/add_app_shortcut" + android:title="@string/add_app_shortcut" /> + <item android:id="@+id/set_wallpaper" + android:title="@string/set_wallpaper" /> +</menu> diff --git a/SecondaryDisplayLauncher/res/values-sw600dp/dimens.xml b/SecondaryDisplayLauncher/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..f33a8db40 --- /dev/null +++ b/SecondaryDisplayLauncher/res/values-sw600dp/dimens.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT 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> + <dimen name="app_picker_width">400dp</dimen> + <dimen name="app_picker_height">400dp</dimen> + <dimen name="app_picker_fab_margin">60dp</dimen> +</resources> diff --git a/SecondaryDisplayLauncher/res/values-sw720dp/dimens.xml b/SecondaryDisplayLauncher/res/values-sw720dp/dimens.xml new file mode 100644 index 000000000..524e52af7 --- /dev/null +++ b/SecondaryDisplayLauncher/res/values-sw720dp/dimens.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT 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> + <dimen name="app_picker_width">660dp</dimen> + <dimen name="app_picker_height">660dp</dimen> + <dimen name="app_picker_fab_margin">70dp</dimen> +</resources> diff --git a/SecondaryDisplayLauncher/res/values/colors.xml b/SecondaryDisplayLauncher/res/values/colors.xml new file mode 100644 index 000000000..66b41a2ba --- /dev/null +++ b/SecondaryDisplayLauncher/res/values/colors.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/colors.xml +** +** Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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="launcher_bg_color">#884e8391</color> +</resources> diff --git a/SecondaryDisplayLauncher/res/values/dimens.xml b/SecondaryDisplayLauncher/res/values/dimens.xml new file mode 100644 index 000000000..7cca60719 --- /dev/null +++ b/SecondaryDisplayLauncher/res/values/dimens.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT 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> + <dimen name="app_list_col_width">72dp</dimen> + <dimen name="app_list_horizontal_spacing">24dp</dimen> + <dimen name="app_list_vertical_spacing">24dp</dimen> + <dimen name="app_icon_width">64dp</dimen> + <dimen name="app_icon_height">64dp</dimen> + <dimen name="app_grid_margin_top">24dp</dimen> + <dimen name="app_grid_margin_left">8dp</dimen> + <dimen name="app_grid_margin_right">8dp</dimen> + + <dimen name="app_picker_width">300dp</dimen> + <dimen name="app_picker_height">300dp</dimen> + <dimen name="app_picker_fab_margin">20dp</dimen> + <dimen name="options_button_margin">20dp</dimen> +</resources> diff --git a/SecondaryDisplayLauncher/res/values/strings.xml b/SecondaryDisplayLauncher/res/values/strings.xml new file mode 100644 index 000000000..0f906c483 --- /dev/null +++ b/SecondaryDisplayLauncher/res/values/strings.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +* Copyright (C) 2018 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT 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"> + <string name="app_name">SecondaryDisplayLauncher</string> + <string name="couldnt_launch">Couldn\'t launch the activity</string> + <string name="add_app_shortcut">Add app shortcut</string> + <string name="set_wallpaper">Set wallpaper</string> +</resources> diff --git a/SecondaryDisplayLauncher/res/values/styles.xml b/SecondaryDisplayLauncher/res/values/styles.xml new file mode 100644 index 000000000..4bfda6a2a --- /dev/null +++ b/SecondaryDisplayLauncher/res/values/styles.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +* Copyright (C) 2018 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT 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="SecondaryLauncherTheme" parent="Theme.AppCompat.Light.NoActionBar" > + <item name="android:windowShowWallpaper">true</item> + <item name="android:windowNoTitle">true</item> + <item name="android:windowIsTranslucent">true</item> + <item name="android:windowTranslucentStatus">true</item> + <item name="android:windowTranslucentNavigation">true</item> + <item name="android:colorBackgroundCacheHint">@null</item> + <item name="android:windowBackground">@android:color/transparent</item> + </style> +</resources> diff --git a/SecondaryDisplayLauncher/src/com/android/launcher3/AppEntry.java b/SecondaryDisplayLauncher/src/com/android/launcher3/AppEntry.java new file mode 100644 index 000000000..3017b81c1 --- /dev/null +++ b/SecondaryDisplayLauncher/src/com/android/launcher3/AppEntry.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.graphics.drawable.Drawable; + +/** An entry that represents a single activity that can be launched. */ +public class AppEntry { + + private String mLabel; + private Drawable mIcon; + private Intent mLaunchIntent; + + AppEntry(ResolveInfo info, PackageManager packageManager) { + mLabel = info.loadLabel(packageManager).toString(); + mIcon = info.loadIcon(packageManager); + mLaunchIntent = new Intent(); + mLaunchIntent.setComponent(new ComponentName(info.activityInfo.packageName, + info.activityInfo.name)); + } + + String getLabel() { + return mLabel; + } + + Drawable getIcon() { + return mIcon; + } + + Intent getLaunchIntent() { return mLaunchIntent; } + + ComponentName getComponentName() { + return mLaunchIntent.getComponent(); + } + + @Override + public String toString() { + return mLabel; + } +} diff --git a/SecondaryDisplayLauncher/src/com/android/launcher3/AppListAdapter.java b/SecondaryDisplayLauncher/src/com/android/launcher3/AppListAdapter.java new file mode 100644 index 000000000..aa115cb57 --- /dev/null +++ b/SecondaryDisplayLauncher/src/com/android/launcher3/AppListAdapter.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.launcher3.R; + +import java.util.List; + +/** Adapter for available apps list. */ +public class AppListAdapter extends ArrayAdapter<AppEntry> { + private final LayoutInflater mInflater; + + AppListAdapter(Context context) { + super(context, android.R.layout.simple_list_item_2); + mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + void setData(List<AppEntry> data) { + clear(); + if (data != null) { + addAll(data); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view; + + if (convertView == null) { + view = mInflater.inflate(R.layout.app_grid_item, parent, false); + } else { + view = convertView; + } + + AppEntry item = getItem(position); + ((ImageView)view.findViewById(R.id.app_icon)).setImageDrawable(item.getIcon()); + ((TextView)view.findViewById(R.id.app_name)).setText(item.getLabel()); + + return view; + } +} diff --git a/SecondaryDisplayLauncher/src/com/android/launcher3/AppListViewModel.java b/SecondaryDisplayLauncher/src/com/android/launcher3/AppListViewModel.java new file mode 100644 index 000000000..914fd5e01 --- /dev/null +++ b/SecondaryDisplayLauncher/src/com/android/launcher3/AppListViewModel.java @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.Application; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.AsyncTask; + +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; + +import java.util.ArrayList; +import java.util.List; + +/** + * A view model that provides a list of activities that can be launched. + */ +public class AppListViewModel extends AndroidViewModel { + + private final AppListLiveData mLiveData; + private final PackageIntentReceiver + mPackageIntentReceiver; + + public AppListViewModel(Application application) { + super(application); + mLiveData = new AppListLiveData(application); + mPackageIntentReceiver = new PackageIntentReceiver(mLiveData, application); + } + + public LiveData<List<AppEntry>> getAppList() { + return mLiveData; + } + + protected void onCleared() { + getApplication().unregisterReceiver(mPackageIntentReceiver); + } +} + +class AppListLiveData extends LiveData<List<AppEntry>> { + + private final PackageManager mPackageManager; + private int mCurrentDataVersion; + + public AppListLiveData(Context context) { + mPackageManager = context.getPackageManager(); + loadData(); + } + + void loadData() { + final int loadDataVersion = ++mCurrentDataVersion; + + new AsyncTask<Void, Void, List<AppEntry>>() { + @Override + protected List<AppEntry> doInBackground(Void... voids) { + Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); + mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); + + List<ResolveInfo> apps = mPackageManager.queryIntentActivities(mainIntent, + PackageManager.GET_META_DATA); + + List<AppEntry> entries = new ArrayList<>(); + if (apps != null) { + for (ResolveInfo app : apps) { + AppEntry entry = new AppEntry(app, mPackageManager); + entries.add(entry); + } + } + return entries; + } + + @Override + protected void onPostExecute(List<AppEntry> data) { + if (mCurrentDataVersion == loadDataVersion) { + setValue(data); + } + } + }.execute(); + } +} + +/** + * Receiver used to notify live data about app list changes. + */ +class PackageIntentReceiver extends BroadcastReceiver { + + private final AppListLiveData mLiveData; + + public PackageIntentReceiver(AppListLiveData liveData, Context context) { + mLiveData = liveData; + IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); + filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addAction(Intent.ACTION_PACKAGE_CHANGED); + filter.addDataScheme("package"); + context.registerReceiver(this, filter); + + // Register for events related to sdcard installation. + IntentFilter sdFilter = new IntentFilter(); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + context.registerReceiver(this, sdFilter); + } + + @Override + public void onReceive(Context context, Intent intent) { + mLiveData.loadData(); + } +}
\ No newline at end of file diff --git a/SecondaryDisplayLauncher/src/com/android/launcher3/PinnedAppListViewModel.java b/SecondaryDisplayLauncher/src/com/android/launcher3/PinnedAppListViewModel.java new file mode 100644 index 000000000..4f92038ae --- /dev/null +++ b/SecondaryDisplayLauncher/src/com/android/launcher3/PinnedAppListViewModel.java @@ -0,0 +1,120 @@ +/** + * Copyright (c) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 static com.android.launcher3.PinnedAppListViewModel.PINNED_APPS_KEY; + +import android.app.Application; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.AsyncTask; + +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * A view model that provides a list of activities that were pinned by user to always display on + * home screen. + * The pinned activities are stored in {@link SharedPreferences} to keep the sample simple :). + */ +public class PinnedAppListViewModel extends AndroidViewModel { + + final static String PINNED_APPS_KEY = "pinned_apps"; + + private final PinnedAppListLiveData mLiveData; + + public PinnedAppListViewModel(Application application) { + super(application); + mLiveData = new PinnedAppListLiveData(application); + } + + public LiveData<List<AppEntry>> getPinnedAppList() { + return mLiveData; + } +} + +class PinnedAppListLiveData extends LiveData<List<AppEntry>> { + + private final Context mContext; + private final PackageManager mPackageManager; + // Store listener reference, so it won't be GC-ed. + private final SharedPreferences.OnSharedPreferenceChangeListener mChangeListener; + private int mCurrentDataVersion; + + public PinnedAppListLiveData(Context context) { + mContext = context; + mPackageManager = context.getPackageManager(); + + final SharedPreferences prefs = context.getSharedPreferences(PINNED_APPS_KEY, 0); + mChangeListener = (preferences, key) -> { + loadData(); + }; + prefs.registerOnSharedPreferenceChangeListener(mChangeListener); + + loadData(); + } + + private void loadData() { + final int loadDataVersion = ++mCurrentDataVersion; + + new AsyncTask<Void, Void, List<AppEntry>>() { + @Override + protected List<AppEntry> doInBackground(Void... voids) { + List<AppEntry> entries = new ArrayList<>(); + + final SharedPreferences sp = mContext.getSharedPreferences(PINNED_APPS_KEY, 0); + final Set<String> pinnedAppsComponents = sp.getStringSet(PINNED_APPS_KEY, null); + if (pinnedAppsComponents == null) { + return null; + } + + for (String componentString : pinnedAppsComponents) { + final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); + mainIntent.setComponent(ComponentName.unflattenFromString(componentString)); + mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); + + final List<ResolveInfo> apps = mPackageManager.queryIntentActivities(mainIntent, + PackageManager.GET_META_DATA); + + if (apps != null) { + for (ResolveInfo app : apps) { + final AppEntry entry = new AppEntry(app, mPackageManager); + entries.add(entry); + } + } + } + + return entries; + } + + @Override + protected void onPostExecute(List<AppEntry> data) { + if (mCurrentDataVersion == loadDataVersion) { + setValue(data); + } + } + }.execute(); + } +}
\ No newline at end of file diff --git a/SecondaryDisplayLauncher/src/com/android/launcher3/PinnedAppPickerDialog.java b/SecondaryDisplayLauncher/src/com/android/launcher3/PinnedAppPickerDialog.java new file mode 100644 index 000000000..02e6e4a9c --- /dev/null +++ b/SecondaryDisplayLauncher/src/com/android/launcher3/PinnedAppPickerDialog.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.GridView; + +import androidx.fragment.app.DialogFragment; + +import com.android.launcher3.R; + +/** + * Callback to be invoked when an app was picked. + */ +interface AppPickedCallback { + void onAppPicked(AppEntry appEntry); +} + +/** + * Dialog that provides the user with a list of available apps to pin to the home screen. + */ +public class PinnedAppPickerDialog extends DialogFragment { + + private AppListAdapter mAppListAdapter; + private AppPickedCallback mAppPickerCallback; + + public PinnedAppPickerDialog() { + } + + public static PinnedAppPickerDialog newInstance(AppListAdapter appListAdapter, + AppPickedCallback callback) { + PinnedAppPickerDialog + frag = new PinnedAppPickerDialog(); + frag.mAppListAdapter = appListAdapter; + frag.mAppPickerCallback = callback; + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.app_picker_dialog, container); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + GridView appGridView = view.findViewById(R.id.picker_app_grid); + appGridView.setAdapter(mAppListAdapter); + appGridView.setOnItemClickListener((adapterView, itemView, position, id) -> { + final AppEntry entry = mAppListAdapter.getItem(position); + mAppPickerCallback.onAppPicked(entry); + dismiss(); + }); + } +}
\ No newline at end of file diff --git a/SecondaryDisplayLauncher/src/com/android/launcher3/SecondaryDisplayLauncher.java b/SecondaryDisplayLauncher/src/com/android/launcher3/SecondaryDisplayLauncher.java new file mode 100644 index 000000000..5e8b40246 --- /dev/null +++ b/SecondaryDisplayLauncher/src/com/android/launcher3/SecondaryDisplayLauncher.java @@ -0,0 +1,217 @@ +/** + * Copyright (c) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 static com.android.launcher3.PinnedAppListViewModel.PINNED_APPS_KEY; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.app.AlertDialog; +import android.app.Application; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.res.Configuration; +import android.os.Bundle; +import android.support.design.circularreveal.cardview.CircularRevealCardView; +import android.support.design.widget.FloatingActionButton; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewAnimationUtils; +import android.widget.GridView; +import android.widget.ImageButton; +import android.widget.PopupMenu; + +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; +import androidx.lifecycle.ViewModelProvider; +import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory; + +import java.util.HashSet; +import java.util.Set; + +/** + * Secondary launcher activity. It's launch mode is configured as "singleTop" to allow showing on + * multiple displays and to ensure a single instance per each display. + */ +public class SecondaryDisplayLauncher extends FragmentActivity implements AppPickedCallback, + PopupMenu.OnMenuItemClickListener { + + private AppListAdapter mAppListAdapter; + private AppListAdapter mPinnedAppListAdapter; + private CircularRevealCardView mAppDrawerView; + private FloatingActionButton mFab; + + private boolean mAppDrawerShown; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.secondary_display_launcher); + + mAppDrawerView = findViewById(R.id.FloatingSheet); + mFab = findViewById(R.id.FloatingActionButton); + + mFab.setOnClickListener((View v) -> { + showAppDrawer(true); + }); + + final ViewModelProvider viewModelProvider = new ViewModelProvider(getViewModelStore(), + new AndroidViewModelFactory((Application) getApplicationContext())); + + mPinnedAppListAdapter = new AppListAdapter(this); + final GridView pinnedAppGridView = findViewById(R.id.pinned_app_grid); + pinnedAppGridView.setAdapter(mPinnedAppListAdapter); + pinnedAppGridView.setOnItemClickListener((adapterView, view, position, id) -> { + final AppEntry entry = mPinnedAppListAdapter.getItem(position); + launch(entry.getLaunchIntent()); + }); + final PinnedAppListViewModel pinnedAppListViewModel = + viewModelProvider.get(PinnedAppListViewModel.class); + pinnedAppListViewModel.getPinnedAppList().observe(this, data -> { + mPinnedAppListAdapter.setData(data); + }); + + mAppListAdapter = new AppListAdapter(this); + final GridView appGridView = findViewById(R.id.app_grid); + appGridView.setAdapter(mAppListAdapter); + appGridView.setOnItemClickListener((adapterView, view, position, id) -> { + final AppEntry entry = mAppListAdapter.getItem(position); + launch(entry.getLaunchIntent()); + }); + final AppListViewModel appListViewModel = viewModelProvider.get(AppListViewModel.class); + appListViewModel.getAppList().observe(this, data -> { + mAppListAdapter.setData(data); + }); + + ImageButton optionsButton = findViewById(R.id.OptionsButton); + optionsButton.setOnClickListener((View v) -> { + PopupMenu popup = new PopupMenu(this,v); + popup.setOnMenuItemClickListener(this); + MenuInflater inflater = popup.getMenuInflater(); + inflater.inflate(R.menu.context_menu, popup.getMenu()); + popup.show(); + }); + } + + @Override + public boolean onMenuItemClick(MenuItem item) { + // Respond to picking one of the popup menu items. + final int id = item.getItemId(); + if (id == R.id.add_app_shortcut) { + FragmentManager fm = getSupportFragmentManager(); + PinnedAppPickerDialog pickerDialogFragment = + PinnedAppPickerDialog.newInstance(mAppListAdapter, this); + pickerDialogFragment.show(fm, "fragment_app_picker"); + return true; + } else if (id == R.id.set_wallpaper) { + Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER); + startActivity(Intent.createChooser(intent, getString(R.string.set_wallpaper))); + return true; + } + + return true; + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + showAppDrawer(false); + } + + public void onBackPressed() { + // If the app drawer was shown - hide it. Otherwise, not doing anything since we don't want + // to close the launcher. + showAppDrawer(false); + } + + public void onNewIntent(Intent intent) { + super.onNewIntent(intent); + // A new intent will bring the launcher to top. Hide the app drawer to reset the state. + showAppDrawer(false); + } + + void launch(Intent launchIntent) { + launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + startActivity(launchIntent); + } catch (Exception e) { + final AlertDialog.Builder builder = + new AlertDialog.Builder(this, android.R.style.Theme_Material_Dialog_Alert); + builder.setTitle(R.string.couldnt_launch) + .setMessage(e.getLocalizedMessage()) + .setIcon(android.R.drawable.ic_dialog_alert) + .show(); + } + } + + /** + * Store the picked app to persistent pinned list and update the loader. + */ + @Override + public void onAppPicked(AppEntry appEntry) { + final SharedPreferences sp = getSharedPreferences(PINNED_APPS_KEY, 0); + Set<String> pinnedApps = sp.getStringSet(PINNED_APPS_KEY, null); + if (pinnedApps == null) { + pinnedApps = new HashSet<String>(); + } else { + // Always need to create a new object to make sure that the changes are persisted. + pinnedApps = new HashSet<String>(pinnedApps); + } + pinnedApps.add(appEntry.getComponentName().flattenToString()); + + final SharedPreferences.Editor editor = sp.edit(); + editor.putStringSet(PINNED_APPS_KEY, pinnedApps); + editor.apply(); + } + + /** + * Show/hide app drawer card with animation. + */ + private void showAppDrawer(boolean show) { + if (show == mAppDrawerShown) { + return; + } + + final Animator animator = revealAnimator(mAppDrawerView, show); + if (show) { + mAppDrawerShown = true; + mAppDrawerView.setVisibility(View.VISIBLE); + mFab.setVisibility(View.INVISIBLE); + } else { + mAppDrawerShown = false; + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + mAppDrawerView.setVisibility(View.INVISIBLE); + mFab.setVisibility(View.VISIBLE); + } + }); + } + animator.start(); + } + + /** + * Create reveal/hide animator for app list card. + */ + private Animator revealAnimator(View view, boolean open) { + final int radius = (int) Math.hypot((double) view.getWidth(), (double) view.getHeight()); + return ViewAnimationUtils.createCircularReveal(view, view.getRight(), view.getBottom(), + open ? 0 : radius, open ? radius : 0); + } +} |