Merge branch 'ub-launcher3-dorval-polish2'
Bug: 64937210 Test: flashed img Change-Id: Id6a05e7be885bf5c53a41eab2573c6ae3a44dc55
This commit is contained in:
@@ -82,6 +82,7 @@
|
||||
<category android:name="android.intent.category.HOME" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.MONKEY"/>
|
||||
<category android:name="android.intent.category.LAUNCHER_APP" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/* Copyright 2017, The Android Open 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>
|
||||
<bool name="notification_badging_enabled">false</bool>
|
||||
</resources>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open 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>
|
||||
<!-- String representing the intent to delete a package. -->
|
||||
<string name="delete_package_intent" translatable="false">#Intent;action=android.intent.action.DELETE;launchFlags=0x10800000;B.android.intent.extra.RETURN_RESULT=true;end</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
** Copyright 2017, The Android Open 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.
|
||||
*/
|
||||
-->
|
||||
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="2166"
|
||||
android:repeatCount="5">
|
||||
<propertyValuesHolder
|
||||
android:propertyName="progress"
|
||||
android:valueType="floatType">
|
||||
<keyframe
|
||||
android:fraction="0"
|
||||
android:value="1f" />
|
||||
<keyframe
|
||||
android:fraction="0.346"
|
||||
android:value="1f" />
|
||||
<keyframe
|
||||
android:fraction=".423"
|
||||
android:interpolator="@interpolator/disco_bounce"
|
||||
android:value="0.9438f" />
|
||||
<keyframe
|
||||
android:fraction="0.654"
|
||||
android:interpolator="@interpolator/disco_bounce"
|
||||
android:value="1f" />
|
||||
<keyframe
|
||||
android:fraction="1"
|
||||
android:value="1f" />
|
||||
</propertyValuesHolder>
|
||||
</objectAnimator>
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
** Copyright 2016, The Android Open Source Project
|
||||
** Copyright 2017, The Android Open 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,30 +16,20 @@
|
||||
** limitations under the License.
|
||||
*/
|
||||
-->
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:ordering="sequentially"
|
||||
android:startOffset="200">
|
||||
|
||||
android:ordering="sequentially">
|
||||
<objectAnimator
|
||||
android:duration="166"
|
||||
android:interpolator="@interpolator/disco_bounce"
|
||||
android:propertyName="progress"
|
||||
android:duration="250"
|
||||
android:interpolator="@interpolator/disco_bounce_section1"
|
||||
android:startOffset="750"
|
||||
android:valueFrom="1f"
|
||||
android:valueTo=".94f"
|
||||
android:valueType="floatType"/>
|
||||
android:valueTo="0.9438f"
|
||||
android:valueType="floatType" />
|
||||
<objectAnimator
|
||||
android:duration="500"
|
||||
android:interpolator="@interpolator/disco_bounce"
|
||||
android:propertyName="progress"
|
||||
android:duration="216"
|
||||
android:interpolator="@interpolator/disco_bounce_section2"
|
||||
android:valueFrom=".94f"
|
||||
android:valueTo="1.012f"
|
||||
android:valueType="floatType"/>
|
||||
<objectAnimator
|
||||
android:propertyName="progress"
|
||||
android:duration="234"
|
||||
android:interpolator="@interpolator/disco_bounce_section3"
|
||||
android:valueFrom="1.012f"
|
||||
android:valueTo="1f"
|
||||
android:valueType="floatType"/>
|
||||
android:valueType="floatType" />
|
||||
</set>
|
||||
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 The Android Open 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:width="@dimen/profile_badge_size"
|
||||
android:height="@dimen/profile_badge_size"
|
||||
android:viewportWidth="18"
|
||||
android:viewportHeight="18">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:fillAlpha="0.87"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M 6 10.4123279 L 8.63934949 10.4123279 L 8.63934949 15.6 L 12.5577168 7.84517705 L 9.94547194 7.84517705 L 9.94547194 2 Z" />
|
||||
</vector>
|
||||
@@ -16,7 +16,7 @@
|
||||
-->
|
||||
|
||||
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:controlX1="0.9"
|
||||
android:controlY1="0"
|
||||
android:controlX2="0.5"
|
||||
android:controlY2="1"/>
|
||||
android:controlX1="0.35"
|
||||
android:controlY1="0"
|
||||
android:controlX2="0.5"
|
||||
android:controlY2="1" />
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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
|
||||
-->
|
||||
|
||||
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:controlX1="0.9"
|
||||
android:controlY1="0"
|
||||
android:controlX2="0.6"
|
||||
android:controlY2="1"/>
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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
|
||||
-->
|
||||
|
||||
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:controlX1="0.1"
|
||||
android:controlY1="0"
|
||||
android:controlX2="0.7"
|
||||
android:controlY2="1"/>
|
||||
@@ -68,11 +68,10 @@
|
||||
|
||||
</com.android.launcher3.allapps.AllAppsRecyclerViewContainerView>
|
||||
<View
|
||||
style="@style/AllAppsNavBarProtection"
|
||||
android:id="@+id/nav_bar_bg"
|
||||
android:background="?attr/allAppsNavBarScrimColor"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:focusable="false"
|
||||
android:visibility="invisible" />
|
||||
android:focusable="false" />
|
||||
</com.android.launcher3.allapps.AllAppsContainerView>
|
||||
@@ -49,7 +49,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
android:gravity="bottom"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/notifications_header"
|
||||
android:textSize="@dimen/notification_header_text_size"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
@@ -58,7 +58,7 @@
|
||||
android:layout_width="@dimen/notification_icon_size"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
android:gravity="bottom|center_horizontal"
|
||||
android:gravity="center"
|
||||
android:textSize="@dimen/notification_header_count_text_size"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
android:background="?attr/popupColorPrimary"
|
||||
android:paddingStart="@dimen/notification_padding_start"
|
||||
android:paddingEnd="@dimen/notification_main_text_padding_end"
|
||||
android:paddingBottom="16dp">
|
||||
android:paddingBottom="14dp">
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
@@ -59,7 +59,7 @@
|
||||
android:layout_width="@dimen/notification_icon_size"
|
||||
android:layout_height="@dimen/notification_icon_size"
|
||||
android:layout_marginEnd="@dimen/notification_padding_end"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginBottom="7dp"
|
||||
android:layout_gravity="center_vertical|end" />
|
||||
|
||||
</com.android.launcher3.notification.NotificationMainView>
|
||||
|
||||
@@ -26,10 +26,4 @@
|
||||
<item name="android:colorPrimaryDark">#616161</item> <!-- Gray 700 -->
|
||||
</style>
|
||||
|
||||
<!-- From O and above, we show a dark nav bar in all-apps -->
|
||||
<style name="AllAppsNavBarProtection">
|
||||
<item name="android:alpha">0.6</item>
|
||||
<item name="android:background">?android:attr/colorPrimary</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
<!-- Attributes used for launcher theme -->
|
||||
<attr name="allAppsScrimColor" format="color" />
|
||||
<attr name="allAppsNavBarScrimColor" format="color" />
|
||||
<attr name="popupColorPrimary" format="color" />
|
||||
<attr name="popupColorSecondary" format="color" />
|
||||
<attr name="popupColorTertiary" format="color" />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<resources>
|
||||
<!-- Dynamic Grid -->
|
||||
<!-- Out of 100, the percent of space the overview bar should try and take vertically. -->
|
||||
<integer name="config_dynamic_grid_overview_icon_zone_percentage">20</integer>
|
||||
<integer name="config_dynamic_grid_overview_icon_zone_percentage">22</integer>
|
||||
|
||||
<!-- Miscellaneous -->
|
||||
<bool name="config_largeHeap">false</bool>
|
||||
@@ -9,7 +9,7 @@
|
||||
<bool name="is_large_tablet">false</bool>
|
||||
<bool name="allow_rotation">false</bool>
|
||||
|
||||
<integer name="extracted_color_gradient_alpha">191</integer>
|
||||
<integer name="extracted_color_gradient_alpha">153</integer>
|
||||
|
||||
<!-- A string pointer to the original app name string. This allows derived projects to
|
||||
easily override the app name without providing all translations -->
|
||||
@@ -19,6 +19,9 @@
|
||||
q=<query> to the data to the intent -->
|
||||
<string name="market_search_intent" translatable="false">market://search?c=apps</string>
|
||||
|
||||
<!-- String representing the intent to delete a package.-->
|
||||
<string name="delete_package_intent" translatable="false">#Intent;action=android.intent.action.DELETE;launchFlags=0x10800000;end</string>
|
||||
|
||||
<!-- Values for icon shape overrides. These should correspond to entries defined
|
||||
in icon_shape_override_paths_names -->
|
||||
<string-array translatable="false" name="icon_shape_override_paths_values">
|
||||
@@ -113,6 +116,9 @@
|
||||
<!-- Name of a color extraction implementation class. -->
|
||||
<string name="color_extraction_impl_class" translatable="false"></string>
|
||||
|
||||
<!-- Name of a subclass of com.android.launcher3.util.InstantAppResolver. Can be empty. -->
|
||||
<string name="instant_app_resolver_class" translatable="false"></string>
|
||||
|
||||
<!-- Package name of the default wallpaper picker. -->
|
||||
<string name="wallpaper_picker_package" translatable="false"></string>
|
||||
|
||||
|
||||
@@ -214,9 +214,11 @@
|
||||
<dimen name="notification_padding_end">12dp</dimen>
|
||||
<!-- notification_padding_end + (icon_size - footer_icon_size) / 2 -->
|
||||
<dimen name="notification_footer_icon_row_padding">15dp</dimen>
|
||||
<dimen name="notification_header_height">32dp</dimen>
|
||||
<dimen name="notification_main_height">96dp</dimen>
|
||||
<dimen name="notification_header_height">36dp</dimen>
|
||||
<dimen name="notification_main_height">84dp</dimen>
|
||||
<dimen name="notification_footer_height">32dp</dimen>
|
||||
<!-- How much space to keep as padding for the last notification when the footer collapses -->
|
||||
<dimen name="notification_empty_footer_height">6dp</dimen>
|
||||
<dimen name="notification_header_text_size">13sp</dimen>
|
||||
<dimen name="notification_header_count_text_size">12sp</dimen>
|
||||
<dimen name="notification_main_title_size">16sp</dimen>
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
</style>
|
||||
|
||||
<style name="BaseLauncherThemeWithCustomAttrs" parent="@style/BaseLauncherTheme">
|
||||
<item name="allAppsScrimColor">#DDFFFFFF</item>
|
||||
<item name="allAppsScrimColor">#CCFFFFFF</item>
|
||||
<item name="allAppsNavBarScrimColor">#66FFFFFF</item>
|
||||
<item name="popupColorPrimary">#FFF</item>
|
||||
<item name="popupColorSecondary">#F5F5F5</item> <!-- Gray 100 -->
|
||||
<item name="popupColorTertiary">#E0E0E0</item> <!-- Gray 300 -->
|
||||
@@ -61,7 +62,8 @@
|
||||
<item name="android:textColorHint">#A0FFFFFF</item>
|
||||
<item name="android:colorControlHighlight">#A0FFFFFF</item>
|
||||
<item name="android:colorPrimary">#FF333333</item>
|
||||
<item name="allAppsScrimColor">#33000000</item>
|
||||
<item name="allAppsScrimColor">#7A212121</item>
|
||||
<item name="allAppsNavBarScrimColor">#80000000</item>
|
||||
<item name="popupColorPrimary">?android:attr/colorPrimary</item>
|
||||
<item name="popupColorSecondary">#424242</item> <!-- Gray 800 -->
|
||||
<item name="popupColorTertiary">#757575</item> <!-- Gray 600 -->
|
||||
@@ -103,12 +105,6 @@
|
||||
<item name="android:includeFontPadding">false</item>
|
||||
</style>
|
||||
|
||||
<!-- Style for nav bar background in all-apps screen -->
|
||||
<style name="AllAppsNavBarProtection">
|
||||
<item name="android:alpha">?android:attr/spotShadowAlpha</item>
|
||||
<item name="android:background">@color/default_shadow_color_no_alpha</item>
|
||||
</style>
|
||||
|
||||
<!-- Base theme for BubbleTextView and sub classes -->
|
||||
<style name="BaseIcon">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
|
||||
@@ -95,10 +95,14 @@ public class AppInfo extends ItemInfoWithIcon {
|
||||
}
|
||||
|
||||
public static Intent makeLaunchIntent(LauncherActivityInfo info) {
|
||||
return makeLaunchIntent(info.getComponentName());
|
||||
}
|
||||
|
||||
public static Intent makeLaunchIntent(ComponentName cn) {
|
||||
return new Intent(Intent.ACTION_MAIN)
|
||||
.addCategory(Intent.CATEGORY_LAUNCHER)
|
||||
.setComponent(info.getComponentName())
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
|
||||
.addCategory(Intent.CATEGORY_LAUNCHER)
|
||||
.setComponent(cn)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -55,7 +55,6 @@ public class AppWidgetResizeFrame extends FrameLayout
|
||||
|
||||
private final int[] mDirectionVector = new int[2];
|
||||
private final int[] mLastDirectionVector = new int[2];
|
||||
private final int[] mTmpPt = new int[2];
|
||||
|
||||
private final IntRange mTempRange1 = new IntRange();
|
||||
private final IntRange mTempRange2 = new IntRange();
|
||||
@@ -344,13 +343,12 @@ public class AppWidgetResizeFrame extends FrameLayout
|
||||
return rect;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the final step of the resize. Here we save the new widget size and position
|
||||
* to LauncherModel and animate the resize frame.
|
||||
*/
|
||||
public void commitResize() {
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
|
||||
// We are done with resizing the widget. Save the widget size & position to LauncherModel
|
||||
resizeWidgetIfNeeded(true);
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
private void onTouchUp() {
|
||||
|
||||
@@ -184,7 +184,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
|
||||
public void applyFromShortcutInfo(ShortcutInfo info, boolean promiseStateChanged) {
|
||||
applyIconAndLabel(info.iconBitmap, info);
|
||||
setTag(info);
|
||||
if (promiseStateChanged || info.isPromise()) {
|
||||
if (promiseStateChanged || (info.hasPromiseIconUi())) {
|
||||
applyPromiseState(promiseStateChanged);
|
||||
}
|
||||
|
||||
@@ -481,7 +481,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
|
||||
public void applyPromiseState(boolean promiseStateChanged) {
|
||||
if (getTag() instanceof ShortcutInfo) {
|
||||
ShortcutInfo info = (ShortcutInfo) getTag();
|
||||
final boolean isPromise = info.isPromise();
|
||||
final boolean isPromise = info.hasPromiseIconUi();
|
||||
final int progressLevel = isPromise ?
|
||||
((info.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE) ?
|
||||
info.getInstallProgress() : 0)) : 100;
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.graphics.ColorMatrix;
|
||||
import android.graphics.ColorMatrixColorFilter;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
@@ -69,6 +70,7 @@ public abstract class ButtonDropTarget extends TextView
|
||||
/** The paint applied to the drag view on hover */
|
||||
protected int mHoverColor = 0;
|
||||
|
||||
protected CharSequence mText;
|
||||
protected ColorStateList mOriginalTextColor;
|
||||
protected Drawable mDrawable;
|
||||
|
||||
@@ -96,6 +98,7 @@ public abstract class ButtonDropTarget extends TextView
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mText = getText();
|
||||
mOriginalTextColor = getTextColors();
|
||||
}
|
||||
|
||||
@@ -297,4 +300,30 @@ public abstract class ButtonDropTarget extends TextView
|
||||
public int getTextColor() {
|
||||
return getTextColors().getDefaultColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns True if any update was made.
|
||||
*/
|
||||
public boolean updateText(boolean hide) {
|
||||
if ((hide && getText().toString().isEmpty()) || (!hide && mText.equals(getText()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
setText(hide ? "" : mText);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isTextTruncated() {
|
||||
int availableWidth = getMeasuredWidth();
|
||||
if (mHideParentOnDisable) {
|
||||
ViewGroup parent = (ViewGroup) getParent();
|
||||
availableWidth = parent.getMeasuredWidth() - parent.getPaddingLeft()
|
||||
- parent.getPaddingRight();
|
||||
}
|
||||
availableWidth -= (getPaddingLeft() + getPaddingRight() + mDrawable.getIntrinsicWidth()
|
||||
+ getCompoundDrawablePadding());
|
||||
CharSequence displayedText = TextUtils.ellipsize(mText, getPaint(), availableWidth,
|
||||
TextUtils.TruncateAt.END);
|
||||
return !mText.equals(displayedText);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,9 +65,11 @@ public class DeleteDropTarget extends ButtonDropTarget {
|
||||
* Set the drop target's text to either "Remove" or "Cancel" depending on the drag source.
|
||||
*/
|
||||
public void setTextBasedOnDragSource(DragSource dragSource) {
|
||||
if (!TextUtils.isEmpty(getText())) {
|
||||
setText(dragSource.supportsDeleteDropTarget() ? R.string.remove_drop_target_label
|
||||
if (!TextUtils.isEmpty(mText)) {
|
||||
mText = getResources().getString(dragSource.supportsDeleteDropTarget()
|
||||
? R.string.remove_drop_target_label
|
||||
: android.R.string.cancel);
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -267,6 +267,10 @@ public class DeviceProfile {
|
||||
}
|
||||
|
||||
DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
|
||||
// We take the minimum sizes of this profile and it's multi-window variant to ensure that
|
||||
// the system decor is always excluded.
|
||||
mwSize.set(Math.min(availableWidthPx, mwSize.x), Math.min(availableHeightPx, mwSize.y));
|
||||
|
||||
// In multi-window mode, we can have widthPx = availableWidthPx
|
||||
// and heightPx = availableHeightPx because Launcher uses the InvariantDeviceProfiles'
|
||||
// widthPx and heightPx values where it's needed.
|
||||
@@ -555,9 +559,9 @@ public class DeviceProfile {
|
||||
|
||||
int getOverviewModeButtonBarHeight() {
|
||||
int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx);
|
||||
zoneHeight = Math.min(overviewModeMaxIconZoneHeightPx,
|
||||
Math.max(overviewModeMinIconZoneHeightPx, zoneHeight));
|
||||
return zoneHeight;
|
||||
return Utilities.boundToRange(zoneHeight,
|
||||
overviewModeMinIconZoneHeightPx,
|
||||
overviewModeMaxIconZoneHeightPx);
|
||||
}
|
||||
|
||||
public static int calculateCellWidth(int width, int countX) {
|
||||
@@ -693,7 +697,8 @@ public class DeviceProfile {
|
||||
|
||||
lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams();
|
||||
lp.width = Math.min(availableWidthPx, maxWidth);
|
||||
lp.height = getOverviewModeButtonBarHeight() + mInsets.bottom;
|
||||
lp.height = getOverviewModeButtonBarHeight();
|
||||
lp.bottomMargin = mInsets.bottom;
|
||||
overviewMode.setLayoutParams(lp);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,6 +78,58 @@ public class DropTargetBar extends LinearLayout implements DragController.DragLi
|
||||
setupButtonDropTarget(this, dragController);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
boolean hideText = hideTextHelper(false /* shouldUpdateText */, false /* no-op */);
|
||||
if (hideTextHelper(true /* shouldUpdateText */, hideText)) {
|
||||
// Text has changed, so we need to re-measure.
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that iterates through the children and returns whether any of the visible
|
||||
* {@link ButtonDropTarget} has truncated text.
|
||||
*
|
||||
* @param shouldUpdateText If True, updates the text of all children.
|
||||
* @param hideText If True and {@param shouldUpdateText} is True, clears the text of all
|
||||
* children; otherwise it sets the original text value.
|
||||
*
|
||||
*
|
||||
* @return If shouldUpdateText is True, returns whether any of the children updated their text.
|
||||
* Else, returns whether any of the children have truncated their text.
|
||||
*/
|
||||
private boolean hideTextHelper(boolean shouldUpdateText, boolean hideText) {
|
||||
boolean result = false;
|
||||
View visibleView;
|
||||
ButtonDropTarget dropTarget;
|
||||
for (int i = getChildCount() - 1; i >= 0; --i) {
|
||||
if (getChildAt(i) instanceof ButtonDropTarget) {
|
||||
visibleView = dropTarget = (ButtonDropTarget) getChildAt(i);
|
||||
} else if (getChildAt(i) instanceof ViewGroup) {
|
||||
// The Drop Target is wrapped in a FrameLayout.
|
||||
visibleView = getChildAt(i);
|
||||
dropTarget = (ButtonDropTarget) ((ViewGroup) visibleView).getChildAt(0);
|
||||
} else {
|
||||
// Ignore other views.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (visibleView.getVisibility() == View.VISIBLE) {
|
||||
if (shouldUpdateText) {
|
||||
result |= dropTarget.updateText(hideText);
|
||||
} else if (dropTarget.isTextTruncated()) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void setupButtonDropTarget(View view, DragController dragController) {
|
||||
if (view instanceof ButtonDropTarget) {
|
||||
ButtonDropTarget bdt = (ButtonDropTarget) view;
|
||||
|
||||
@@ -149,7 +149,6 @@ public class Hotseat extends FrameLayout
|
||||
allAppsButton.setOnKeyListener(new HotseatIconKeyEventListener());
|
||||
if (mLauncher != null) {
|
||||
mLauncher.setAllAppsButton(allAppsButton);
|
||||
allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
|
||||
allAppsButton.setOnClickListener(mLauncher);
|
||||
allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.graphics.LauncherIcons;
|
||||
import com.android.launcher3.model.PackageItemInfo;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.InstantAppResolver;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
import com.android.launcher3.util.Provider;
|
||||
import com.android.launcher3.util.SQLiteCacheHelper;
|
||||
@@ -94,6 +95,7 @@ public class IconCache {
|
||||
private final LauncherAppsCompat mLauncherApps;
|
||||
private final HashMap<ComponentKey, CacheEntry> mCache =
|
||||
new HashMap<>(INITIAL_ICON_CACHE_CAPACITY);
|
||||
private final InstantAppResolver mInstantAppResolver;
|
||||
private final int mIconDpi;
|
||||
@Thunk final IconDB mIconDb;
|
||||
|
||||
@@ -106,6 +108,7 @@ public class IconCache {
|
||||
mPackageManager = context.getPackageManager();
|
||||
mUserManager = UserManagerCompat.getInstance(mContext);
|
||||
mLauncherApps = LauncherAppsCompat.getInstance(mContext);
|
||||
mInstantAppResolver = InstantAppResolver.newInstance(mContext);
|
||||
mIconDpi = inv.fillResIconDpi;
|
||||
mIconDb = new IconDB(context, inv.iconBitmapSize);
|
||||
|
||||
@@ -120,7 +123,7 @@ public class IconCache {
|
||||
}
|
||||
|
||||
private Drawable getFullResDefaultActivityIcon() {
|
||||
return getFullResIcon(Resources.getSystem(), Utilities.isAtLeastO() ?
|
||||
return getFullResIcon(Resources.getSystem(), Utilities.ATLEAST_OREO ?
|
||||
android.R.drawable.sym_def_app_icon : android.R.mipmap.sym_def_app_icon);
|
||||
}
|
||||
|
||||
@@ -575,7 +578,6 @@ public class IconCache {
|
||||
// For icon caching, do not go through DB. Just update the in-memory entry.
|
||||
if (entry == null) {
|
||||
entry = new CacheEntry();
|
||||
mCache.put(cacheKey, entry);
|
||||
}
|
||||
if (!TextUtils.isEmpty(title)) {
|
||||
entry.title = title;
|
||||
@@ -583,6 +585,9 @@ public class IconCache {
|
||||
if (icon != null) {
|
||||
entry.icon = LauncherIcons.createIconBitmap(icon, mContext);
|
||||
}
|
||||
if (!TextUtils.isEmpty(title) && entry.icon != null) {
|
||||
mCache.put(cacheKey, entry);
|
||||
}
|
||||
}
|
||||
|
||||
private static ComponentKey getPackageKey(String packageName, UserHandle user) {
|
||||
@@ -619,6 +624,10 @@ public class IconCache {
|
||||
// only keep the low resolution icon instead of the larger full-sized icon
|
||||
Bitmap icon = LauncherIcons.createBadgedIconBitmap(
|
||||
appInfo.loadIcon(mPackageManager), user, mContext, appInfo.targetSdkVersion);
|
||||
if (mInstantAppResolver.isInstantApp(appInfo)) {
|
||||
icon = LauncherIcons.badgeWithDrawable(icon,
|
||||
mContext.getDrawable(R.drawable.ic_instant_app_badge), mContext);
|
||||
}
|
||||
Bitmap lowResIcon = generateLowResIcon(icon);
|
||||
entry.title = appInfo.loadLabel(mPackageManager);
|
||||
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.android.launcher3;
|
||||
|
||||
import android.content.pm.LauncherActivityInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -17,7 +18,7 @@ public class IconProvider {
|
||||
}
|
||||
|
||||
public void updateSystemStateString() {
|
||||
mSystemState = Locale.getDefault().toString();
|
||||
mSystemState = Locale.getDefault().toString() + "," + Build.VERSION.SDK_INT;
|
||||
}
|
||||
|
||||
public String getIconSystemState(String packageName) {
|
||||
|
||||
@@ -75,7 +75,7 @@ public class InfoDropTarget extends UninstallDropTarget {
|
||||
if (info instanceof AppInfo) {
|
||||
componentName = ((AppInfo) info).componentName;
|
||||
} else if (info instanceof ShortcutInfo) {
|
||||
componentName = ((ShortcutInfo) info).intent.getComponent();
|
||||
componentName = info.getTargetComponent();
|
||||
} else if (info instanceof PendingAddItemInfo) {
|
||||
componentName = ((PendingAddItemInfo) info).componentName;
|
||||
} else if (info instanceof LauncherAppWidgetInfo) {
|
||||
|
||||
@@ -138,17 +138,11 @@ public class ItemInfo {
|
||||
|
||||
public ComponentName getTargetComponent() {
|
||||
Intent intent = getIntent();
|
||||
if (intent == null) {
|
||||
if (intent != null) {
|
||||
return intent.getComponent();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
ComponentName cn = intent.getComponent();
|
||||
if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT && cn == null) {
|
||||
// Legacy shortcuts may not have a componentName but just a packageName. In that case
|
||||
// create a dummy componentName instead of adding additional check everywhere.
|
||||
String pkg = intent.getPackage();
|
||||
return pkg == null ? null : new ComponentName(pkg, IconCache.EMPTY_CLASS_NAME);
|
||||
}
|
||||
return cn;
|
||||
}
|
||||
|
||||
public void writeToValues(ContentWriter writer) {
|
||||
|
||||
@@ -113,7 +113,6 @@ import com.android.launcher3.pageindicators.PageIndicator;
|
||||
import com.android.launcher3.popup.PopupContainerWithArrow;
|
||||
import com.android.launcher3.popup.PopupDataProvider;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutManager;
|
||||
import com.android.launcher3.shortcuts.ShortcutKey;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
@@ -121,6 +120,7 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
|
||||
import com.android.launcher3.util.ActivityResultInfo;
|
||||
import com.android.launcher3.util.RunnableWithId;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.ComponentKeyMapper;
|
||||
import com.android.launcher3.util.ItemInfoMatcher;
|
||||
import com.android.launcher3.util.MultiHashMap;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
@@ -272,7 +272,6 @@ public class Launcher extends BaseActivity
|
||||
private IconCache mIconCache;
|
||||
private LauncherAccessibilityDelegate mAccessibilityDelegate;
|
||||
private final Handler mHandler = new Handler();
|
||||
private boolean mIsResumeFromActionScreenOff;
|
||||
private boolean mHasFocus = false;
|
||||
|
||||
private ObjectAnimator mScrimAnimator;
|
||||
@@ -280,8 +279,6 @@ public class Launcher extends BaseActivity
|
||||
|
||||
private PopupDataProvider mPopupDataProvider;
|
||||
|
||||
private View.OnTouchListener mHapticFeedbackTouchListener;
|
||||
|
||||
// Determines how long to wait after a rotation before restoring the screen orientation to
|
||||
// match the sensor state.
|
||||
private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
|
||||
@@ -1046,7 +1043,6 @@ public class Launcher extends BaseActivity
|
||||
if (shouldShowDiscoveryBounce()) {
|
||||
mAllAppsController.showDiscoveryBounce();
|
||||
}
|
||||
mIsResumeFromActionScreenOff = false;
|
||||
if (mLauncherCallbacks != null) {
|
||||
mLauncherCallbacks.onResume();
|
||||
}
|
||||
@@ -1134,7 +1130,7 @@ public class Launcher extends BaseActivity
|
||||
// On O and above we there is always some setting present settings (add icon to
|
||||
// home screen or icon badging). On earlier APIs we will have the allow rotation
|
||||
// setting, on devices with a locked orientation,
|
||||
return Utilities.isAtLeastO() || !getResources().getBoolean(R.bool.allow_rotation);
|
||||
return Utilities.ATLEAST_OREO || !getResources().getBoolean(R.bool.allow_rotation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1328,7 +1324,6 @@ public class Launcher extends BaseActivity
|
||||
onClickWallpaperPicker(view);
|
||||
}
|
||||
}.attachTo(wallpaperButton);
|
||||
wallpaperButton.setOnTouchListener(getHapticFeedbackTouchListener());
|
||||
|
||||
// Bind widget button actions
|
||||
mWidgetsButton = findViewById(R.id.widget_button);
|
||||
@@ -1338,7 +1333,6 @@ public class Launcher extends BaseActivity
|
||||
onClickAddWidgetButton(view);
|
||||
}
|
||||
}.attachTo(mWidgetsButton);
|
||||
mWidgetsButton.setOnTouchListener(getHapticFeedbackTouchListener());
|
||||
|
||||
// Bind settings actions
|
||||
View settingsButton = findViewById(R.id.settings_button);
|
||||
@@ -1350,7 +1344,6 @@ public class Launcher extends BaseActivity
|
||||
onClickSettingsButton(view);
|
||||
}
|
||||
}.attachTo(settingsButton);
|
||||
settingsButton.setOnTouchListener(getHapticFeedbackTouchListener());
|
||||
} else {
|
||||
settingsButton.setVisibility(View.GONE);
|
||||
}
|
||||
@@ -1416,7 +1409,7 @@ public class Launcher extends BaseActivity
|
||||
CellLayout layout = getCellLayout(container, screenId);
|
||||
|
||||
ShortcutInfo info = null;
|
||||
if (Utilities.isAtLeastO()) {
|
||||
if (Utilities.ATLEAST_OREO) {
|
||||
info = LauncherAppsCompatVO.createShortcutInfoFromPinItemRequest(
|
||||
this, LauncherAppsCompatVO.getPinItemRequest(data), 0);
|
||||
}
|
||||
@@ -1548,7 +1541,6 @@ public class Launcher extends BaseActivity
|
||||
mAppsView.reset();
|
||||
}
|
||||
}
|
||||
mIsResumeFromActionScreenOff = true;
|
||||
mShouldFadeInScrim = true;
|
||||
} else if (Intent.ACTION_USER_PRESENT.equals(action)) {
|
||||
// ACTION_USER_PRESENT is sent after onStart/onResume. This covers the case where
|
||||
@@ -2424,7 +2416,7 @@ public class Launcher extends BaseActivity
|
||||
}
|
||||
|
||||
// Check for abandoned promise
|
||||
if ((v instanceof BubbleTextView) && shortcut.isPromise()) {
|
||||
if ((v instanceof BubbleTextView) && shortcut.hasPromiseIconUi()) {
|
||||
String packageName = shortcut.intent.getComponent() != null ?
|
||||
shortcut.intent.getComponent().getPackageName() : shortcut.intent.getPackage();
|
||||
if (!TextUtils.isEmpty(packageName)) {
|
||||
@@ -2536,22 +2528,6 @@ public class Launcher extends BaseActivity
|
||||
startActivity(intent, getActivityLaunchOptions(v));
|
||||
}
|
||||
|
||||
public View.OnTouchListener getHapticFeedbackTouchListener() {
|
||||
if (mHapticFeedbackTouchListener == null) {
|
||||
mHapticFeedbackTouchListener = new View.OnTouchListener() {
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
|
||||
v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
return mHapticFeedbackTouchListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccessibilityStateChanged(boolean enabled) {
|
||||
mDragLayer.onAccessibilityStateChanged(enabled);
|
||||
@@ -3068,7 +3044,7 @@ public class Launcher extends BaseActivity
|
||||
*/
|
||||
public void tryAndUpdatePredictedApps() {
|
||||
if (mLauncherCallbacks != null) {
|
||||
List<ComponentKey> apps = mLauncherCallbacks.getPredictedApps();
|
||||
List<ComponentKeyMapper<AppInfo>> apps = mLauncherCallbacks.getPredictedApps();
|
||||
if (apps != null) {
|
||||
mAppsView.setPredictedApps(apps);
|
||||
}
|
||||
@@ -3910,16 +3886,7 @@ public class Launcher extends BaseActivity
|
||||
}
|
||||
|
||||
private boolean shouldShowDiscoveryBounce() {
|
||||
if (mState != State.WORKSPACE) {
|
||||
return false;
|
||||
}
|
||||
if (mLauncherCallbacks != null && mLauncherCallbacks.shouldShowDiscoveryBounce()) {
|
||||
return true;
|
||||
}
|
||||
if (!mIsResumeFromActionScreenOff) {
|
||||
return false;
|
||||
}
|
||||
return !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false);
|
||||
return mState == State.WORKSPACE && !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false);
|
||||
}
|
||||
|
||||
protected void moveWorkspaceToDefaultScreen() {
|
||||
|
||||
@@ -92,7 +92,7 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView
|
||||
setAccessibilityDelegate(Launcher.getLauncher(context).getAccessibilityDelegate());
|
||||
setBackgroundResource(R.drawable.widget_internal_focus_bg);
|
||||
|
||||
if (Utilities.isAtLeastO()) {
|
||||
if (Utilities.ATLEAST_OREO) {
|
||||
setExecutor(Utilities.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.ComponentKeyMapper;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
@@ -91,10 +91,8 @@ public interface LauncherCallbacks {
|
||||
*/
|
||||
boolean shouldMoveToDefaultScreenOnHomeIntent();
|
||||
boolean hasSettings();
|
||||
List<ComponentKey> getPredictedApps();
|
||||
List<ComponentKeyMapper<AppInfo>> getPredictedApps();
|
||||
int SEARCH_BAR_HEIGHT_NORMAL = 0, SEARCH_BAR_HEIGHT_TALL = 1;
|
||||
/** Must return one of {@link #SEARCH_BAR_HEIGHT_NORMAL} or {@link #SEARCH_BAR_HEIGHT_TALL} */
|
||||
int getSearchBarHeight();
|
||||
|
||||
boolean shouldShowDiscoveryBounce();
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public class SessionCommitReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (!isEnabled(context) || !Utilities.isAtLeastO()) {
|
||||
if (!isEnabled(context) || !Utilities.ATLEAST_OREO) {
|
||||
// User has decided to not add icons on homescreen.
|
||||
return;
|
||||
}
|
||||
@@ -92,7 +92,7 @@ public class SessionCommitReceiver extends BroadcastReceiver {
|
||||
}
|
||||
|
||||
public static void applyDefaultUserPrefs(final Context context) {
|
||||
if (!Utilities.isAtLeastO()) {
|
||||
if (!Utilities.ATLEAST_OREO) {
|
||||
return;
|
||||
}
|
||||
SharedPreferences prefs = Utilities.getPrefs(context);
|
||||
|
||||
@@ -94,10 +94,12 @@ public class SettingsActivity extends Activity {
|
||||
|
||||
ButtonPreference iconBadgingPref =
|
||||
(ButtonPreference) findPreference(ICON_BADGING_PREFERENCE_KEY);
|
||||
if (!Utilities.isAtLeastO()) {
|
||||
if (!Utilities.ATLEAST_OREO) {
|
||||
getPreferenceScreen().removePreference(
|
||||
findPreference(SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY));
|
||||
getPreferenceScreen().removePreference(iconBadgingPref);
|
||||
} else if (!getResources().getBoolean(R.bool.notification_badging_enabled)) {
|
||||
getPreferenceScreen().removePreference(iconBadgingPref);
|
||||
} else {
|
||||
// Listen to system notification badge settings while this UI is active.
|
||||
mIconBadgingObserver = new IconBadgingObserver(
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.launcher3;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
@@ -58,6 +59,11 @@ public class ShortcutInfo extends ItemInfoWithIcon {
|
||||
*/
|
||||
public static final int FLAG_RESTORE_STARTED = 8; //0B1000;
|
||||
|
||||
/**
|
||||
* Web UI supported.
|
||||
*/
|
||||
public static final int FLAG_SUPPORTS_WEB_UI = 16; //0B10000;
|
||||
|
||||
/**
|
||||
* Indicates if it represents a common type mentioned in {@link CommonAppTypeParser}.
|
||||
* Upto 15 different types supported.
|
||||
@@ -188,6 +194,10 @@ public class ShortcutInfo extends ItemInfoWithIcon {
|
||||
return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON);
|
||||
}
|
||||
|
||||
public boolean hasPromiseIconUi() {
|
||||
return isPromise() && !hasStatusFlag(FLAG_SUPPORTS_WEB_UI);
|
||||
}
|
||||
|
||||
public int getInstallProgress() {
|
||||
return mInstallProgress;
|
||||
}
|
||||
@@ -226,4 +236,18 @@ public class ShortcutInfo extends ItemInfoWithIcon {
|
||||
public boolean isDisabled() {
|
||||
return isDisabled != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentName getTargetComponent() {
|
||||
ComponentName cn = super.getTargetComponent();
|
||||
if (cn == null && (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
|
||||
|| hasStatusFlag(FLAG_SUPPORTS_WEB_UI))) {
|
||||
// Legacy shortcuts and promise icons with web UI may not have a componentName but just
|
||||
// a packageName. In that case create a dummy componentName instead of adding additional
|
||||
// check everywhere.
|
||||
String pkg = intent.getPackage();
|
||||
return pkg == null ? null : new ComponentName(pkg, IconCache.EMPTY_CLASS_NAME);
|
||||
}
|
||||
return cn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,17 @@ import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.launcher3.compat.LauncherAppsCompat;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
public class UninstallDropTarget extends ButtonDropTarget {
|
||||
|
||||
private static final String TAG = "UninstallDropTarget";
|
||||
|
||||
public UninstallDropTarget(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
@@ -99,25 +104,28 @@ public class UninstallDropTarget extends ButtonDropTarget {
|
||||
final Launcher launcher, ItemInfo info, DropTargetResultCallback callback) {
|
||||
final ComponentName cn = getUninstallTarget(launcher, info);
|
||||
|
||||
final boolean isUninstallable;
|
||||
boolean canUninstall;
|
||||
if (cn == null) {
|
||||
// System applications cannot be installed. For now, show a toast explaining that.
|
||||
// We may give them the option of disabling apps this way.
|
||||
Toast.makeText(launcher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
|
||||
isUninstallable = false;
|
||||
canUninstall = false;
|
||||
} else {
|
||||
Intent intent = new Intent(Intent.ACTION_DELETE,
|
||||
Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
|
||||
intent.putExtra(Intent.EXTRA_USER, info.user);
|
||||
launcher.startActivity(intent);
|
||||
isUninstallable = true;
|
||||
try {
|
||||
Intent i = Intent.parseUri(launcher.getString(R.string.delete_package_intent), 0)
|
||||
.setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
|
||||
.putExtra(Intent.EXTRA_USER, info.user);
|
||||
launcher.startActivity(i);
|
||||
canUninstall = true;
|
||||
} catch (URISyntaxException e) {
|
||||
Log.e(TAG, "Failed to parse intent to start uninstall activity for item=" + info);
|
||||
canUninstall = false;
|
||||
}
|
||||
}
|
||||
if (callback != null) {
|
||||
sendUninstallResult(launcher, isUninstallable, cn, info.user, callback);
|
||||
sendUninstallResult(launcher, canUninstall, cn, info.user, callback);
|
||||
}
|
||||
return isUninstallable;
|
||||
return canUninstall;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -83,15 +83,17 @@ public final class Utilities {
|
||||
private static final Matrix sMatrix = new Matrix();
|
||||
private static final Matrix sInverseMatrix = new Matrix();
|
||||
|
||||
public static boolean isAtLeastO() {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
|
||||
}
|
||||
public static final boolean ATLEAST_OREO_MR1 =
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1;
|
||||
|
||||
public static final boolean ATLEAST_OREO =
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
|
||||
|
||||
public static final boolean ATLEAST_NOUGAT_MR1 =
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1;
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1;
|
||||
|
||||
public static final boolean ATLEAST_NOUGAT =
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
|
||||
|
||||
public static final boolean ATLEAST_MARSHMALLOW =
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
|
||||
|
||||
@@ -50,6 +50,7 @@ import com.android.launcher3.folder.Folder;
|
||||
import com.android.launcher3.keyboard.FocusedItemDecorator;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.ComponentKeyMapper;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
|
||||
import java.util.List;
|
||||
@@ -116,7 +117,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
||||
/**
|
||||
* Sets the current set of predicted apps.
|
||||
*/
|
||||
public void setPredictedApps(List<ComponentKey> apps) {
|
||||
public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
|
||||
mApps.setPredictedApps(apps);
|
||||
}
|
||||
|
||||
@@ -349,8 +350,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
||||
ViewGroup.LayoutParams navBarBgLp = navBarBg.getLayoutParams();
|
||||
navBarBgLp.height = insets.bottom;
|
||||
navBarBg.setLayoutParams(navBarBgLp);
|
||||
navBarBg.setVisibility(FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS
|
||||
? View.INVISIBLE : View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -448,9 +448,20 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
|
||||
row = Math.abs(numTotalRows - row);
|
||||
}
|
||||
|
||||
// We manipulate the stiffness, min, and max values based on the items distance to the
|
||||
// first row and the items distance to the center column to create the ^-shaped motion
|
||||
// effect.
|
||||
calculateSpringValues(spring, row, col);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultValues(SpringAnimation spring) {
|
||||
calculateSpringValues(spring, 0, mAppsPerRow / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* We manipulate the stiffness, min, and max values based on the items distance to the
|
||||
* first row and the items distance to the center column to create the ^-shaped motion
|
||||
* effect.
|
||||
*/
|
||||
private void calculateSpringValues(SpringAnimation spring, int row, int col) {
|
||||
float rowFactor = (1 + row) * 0.5f;
|
||||
float colFactor = getColumnFactor(col, mAppsPerRow);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.animation.AnimatorSet;
|
||||
import android.animation.ArgbEvaluator;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.graphics.Color;
|
||||
import android.support.animation.SpringAnimation;
|
||||
import android.support.v4.graphics.ColorUtils;
|
||||
import android.support.v4.view.animation.FastOutSlowInInterpolator;
|
||||
import android.view.MotionEvent;
|
||||
@@ -98,9 +99,10 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect
|
||||
|
||||
// Used in discovery bounce animation to provide the transition without workspace changing.
|
||||
private boolean mIsTranslateWithoutWorkspace = false;
|
||||
private AnimatorSet mDiscoBounceAnimation;
|
||||
private Animator mDiscoBounceAnimation;
|
||||
private GradientView mGradientView;
|
||||
|
||||
private SpringAnimation mSearchSpring;
|
||||
private SpringAnimationHandler mSpringAnimationHandler;
|
||||
|
||||
public AllAppsTransitionController(Launcher l) {
|
||||
@@ -226,6 +228,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect
|
||||
}
|
||||
mLauncher.showAppsView(true /* animated */, false /* updatePredictedApps */);
|
||||
if (hasSpringAnimationHandler()) {
|
||||
mSpringAnimationHandler.add(mSearchSpring, true /* setDefaultValues */);
|
||||
// The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.)
|
||||
mSpringAnimationHandler.animateToFinalPosition(0 /* pos */, 1 /* startValue */);
|
||||
}
|
||||
@@ -418,8 +421,8 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect
|
||||
cancelDiscoveryAnimation();
|
||||
|
||||
// assumption is that this variable is always null
|
||||
mDiscoBounceAnimation = (AnimatorSet) AnimatorInflater.loadAnimator(mLauncher,
|
||||
R.anim.discovery_bounce);
|
||||
mDiscoBounceAnimation = AnimatorInflater.loadAnimator(mLauncher,
|
||||
R.animator.discovery_bounce);
|
||||
mDiscoBounceAnimation.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animator) {
|
||||
@@ -499,6 +502,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect
|
||||
public void finishPullUp() {
|
||||
mHotseat.setVisibility(View.INVISIBLE);
|
||||
if (hasSpringAnimationHandler()) {
|
||||
mSpringAnimationHandler.remove(mSearchSpring);
|
||||
mSpringAnimationHandler.reset();
|
||||
}
|
||||
setProgress(0f);
|
||||
@@ -544,6 +548,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect
|
||||
mWorkspace.getPageIndicator().getCaretDrawable(), mLauncher);
|
||||
mAppsView.getSearchUiManager().addOnScrollRangeChangeListener(this);
|
||||
mSpringAnimationHandler = mAppsView.getSpringAnimationHandler();
|
||||
mSearchSpring = mAppsView.getSearchUiManager().getSpringForFling();
|
||||
}
|
||||
|
||||
private boolean hasSpringAnimationHandler() {
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.android.launcher3.discovery.AppDiscoveryAppInfo;
|
||||
import com.android.launcher3.discovery.AppDiscoveryItem;
|
||||
import com.android.launcher3.discovery.AppDiscoveryUpdateState;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.ComponentKeyMapper;
|
||||
import com.android.launcher3.util.LabelComparator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -173,7 +174,7 @@ public class AlphabeticalAppsList {
|
||||
// The set of sections that we allow fast-scrolling to (includes non-merged sections)
|
||||
private final List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
|
||||
// The set of predicted app component names
|
||||
private final List<ComponentKey> mPredictedAppComponents = new ArrayList<>();
|
||||
private final List<ComponentKeyMapper<AppInfo>> mPredictedAppComponents = new ArrayList<>();
|
||||
// The set of predicted apps resolved from the component names and the current set of apps
|
||||
private final List<AppInfo> mPredictedApps = new ArrayList<>();
|
||||
private final List<AppDiscoveryAppInfo> mDiscoveredApps = new ArrayList<>();
|
||||
@@ -298,20 +299,20 @@ public class AlphabeticalAppsList {
|
||||
updateAdapterItems();
|
||||
}
|
||||
|
||||
private List<AppInfo> processPredictedAppComponents(List<ComponentKey> components) {
|
||||
private List<AppInfo> processPredictedAppComponents(List<ComponentKeyMapper<AppInfo>> components) {
|
||||
if (mComponentToAppMap.isEmpty()) {
|
||||
// Apps have not been bound yet.
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<AppInfo> predictedApps = new ArrayList<>();
|
||||
for (ComponentKey ck : components) {
|
||||
AppInfo info = mComponentToAppMap.get(ck);
|
||||
for (ComponentKeyMapper<AppInfo> mapper : components) {
|
||||
AppInfo info = mapper.getItem(mComponentToAppMap);
|
||||
if (info != null) {
|
||||
predictedApps.add(info);
|
||||
} else {
|
||||
if (FeatureFlags.IS_DOGFOOD_BUILD) {
|
||||
Log.e(TAG, "Predicted app not found: " + ck);
|
||||
Log.e(TAG, "Predicted app not found: " + mapper);
|
||||
}
|
||||
}
|
||||
// Stop at the number of predicted apps
|
||||
@@ -331,7 +332,7 @@ public class AlphabeticalAppsList {
|
||||
* If the number of predicted apps is the same as the previous list of predicted apps,
|
||||
* we can optimize by swapping them in place.
|
||||
*/
|
||||
public void setPredictedApps(List<ComponentKey> apps) {
|
||||
public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
|
||||
mPredictedAppComponents.clear();
|
||||
mPredictedAppComponents.addAll(apps);
|
||||
|
||||
@@ -472,14 +473,14 @@ public class AlphabeticalAppsList {
|
||||
|
||||
if (DEBUG_PREDICTIONS) {
|
||||
if (mPredictedAppComponents.isEmpty() && !mApps.isEmpty()) {
|
||||
mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName,
|
||||
Process.myUserHandle()));
|
||||
mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName,
|
||||
Process.myUserHandle()));
|
||||
mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName,
|
||||
Process.myUserHandle()));
|
||||
mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName,
|
||||
Process.myUserHandle()));
|
||||
mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
|
||||
Process.myUserHandle())));
|
||||
mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
|
||||
Process.myUserHandle())));
|
||||
mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
|
||||
Process.myUserHandle())));
|
||||
mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
|
||||
Process.myUserHandle())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,8 +645,8 @@ public class AlphabeticalAppsList {
|
||||
return result;
|
||||
}
|
||||
|
||||
public AppInfo findApp(ComponentKey key) {
|
||||
return mComponentToAppMap.get(key);
|
||||
public AppInfo findApp(ComponentKeyMapper<AppInfo> mapper) {
|
||||
return mapper.getItem(mComponentToAppMap);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.android.launcher3.allapps;
|
||||
|
||||
import android.support.animation.SpringAnimation;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
/**
|
||||
@@ -27,6 +29,11 @@ public interface SearchUiManager {
|
||||
*/
|
||||
void initialize(AlphabeticalAppsList appsList, AllAppsRecyclerView recyclerView);
|
||||
|
||||
/**
|
||||
* A {@link SpringAnimation} that will be used when the user flings.
|
||||
*/
|
||||
@NonNull SpringAnimation getSpringForFling();
|
||||
|
||||
/**
|
||||
* Notifies the search manager that the apps-list has changed and the search UI should be
|
||||
* updated accordingly.
|
||||
|
||||
@@ -17,6 +17,9 @@ package com.android.launcher3.allapps.search;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.support.animation.FloatValueHolder;
|
||||
import android.support.animation.SpringAnimation;
|
||||
import android.support.animation.SpringForce;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.Selection;
|
||||
@@ -62,6 +65,8 @@ public class AppsSearchContainerLayout extends FrameLayout
|
||||
private View mDivider;
|
||||
private HeaderElevationController mElevationController;
|
||||
|
||||
private SpringAnimation mSpring;
|
||||
|
||||
public AppsSearchContainerLayout(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
@@ -81,6 +86,9 @@ public class AppsSearchContainerLayout extends FrameLayout
|
||||
|
||||
mSearchQueryBuilder = new SpannableStringBuilder();
|
||||
Selection.setSelection(mSearchQueryBuilder, 0);
|
||||
|
||||
// Note: This spring does nothing.
|
||||
mSpring = new SpringAnimation(new FloatValueHolder()).setSpring(new SpringForce(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -126,6 +134,11 @@ public class AppsSearchContainerLayout extends FrameLayout
|
||||
new DefaultAppSearchAlgorithm(appsList.getApps()), mSearchInput, mLauncher, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull SpringAnimation getSpringForFling() {
|
||||
return mSpring;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshSearchResult() {
|
||||
mSearchBarController.refreshSearchResult();
|
||||
|
||||
@@ -69,6 +69,20 @@ public class SpringAnimationHandler<T> {
|
||||
mAnimationFactory = factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a spring to the list of springs handled by this class.
|
||||
* @param spring The new spring to be added.
|
||||
* @param setDefaultValues If True, sets the spring to the default
|
||||
* {@link AnimationFactory} values.
|
||||
*/
|
||||
public void add(SpringAnimation spring, boolean setDefaultValues) {
|
||||
if (setDefaultValues) {
|
||||
mAnimationFactory.setDefaultValues(spring);
|
||||
}
|
||||
spring.setStartVelocity(mCurrentVelocity);
|
||||
mAnimations.add(spring);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new or recycled animation to the list of springs handled by this class.
|
||||
*
|
||||
@@ -82,15 +96,17 @@ public class SpringAnimationHandler<T> {
|
||||
view.setTag(R.id.spring_animation_tag, spring);
|
||||
}
|
||||
mAnimationFactory.update(spring, object);
|
||||
spring.setStartVelocity(mCurrentVelocity);
|
||||
mAnimations.add(spring);
|
||||
add(spring, false /* setDefaultValues */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops and removes the spring attached to {@param view}.
|
||||
*/
|
||||
public void remove(View view) {
|
||||
SpringAnimation animation = (SpringAnimation) view.getTag(R.id.spring_animation_tag);
|
||||
remove((SpringAnimation) view.getTag(R.id.spring_animation_tag));
|
||||
}
|
||||
|
||||
public void remove(SpringAnimation animation) {
|
||||
if (animation.canSkipToEnd()) {
|
||||
animation.skipToEnd();
|
||||
}
|
||||
@@ -226,6 +242,11 @@ public class SpringAnimationHandler<T> {
|
||||
* Updates the value of {@param spring} based on {@param object}.
|
||||
*/
|
||||
void update(SpringAnimation spring, T object);
|
||||
|
||||
/**
|
||||
* Sets the factory default values for the given {@param spring}.
|
||||
*/
|
||||
void setDefaultValues(SpringAnimation spring);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -40,7 +40,7 @@ public abstract class AppWidgetManagerCompat {
|
||||
public static AppWidgetManagerCompat getInstance(Context context) {
|
||||
synchronized (sInstanceLock) {
|
||||
if (sInstance == null) {
|
||||
if (Utilities.isAtLeastO()) {
|
||||
if (Utilities.ATLEAST_OREO) {
|
||||
sInstance = new AppWidgetManagerCompatVO(context.getApplicationContext());
|
||||
} else {
|
||||
sInstance = new AppWidgetManagerCompatVL(context.getApplicationContext());
|
||||
|
||||
@@ -53,7 +53,7 @@ public abstract class LauncherAppsCompat {
|
||||
public static LauncherAppsCompat getInstance(Context context) {
|
||||
synchronized (sInstanceLock) {
|
||||
if (sInstance == null) {
|
||||
if (Utilities.isAtLeastO()) {
|
||||
if (Utilities.ATLEAST_OREO) {
|
||||
sInstance = new LauncherAppsCompatVO(context.getApplicationContext());
|
||||
} else {
|
||||
sInstance = new LauncherAppsCompatVL(context.getApplicationContext());
|
||||
|
||||
@@ -31,10 +31,10 @@ public abstract class WallpaperManagerCompat {
|
||||
if (sInstance == null) {
|
||||
context = context.getApplicationContext();
|
||||
|
||||
if (Utilities.isAtLeastO()) {
|
||||
if (Utilities.ATLEAST_OREO) {
|
||||
try {
|
||||
sInstance = new WallpaperManagerCompatVOMR1(context);
|
||||
} catch (Exception e) {
|
||||
} catch (Throwable e) {
|
||||
// The wallpaper APIs do not yet exist
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,96 +16,70 @@
|
||||
package com.android.launcher3.compat;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.WallpaperColors;
|
||||
import android.app.WallpaperManager;
|
||||
import android.app.WallpaperManager.OnColorsChangedListener;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
|
||||
@TargetApi(27)
|
||||
public class WallpaperManagerCompatVOMR1 extends WallpaperManagerCompat {
|
||||
|
||||
private static final String TAG = "WMCompatVOMR1";
|
||||
|
||||
private final WallpaperManager mWm;
|
||||
private Method mWCColorHintsMethod;
|
||||
|
||||
private final Class mOCLClass;
|
||||
private final Method mAddOCLMethod;
|
||||
|
||||
private final Method mWCGetMethod;
|
||||
private final Method mWCGetPrimaryColorMethod;
|
||||
private final Method mWCGetSecondaryColorMethod;
|
||||
private final Method mWCGetTertiaryColorMethod;
|
||||
private final Method mWCColorHintsMethod;
|
||||
|
||||
WallpaperManagerCompatVOMR1(Context context) throws Exception {
|
||||
WallpaperManagerCompatVOMR1(Context context) throws Throwable {
|
||||
mWm = context.getSystemService(WallpaperManager.class);
|
||||
|
||||
mOCLClass = Class.forName("android.app.WallpaperManager$OnColorsChangedListener");
|
||||
mAddOCLMethod = WallpaperManager.class.getDeclaredMethod(
|
||||
"addOnColorsChangedListener", mOCLClass, Handler.class);
|
||||
mWCGetMethod = WallpaperManager.class.getDeclaredMethod("getWallpaperColors", int.class);
|
||||
Class wallpaperColorsClass = mWCGetMethod.getReturnType();
|
||||
mWCGetPrimaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getPrimaryColor");
|
||||
mWCGetSecondaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getSecondaryColor");
|
||||
mWCGetTertiaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getTertiaryColor");
|
||||
mWCColorHintsMethod = wallpaperColorsClass.getDeclaredMethod("getColorHints");
|
||||
String className = WallpaperColors.class.getName();
|
||||
try {
|
||||
mWCColorHintsMethod = WallpaperColors.class.getDeclaredMethod("getColorHints");
|
||||
} catch (Exception exc) {
|
||||
Log.e(TAG, "getColorHints not available", exc);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public WallpaperColorsCompat getWallpaperColors(int which) {
|
||||
try {
|
||||
return convertColorsObject(mWCGetMethod.invoke(mWm, which));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error calling wallpaper API", e);
|
||||
return null;
|
||||
}
|
||||
return convertColorsObject(mWm.getWallpaperColors(which));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOnColorsChangedListener(final OnColorsChangedListenerCompat listener) {
|
||||
Object onChangeListener = Proxy.newProxyInstance(
|
||||
WallpaperManager.class.getClassLoader(),
|
||||
new Class[]{mOCLClass},
|
||||
new InvocationHandler() {
|
||||
@Override
|
||||
public Object invoke(Object o, Method method, Object[] objects)
|
||||
throws Throwable {
|
||||
String methodName = method.getName();
|
||||
if ("onColorsChanged".equals(methodName)) {
|
||||
listener.onColorsChanged(
|
||||
convertColorsObject(objects[0]), (Integer) objects[1]);
|
||||
} else if ("toString".equals(methodName)) {
|
||||
return listener.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
try {
|
||||
mAddOCLMethod.invoke(mWm, onChangeListener, null);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error calling wallpaper API", e);
|
||||
}
|
||||
OnColorsChangedListener onChangeListener = new OnColorsChangedListener() {
|
||||
@Override
|
||||
public void onColorsChanged(WallpaperColors colors, int which) {
|
||||
listener.onColorsChanged(convertColorsObject(colors), which);
|
||||
}
|
||||
};
|
||||
mWm.addOnColorsChangedListener(onChangeListener, null);
|
||||
}
|
||||
|
||||
private WallpaperColorsCompat convertColorsObject(Object colors) throws Exception {
|
||||
private WallpaperColorsCompat convertColorsObject(WallpaperColors colors) {
|
||||
if (colors == null) {
|
||||
return null;
|
||||
}
|
||||
Color primary = (Color) mWCGetPrimaryColorMethod.invoke(colors);
|
||||
Color secondary = (Color) mWCGetSecondaryColorMethod.invoke(colors);
|
||||
Color tertiary = (Color) mWCGetTertiaryColorMethod.invoke(colors);
|
||||
Color primary = colors.getPrimaryColor();
|
||||
Color secondary = colors.getSecondaryColor();
|
||||
Color tertiary = colors.getTertiaryColor();
|
||||
int primaryVal = primary != null ? primary.toArgb() : 0;
|
||||
int secondaryVal = secondary != null ? secondary.toArgb() : 0;
|
||||
int tertiaryVal = tertiary != null ? tertiary.toArgb() : 0;
|
||||
int colorHints = (Integer) mWCColorHintsMethod.invoke(colors);
|
||||
int colorHints = 0;
|
||||
try {
|
||||
if (mWCColorHintsMethod != null) {
|
||||
colorHints = (Integer) mWCColorHintsMethod.invoke(colors);
|
||||
}
|
||||
} catch (Exception exc) {
|
||||
Log.e(TAG, "error calling color hints", exc);
|
||||
}
|
||||
return new WallpaperColorsCompat(primaryVal, secondaryVal, tertiaryVal, colorHints);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,8 +56,6 @@ abstract class BaseFlags {
|
||||
public static final boolean PULLDOWN_SEARCH = false;
|
||||
// When enabled the status bar may show dark icons based on the top of the wallpaper.
|
||||
public static final boolean LIGHT_STATUS_BAR = false;
|
||||
// When enabled icons are badged with the number of notifications associated with that app.
|
||||
public static final boolean BADGE_ICONS = true;
|
||||
// When enabled, icons not supporting {@link AdaptiveIconDrawable} will be wrapped in {@link FixedScaleDrawable}.
|
||||
public static final boolean LEGACY_ICON_TREATMENT = true;
|
||||
// When enabled, adaptive icons would have shadows baked when being stored to icon cache.
|
||||
|
||||
@@ -544,7 +544,6 @@ public class DragLayer extends InsettableFrameLayout {
|
||||
|
||||
public void clearResizeFrame() {
|
||||
if (mCurrentResizeFrame != null) {
|
||||
mCurrentResizeFrame.commitResize();
|
||||
removeView(mCurrentResizeFrame);
|
||||
mCurrentResizeFrame = null;
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ public class DragView extends View {
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
public void setItemInfo(final ItemInfo info) {
|
||||
if (!(FeatureFlags.LAUNCHER3_SPRING_ICONS && Utilities.isAtLeastO())) {
|
||||
if (!(FeatureFlags.LAUNCHER3_SPRING_ICONS && Utilities.ATLEAST_OREO)) {
|
||||
return;
|
||||
}
|
||||
if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
|
||||
|
||||
@@ -128,7 +128,7 @@ public class PinItemDragListener extends BaseItemDragListener implements Parcela
|
||||
}
|
||||
|
||||
public static boolean handleDragRequest(Launcher launcher, Intent intent) {
|
||||
if (!Utilities.isAtLeastO()) {
|
||||
if (!Utilities.ATLEAST_OREO) {
|
||||
return false;
|
||||
}
|
||||
if (intent == null || !Intent.ACTION_MAIN.equals(intent.getAction())) {
|
||||
|
||||
@@ -25,7 +25,9 @@ import android.content.pm.LauncherApps.PinItemRequest;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
|
||||
import com.android.launcher3.FastBitmapDrawable;
|
||||
import com.android.launcher3.IconCache;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
@@ -69,8 +71,12 @@ class PinShortcutRequestActivityInfo extends ShortcutConfigActivityInfo {
|
||||
|
||||
@Override
|
||||
public Drawable getFullResIcon(IconCache cache) {
|
||||
return mContext.getSystemService(LauncherApps.class)
|
||||
Drawable d = mContext.getSystemService(LauncherApps.class)
|
||||
.getShortcutIconDrawable(mInfo, LauncherAppState.getIDP(mContext).fillResIconDpi);
|
||||
if (d == null) {
|
||||
d = new FastBitmapDrawable(cache.getDefaultIcon(Process.myUserHandle()));
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -61,6 +61,7 @@ import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.badge.BadgeRenderer;
|
||||
import com.android.launcher3.badge.FolderBadgeInfo;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.dragndrop.BaseItemDragListener;
|
||||
import com.android.launcher3.dragndrop.DragLayer;
|
||||
import com.android.launcher3.dragndrop.DragView;
|
||||
import com.android.launcher3.graphics.IconPalette;
|
||||
@@ -375,6 +376,9 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
||||
if (d.dragInfo instanceof AppInfo) {
|
||||
// Came from all apps -- make a copy
|
||||
item = ((AppInfo) d.dragInfo).makeShortcut();
|
||||
} else if (d.dragSource instanceof BaseItemDragListener){
|
||||
// Came from a different window -- make a copy
|
||||
item = new ShortcutInfo((ShortcutInfo) d.dragInfo);
|
||||
} else {
|
||||
item = (ShortcutInfo) d.dragInfo;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ public class DrawableFactory {
|
||||
|
||||
|
||||
protected Path getPreloadProgressPath(Context context) {
|
||||
if (Utilities.isAtLeastO()) {
|
||||
if (Utilities.ATLEAST_OREO) {
|
||||
try {
|
||||
// Try to load the path from Mask Icon
|
||||
Drawable icon = context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper);
|
||||
|
||||
@@ -231,7 +231,7 @@ public class IconNormalizer {
|
||||
*/
|
||||
public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds,
|
||||
@Nullable Path path, @Nullable boolean[] outMaskShape) {
|
||||
if (Utilities.isAtLeastO() && d instanceof AdaptiveIconDrawable &&
|
||||
if (Utilities.ATLEAST_OREO && d instanceof AdaptiveIconDrawable &&
|
||||
mAdaptiveIconScale != SCALE_NOT_INITIALIZED) {
|
||||
if (outBounds != null) {
|
||||
outBounds.set(mAdaptiveIconBounds);
|
||||
@@ -347,7 +347,7 @@ public class IconNormalizer {
|
||||
float areaScale = area / (width * height);
|
||||
// Use sqrt of the final ratio as the images is scaled across both width and height.
|
||||
float scale = areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1;
|
||||
if (Utilities.isAtLeastO() && d instanceof AdaptiveIconDrawable &&
|
||||
if (Utilities.ATLEAST_OREO && d instanceof AdaptiveIconDrawable &&
|
||||
mAdaptiveIconScale == SCALE_NOT_INITIALIZED) {
|
||||
mAdaptiveIconScale = scale;
|
||||
mAdaptiveIconBounds.set(mBounds);
|
||||
|
||||
@@ -59,7 +59,7 @@ public class IconShapeOverride {
|
||||
private static final int RESTART_REQUEST_CODE = 42; // the answer to everything
|
||||
|
||||
public static boolean isSupported(Context context) {
|
||||
if (!Utilities.isAtLeastO()) {
|
||||
if (!Utilities.ATLEAST_OREO) {
|
||||
return false;
|
||||
}
|
||||
// Only supported when developer settings is enabled
|
||||
@@ -82,7 +82,7 @@ public class IconShapeOverride {
|
||||
}
|
||||
|
||||
public static void apply(Context context) {
|
||||
if (!Utilities.isAtLeastO()) {
|
||||
if (!Utilities.ATLEAST_OREO) {
|
||||
return;
|
||||
}
|
||||
String path = getAppliedValue(context);
|
||||
|
||||
@@ -38,6 +38,7 @@ import android.os.UserHandle;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.AppInfo;
|
||||
import com.android.launcher3.FastBitmapDrawable;
|
||||
import com.android.launcher3.IconCache;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.R;
|
||||
@@ -103,7 +104,7 @@ public class LauncherIcons {
|
||||
float scale = 1f;
|
||||
if (!FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION) {
|
||||
normalizer = IconNormalizer.getInstance(context);
|
||||
if (Utilities.isAtLeastO() && iconAppTargetSdk >= Build.VERSION_CODES.O) {
|
||||
if (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) {
|
||||
boolean[] outShape = new boolean[1];
|
||||
AdaptiveIconDrawable dr = (AdaptiveIconDrawable)
|
||||
context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate();
|
||||
@@ -122,7 +123,7 @@ public class LauncherIcons {
|
||||
}
|
||||
}
|
||||
Bitmap bitmap = createIconBitmap(icon, context, scale);
|
||||
if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.isAtLeastO() &&
|
||||
if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO &&
|
||||
icon instanceof AdaptiveIconDrawable) {
|
||||
bitmap = ShadowGenerator.getInstance(context).recreateIcon(bitmap);
|
||||
}
|
||||
@@ -157,13 +158,13 @@ public class LauncherIcons {
|
||||
float scale = 1f;
|
||||
if (!FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION) {
|
||||
normalizer = IconNormalizer.getInstance(context);
|
||||
if (Utilities.isAtLeastO() && iconAppTargetSdk >= Build.VERSION_CODES.O) {
|
||||
if (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) {
|
||||
boolean[] outShape = new boolean[1];
|
||||
AdaptiveIconDrawable dr = (AdaptiveIconDrawable)
|
||||
context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate();
|
||||
dr.setBounds(0, 0, 1, 1);
|
||||
scale = normalizer.getScale(icon, iconBounds, dr.getIconMask(), outShape);
|
||||
if (Utilities.isAtLeastO() && FeatureFlags.LEGACY_ICON_TREATMENT &&
|
||||
if (Utilities.ATLEAST_OREO && FeatureFlags.LEGACY_ICON_TREATMENT &&
|
||||
!outShape[0]) {
|
||||
Drawable wrappedIcon = wrapToAdaptiveIconDrawable(context, icon, scale);
|
||||
if (wrappedIcon != icon) {
|
||||
@@ -192,13 +193,16 @@ public class LauncherIcons {
|
||||
* Adds the {@param badge} on top of {@param srcTgt} using the badge dimensions.
|
||||
*/
|
||||
public static Bitmap badgeWithBitmap(Bitmap srcTgt, Bitmap badge, Context context) {
|
||||
return badgeWithDrawable(srcTgt, new FastBitmapDrawable(badge), context);
|
||||
}
|
||||
|
||||
public static Bitmap badgeWithDrawable(Bitmap srcTgt, Drawable badge, Context context) {
|
||||
int badgeSize = context.getResources().getDimensionPixelSize(R.dimen.profile_badge_size);
|
||||
synchronized (sCanvas) {
|
||||
sCanvas.setBitmap(srcTgt);
|
||||
sCanvas.drawBitmap(badge, new Rect(0, 0, badge.getWidth(), badge.getHeight()),
|
||||
new Rect(srcTgt.getWidth() - badgeSize,
|
||||
srcTgt.getHeight() - badgeSize, srcTgt.getWidth(), srcTgt.getHeight()),
|
||||
new Paint(Paint.FILTER_BITMAP_FLAG));
|
||||
int iconSize = srcTgt.getWidth();
|
||||
badge.setBounds(iconSize - badgeSize, iconSize - badgeSize, iconSize, iconSize);
|
||||
badge.draw(sCanvas);
|
||||
sCanvas.setBitmap(null);
|
||||
}
|
||||
return srcTgt;
|
||||
@@ -209,12 +213,12 @@ public class LauncherIcons {
|
||||
*/
|
||||
public static Bitmap createIconBitmap(Drawable icon, Context context) {
|
||||
float scale = 1f;
|
||||
if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.isAtLeastO() &&
|
||||
if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO &&
|
||||
icon instanceof AdaptiveIconDrawable) {
|
||||
scale = ShadowGenerator.getScaleForBounds(new RectF(0, 0, 0, 0));
|
||||
}
|
||||
Bitmap bitmap = createIconBitmap(icon, context, scale);
|
||||
if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.isAtLeastO() &&
|
||||
if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO &&
|
||||
icon instanceof AdaptiveIconDrawable) {
|
||||
bitmap = ShadowGenerator.getInstance(context).recreateIcon(bitmap);
|
||||
}
|
||||
@@ -267,7 +271,7 @@ public class LauncherIcons {
|
||||
final int top = (textureHeight-height) / 2;
|
||||
|
||||
sOldBounds.set(icon.getBounds());
|
||||
if (Utilities.isAtLeastO() && icon instanceof AdaptiveIconDrawable) {
|
||||
if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
|
||||
int offset = Math.max((int)(ShadowGenerator.BLUR_FACTOR * iconBitmapSize),
|
||||
Math.min(left, top));
|
||||
int size = Math.max(width, height);
|
||||
@@ -292,7 +296,7 @@ public class LauncherIcons {
|
||||
* create AdaptiveIconDrawable.
|
||||
*/
|
||||
static Drawable wrapToAdaptiveIconDrawable(Context context, Drawable drawable, float scale) {
|
||||
if (!(FeatureFlags.LEGACY_ICON_TREATMENT && Utilities.isAtLeastO())) {
|
||||
if (!(FeatureFlags.LEGACY_ICON_TREATMENT && Utilities.ATLEAST_OREO)) {
|
||||
return drawable;
|
||||
}
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ public class ShadowDrawable extends Drawable {
|
||||
d.draw(canvas);
|
||||
}
|
||||
|
||||
if (Utilities.isAtLeastO()) {
|
||||
if (Utilities.ATLEAST_OREO) {
|
||||
bitmap = bitmap.copy(Bitmap.Config.HARDWARE, false);
|
||||
}
|
||||
mState.mLastDrawnBitmap = bitmap;
|
||||
|
||||
@@ -88,7 +88,7 @@ public class CacheDataUpdatedTask extends BaseModelUpdateTask {
|
||||
case OP_CACHE_UPDATE:
|
||||
return true;
|
||||
case OP_SESSION_UPDATE:
|
||||
return si.isPromise();
|
||||
return si.hasPromiseIconUi();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -431,6 +431,10 @@ public class LoaderTask implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
if ((c.restoreFlag & ShortcutInfo.FLAG_SUPPORTS_WEB_UI) != 0) {
|
||||
validTarget = false;
|
||||
}
|
||||
|
||||
if (validTarget) {
|
||||
// The shortcut points to a valid target (either no target
|
||||
// or something which is ready to be used)
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
package com.android.launcher3.model;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Process;
|
||||
|
||||
import com.android.launcher3.AllAppsList;
|
||||
import com.android.launcher3.AppInfo;
|
||||
@@ -28,6 +31,7 @@ import com.android.launcher3.PromiseAppInfo;
|
||||
import com.android.launcher3.ShortcutInfo;
|
||||
import com.android.launcher3.compat.PackageInstallerCompat;
|
||||
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
|
||||
import com.android.launcher3.util.InstantAppResolver;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
@@ -46,6 +50,17 @@ public class PackageInstallStateChangedTask extends BaseModelUpdateTask {
|
||||
@Override
|
||||
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
|
||||
if (mInstallInfo.state == PackageInstallerCompat.STATUS_INSTALLED) {
|
||||
try {
|
||||
// For instant apps we do not get package-add. Use setting events to update
|
||||
// any pinned icons.
|
||||
ApplicationInfo ai = app.getContext()
|
||||
.getPackageManager().getApplicationInfo(mInstallInfo.packageName, 0);
|
||||
if (InstantAppResolver.newInstance(app.getContext()).isInstantApp(ai)) {
|
||||
app.getModel().onPackageAdded(ai.packageName, Process.myUserHandle());
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// Ignore
|
||||
}
|
||||
// Ignore install success events as they are handled by Package add events.
|
||||
return;
|
||||
}
|
||||
@@ -94,7 +109,7 @@ public class PackageInstallStateChangedTask extends BaseModelUpdateTask {
|
||||
if (info instanceof ShortcutInfo) {
|
||||
ShortcutInfo si = (ShortcutInfo) info;
|
||||
ComponentName cn = si.getTargetComponent();
|
||||
if (si.isPromise() && (cn != null)
|
||||
if (si.hasPromiseIconUi() && (cn != null)
|
||||
&& mInstallInfo.packageName.equals(cn.getPackageName())) {
|
||||
si.setInstallProgress(mInstallInfo.progress);
|
||||
if (mInstallInfo.state == PackageInstallerCompat.STATUS_FAILED) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.launcher3.AllAppsList;
|
||||
import com.android.launcher3.AppInfo;
|
||||
import com.android.launcher3.IconCache;
|
||||
@@ -32,7 +33,6 @@ import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherAppWidgetInfo;
|
||||
import com.android.launcher3.LauncherModel.CallbackTask;
|
||||
import com.android.launcher3.LauncherModel.Callbacks;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.LauncherSettings.Favorites;
|
||||
import com.android.launcher3.SessionCommitReceiver;
|
||||
import com.android.launcher3.ShortcutInfo;
|
||||
@@ -46,6 +46,7 @@ import com.android.launcher3.util.ItemInfoMatcher;
|
||||
import com.android.launcher3.util.LongArrayMap;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -100,10 +101,11 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
|
||||
appsList.addPackage(context, packages[i], mUser);
|
||||
|
||||
// Automatically add homescreen icon for work profile apps for below O device.
|
||||
if (!Utilities.isAtLeastO() && !Process.myUserHandle().equals(mUser)) {
|
||||
if (!Utilities.ATLEAST_OREO && !Process.myUserHandle().equals(mUser)) {
|
||||
SessionCommitReceiver.queueAppIconAddition(context, packages[i], mUser);
|
||||
}
|
||||
}
|
||||
flagOp = FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
|
||||
break;
|
||||
}
|
||||
case OP_UPDATE:
|
||||
@@ -170,12 +172,15 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
|
||||
}
|
||||
}
|
||||
|
||||
final LongArrayMap<Boolean> removedShortcuts = new LongArrayMap<>();
|
||||
|
||||
// Update shortcut infos
|
||||
if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) {
|
||||
final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<>();
|
||||
final LongArrayMap<Boolean> removedShortcuts = new LongArrayMap<>();
|
||||
final ArrayList<LauncherAppWidgetInfo> widgets = new ArrayList<>();
|
||||
|
||||
// For system apps, package manager send OP_UPDATE when an app is enabled.
|
||||
final boolean isNewApkAvailable = mOp == OP_ADD || mOp == OP_UPDATE;
|
||||
synchronized (dataModel) {
|
||||
for (ItemInfo info : dataModel.itemsIdMap) {
|
||||
if (info instanceof ShortcutInfo && mUser.equals(info.user)) {
|
||||
@@ -197,9 +202,14 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
|
||||
if (cn != null && matcher.matches(si, cn)) {
|
||||
AppInfo appInfo = addedOrUpdatedApps.get(cn);
|
||||
|
||||
// For system apps, package manager send OP_UPDATE when an
|
||||
// app is enabled.
|
||||
if (si.isPromise() && (mOp == OP_ADD || mOp == OP_UPDATE)) {
|
||||
if (si.hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI)) {
|
||||
removedShortcuts.put(si.id, false);
|
||||
if (mOp == OP_REMOVE) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (si.isPromise() && isNewApkAvailable) {
|
||||
if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) {
|
||||
// Auto install icon
|
||||
LauncherAppsCompat launcherApps
|
||||
@@ -213,23 +223,23 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
|
||||
appInfo = addedOrUpdatedApps.get(cn);
|
||||
}
|
||||
|
||||
if ((intent == null) || (appInfo == null)) {
|
||||
if (intent != null && appInfo != null) {
|
||||
si.intent = intent;
|
||||
si.status = ShortcutInfo.DEFAULT;
|
||||
infoUpdated = true;
|
||||
} else if (si.hasPromiseIconUi()) {
|
||||
removedShortcuts.put(si.id, true);
|
||||
continue;
|
||||
}
|
||||
si.intent = intent;
|
||||
}
|
||||
}
|
||||
|
||||
si.status = ShortcutInfo.DEFAULT;
|
||||
infoUpdated = true;
|
||||
if (si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
|
||||
iconCache.getTitleAndIcon(si, si.usingLowResIcon);
|
||||
} else {
|
||||
si.status = ShortcutInfo.DEFAULT;
|
||||
infoUpdated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (appInfo != null && Intent.ACTION_MAIN.equals(si.intent.getAction())
|
||||
&& si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
|
||||
if (isNewApkAvailable &&
|
||||
si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
|
||||
iconCache.getTitleAndIcon(si, si.usingLowResIcon);
|
||||
infoUpdated = true;
|
||||
}
|
||||
@@ -247,7 +257,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
|
||||
if (infoUpdated) {
|
||||
getModelWriter().updateItemInDatabase(si);
|
||||
}
|
||||
} else if (info instanceof LauncherAppWidgetInfo && mOp == OP_ADD) {
|
||||
} else if (info instanceof LauncherAppWidgetInfo && isNewApkAvailable) {
|
||||
LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
|
||||
if (mUser.equals(widgetInfo.user)
|
||||
&& widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
|
||||
@@ -308,7 +318,8 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
|
||||
|
||||
if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
|
||||
ItemInfoMatcher removeMatch = ItemInfoMatcher.ofPackages(removedPackages, mUser)
|
||||
.or(ItemInfoMatcher.ofComponents(removedComponents, mUser));
|
||||
.or(ItemInfoMatcher.ofComponents(removedComponents, mUser))
|
||||
.and(ItemInfoMatcher.ofItemIds(removedShortcuts, true));
|
||||
deleteAndBindComponentsRemoved(removeMatch);
|
||||
|
||||
// Remove any queued items from the install queue
|
||||
@@ -335,8 +346,9 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
|
||||
callbacks.notifyWidgetProvidersChanged();
|
||||
}
|
||||
});
|
||||
} else if (Utilities.isAtLeastO() && mOp == OP_ADD) {
|
||||
// Load widgets for the new package.
|
||||
} else if (Utilities.ATLEAST_OREO && mOp == OP_ADD) {
|
||||
// Load widgets for the new package. Changes due to app updates are handled through
|
||||
// AppWidgetHost events, this is just to initialize the long-press options.
|
||||
for (int i = 0; i < N; i++) {
|
||||
dataModel.widgetsModel.update(app, new PackageUserKey(packages[i], mUser));
|
||||
}
|
||||
|
||||
@@ -200,7 +200,9 @@ public class NotificationFooterLayout extends FrameLayout {
|
||||
PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(
|
||||
Launcher.getLauncher(getContext()));
|
||||
if (popup != null) {
|
||||
Animator collapseFooter = popup.reduceNotificationViewHeight(getHeight(),
|
||||
final int newHeight = getResources().getDimensionPixelSize(
|
||||
R.dimen.notification_empty_footer_height);
|
||||
Animator collapseFooter = popup.reduceNotificationViewHeight(getHeight() - newHeight,
|
||||
getResources().getInteger(R.integer.config_removeNotificationViewDuration));
|
||||
collapseFooter.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
@@ -208,7 +210,7 @@ public class NotificationFooterLayout extends FrameLayout {
|
||||
((ViewGroup) getParent()).findViewById(R.id.divider).setVisibility(GONE);
|
||||
// Keep view around because gutter is aligned to it, but remove height to
|
||||
// both hide the view and keep calculations correct for last dismissal.
|
||||
getLayoutParams().height = 0;
|
||||
getLayoutParams().height = newHeight;
|
||||
requestLayout();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -94,6 +94,9 @@ public class NotificationInfo implements View.OnClickListener {
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (intent == null) {
|
||||
return;
|
||||
}
|
||||
final Launcher launcher = Launcher.getLauncher(view.getContext());
|
||||
Bundle activityOptions = ActivityOptions.makeClipRevealAnimation(
|
||||
view, 0, 0, view.getWidth(), view.getHeight()).toBundle();
|
||||
|
||||
@@ -90,9 +90,19 @@ public class NotificationItemView extends PopupItemView implements LogContainerP
|
||||
return mMainView;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to calculate the height to remove when dismissing the last notification.
|
||||
* We subtract the height of the footer in this case since the footer should be gone or in the
|
||||
* process of being removed.
|
||||
* @return The height of the entire notification item, minus the footer if it still exists.
|
||||
*/
|
||||
public int getHeightMinusFooter() {
|
||||
int footerHeight = mFooter.getParent() == null ? 0 : mFooter.getHeight();
|
||||
return getHeight() - footerHeight;
|
||||
if (mFooter.getParent() == null) {
|
||||
return getHeight();
|
||||
}
|
||||
int excessFooterHeight = mFooter.getHeight() - getResources().getDimensionPixelSize(
|
||||
R.dimen.notification_empty_footer_height);
|
||||
return getHeight() - excessFooterHeight;
|
||||
}
|
||||
|
||||
public Animator animateHeightRemoval(int heightToRemove, boolean shouldRemoveFromTop) {
|
||||
|
||||
@@ -32,7 +32,6 @@ import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.android.launcher3.LauncherModel;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
import com.android.launcher3.util.SettingsObserver;
|
||||
|
||||
@@ -164,9 +163,6 @@ public class NotificationListener extends NotificationListenerService {
|
||||
}
|
||||
|
||||
public static void setNotificationsChangedListener(NotificationsChangedListener listener) {
|
||||
if (!FeatureFlags.BADGE_ICONS) {
|
||||
return;
|
||||
}
|
||||
sNotificationsChangedListener = listener;
|
||||
|
||||
NotificationListener notificationListener = getInstanceIfConnected();
|
||||
|
||||
@@ -48,7 +48,6 @@ public class PageIndicatorCaretLandscape extends PageIndicator {
|
||||
setCaretDrawable(caretDrawable);
|
||||
|
||||
Launcher l = Launcher.getLauncher(context);
|
||||
setOnTouchListener(l.getHapticFeedbackTouchListener());
|
||||
setOnClickListener(l);
|
||||
setOnFocusChangeListener(l.mFocusHandler);
|
||||
}
|
||||
|
||||
@@ -141,7 +141,6 @@ public class PageIndicatorLineCaret extends PageIndicator {
|
||||
super.onFinishInflate();
|
||||
mAllAppsHandle = (ImageView) findViewById(R.id.all_apps_handle);
|
||||
mAllAppsHandle.setImageDrawable(getCaretDrawable());
|
||||
mAllAppsHandle.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
|
||||
mAllAppsHandle.setOnClickListener(mLauncher);
|
||||
mAllAppsHandle.setOnFocusChangeListener(mLauncher.mFocusHandler);
|
||||
mLauncher.setAllAppsButton(mAllAppsHandle);
|
||||
|
||||
@@ -275,8 +275,9 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
|
||||
if (itemTypeToPopulate == PopupPopulator.Item.NOTIFICATION) {
|
||||
mNotificationItemView = (NotificationItemView) item;
|
||||
boolean notificationFooterHasIcons = numNotifications > 1;
|
||||
int footerHeight = notificationFooterHasIcons ?
|
||||
res.getDimensionPixelSize(R.dimen.notification_footer_height) : 0;
|
||||
int footerHeight = res.getDimensionPixelSize(
|
||||
notificationFooterHasIcons ? R.dimen.notification_footer_height
|
||||
: R.dimen.notification_empty_footer_height);
|
||||
item.findViewById(R.id.footer).getLayoutParams().height = footerHeight;
|
||||
if (notificationFooterHasIcons) {
|
||||
mNotificationItemView.findViewById(R.id.divider).setVisibility(VISIBLE);
|
||||
|
||||
@@ -66,7 +66,7 @@ public class DeepShortcutManager {
|
||||
|
||||
public static boolean supportsShortcuts(ItemInfo info) {
|
||||
boolean isItemPromise = info instanceof com.android.launcher3.ShortcutInfo
|
||||
&& ((com.android.launcher3.ShortcutInfo) info).isPromise();
|
||||
&& ((com.android.launcher3.ShortcutInfo) info).hasPromiseIconUi();
|
||||
return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
|
||||
&& !info.isDisabled() && !isItemPromise;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.android.launcher3.AppInfo;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherCallbacks;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.ComponentKeyMapper;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
@@ -197,7 +198,7 @@ public class LauncherExtension extends Launcher {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ComponentKey> getPredictedApps() {
|
||||
public List<ComponentKeyMapper<AppInfo>> getPredictedApps() {
|
||||
// To debug app predictions, enable AlphabeticalAppsList#DEBUG_PREDICTIONS
|
||||
return new ArrayList<>();
|
||||
}
|
||||
@@ -214,10 +215,5 @@ public class LauncherExtension extends Launcher {
|
||||
@Override
|
||||
public void onDetachedFromWindow() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldShowDiscoveryBounce() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.android.launcher3.util;
|
||||
|
||||
/**
|
||||
* Copyright (C) 2017 The Android Open 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.
|
||||
*/
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ComponentKeyMapper<T> {
|
||||
|
||||
protected final ComponentKey mComponentKey;
|
||||
|
||||
public ComponentKeyMapper(ComponentKey key) {
|
||||
this.mComponentKey = key;
|
||||
}
|
||||
|
||||
public @Nullable T getItem(Map<ComponentKey, T> map) {
|
||||
return map.get(mComponentKey);
|
||||
}
|
||||
|
||||
public String getPackage() {
|
||||
return mComponentKey.componentName.getPackageName();
|
||||
}
|
||||
|
||||
public String getComponentClass() {
|
||||
return mComponentKey.componentName.getClassName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mComponentKey.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A wrapper class to access instant app related APIs.
|
||||
*/
|
||||
public class InstantAppResolver {
|
||||
|
||||
public static InstantAppResolver newInstance(Context context) {
|
||||
return Utilities.getOverrideObject(
|
||||
InstantAppResolver.class, context, R.string.instant_app_resolver_class);
|
||||
}
|
||||
|
||||
public boolean isInstantApp(ApplicationInfo info) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<ApplicationInfo> getInstantApps() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ public class ManagedProfileHeuristic {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Utilities.isAtLeastO() && !SessionCommitReceiver.isEnabled(context)) {
|
||||
if (Utilities.ATLEAST_OREO && !SessionCommitReceiver.isEnabled(context)) {
|
||||
// Just mark the folder id preference to avoid new folder creation later.
|
||||
ufi.prefs.edit().putLong(ufi.folderIdKey, ItemInfo.NO_ID).apply();
|
||||
return;
|
||||
|
||||
@@ -59,7 +59,7 @@ public class SystemUiController {
|
||||
// Apply the state flags in priority order
|
||||
int newFlags = oldFlags;
|
||||
for (int stateFlag : mStates) {
|
||||
if (Utilities.isAtLeastO()) {
|
||||
if (Utilities.ATLEAST_OREO) {
|
||||
if ((stateFlag & FLAG_LIGHT_NAV) != 0) {
|
||||
newFlags |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
|
||||
} else if ((stateFlag & FLAG_DARK_NAV) != 0) {
|
||||
|
||||
@@ -41,4 +41,8 @@ public class WidgetListRowEntry {
|
||||
this.widgets = items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return pkgItem.packageName + ":" + widgets.size();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,12 @@
|
||||
package com.android.launcher3.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.graphics.Point;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
@@ -31,8 +33,10 @@ import com.android.launcher3.DragSource;
|
||||
import com.android.launcher3.DropTarget.DragObject;
|
||||
import com.android.launcher3.ItemInfo;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.compat.AlphabeticIndexCompat;
|
||||
import com.android.launcher3.dragndrop.DragOptions;
|
||||
import com.android.launcher3.folder.Folder;
|
||||
import com.android.launcher3.model.PackageItemInfo;
|
||||
@@ -74,7 +78,11 @@ public class WidgetsContainerView extends BaseContainerView
|
||||
public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mLauncher = Launcher.getLauncher(context);
|
||||
mAdapter = new WidgetsListAdapter(this, this, context);
|
||||
LauncherAppState apps = LauncherAppState.getInstance(context);
|
||||
mAdapter = new WidgetsListAdapter(context, LayoutInflater.from(context),
|
||||
apps.getWidgetCache(), new AlphabeticIndexCompat(context), this, this,
|
||||
new WidgetsDiffReporter(apps.getIconCache()));
|
||||
mAdapter.setNotifyListener();
|
||||
if (LOGD) {
|
||||
Log.d(TAG, "WidgetsContainerView constructor");
|
||||
}
|
||||
@@ -232,7 +240,6 @@ public class WidgetsContainerView extends BaseContainerView
|
||||
*/
|
||||
public void setWidgets(MultiHashMap<PackageItemInfo, WidgetItem> model) {
|
||||
mAdapter.setWidgets(model);
|
||||
mAdapter.notifyDataSetChanged();
|
||||
|
||||
View loader = getContentView().findViewById(R.id.loader);
|
||||
if (loader != null) {
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open 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.widget;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.launcher3.IconCache;
|
||||
import com.android.launcher3.model.PackageItemInfo;
|
||||
import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Do diff on widget's tray list items and call the {@link NotifyListener} methods accordingly.
|
||||
*/
|
||||
public class WidgetsDiffReporter {
|
||||
private final boolean DEBUG = false;
|
||||
private final String TAG = "WidgetsDiffReporter";
|
||||
private final IconCache mIconCache;
|
||||
private NotifyListener mListener;
|
||||
|
||||
public interface NotifyListener {
|
||||
void notifyDataSetChanged();
|
||||
void notifyItemChanged(int index);
|
||||
void notifyItemInserted(int index);
|
||||
void notifyItemRemoved(int index);
|
||||
}
|
||||
|
||||
public WidgetsDiffReporter(IconCache iconCache) {
|
||||
mIconCache = iconCache;
|
||||
}
|
||||
|
||||
public void setListener(NotifyListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public void process(ArrayList<WidgetListRowEntry> currentEntries,
|
||||
ArrayList<WidgetListRowEntry> newEntries, WidgetListRowEntryComparator comparator) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "process oldEntries#=" + currentEntries.size()
|
||||
+ " newEntries#=" + newEntries.size());
|
||||
}
|
||||
if (currentEntries.size() == 0 && newEntries.size() > 0) {
|
||||
currentEntries.addAll(newEntries);
|
||||
mListener.notifyDataSetChanged();
|
||||
return;
|
||||
}
|
||||
ArrayList<WidgetListRowEntry> orgEntries =
|
||||
(ArrayList<WidgetListRowEntry>) currentEntries.clone();
|
||||
Iterator<WidgetListRowEntry> orgIter = orgEntries.iterator();
|
||||
Iterator<WidgetListRowEntry> newIter = newEntries.iterator();
|
||||
|
||||
WidgetListRowEntry orgRowEntry = orgIter.next();
|
||||
WidgetListRowEntry newRowEntry = newIter.next();
|
||||
|
||||
do {
|
||||
int diff = comparePackageName(orgRowEntry, newRowEntry, comparator);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, String.format("diff=%d orgRowEntry (%s) newRowEntry (%s)",
|
||||
diff, orgRowEntry != null? orgRowEntry.toString() : null,
|
||||
newRowEntry != null? newRowEntry.toString() : null));
|
||||
}
|
||||
int index = -1;
|
||||
if (diff < 0) {
|
||||
index = currentEntries.indexOf(orgRowEntry);
|
||||
mListener.notifyItemRemoved(index);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, String.format("notifyItemRemoved called (%d)%s", index,
|
||||
orgRowEntry.titleSectionName));
|
||||
}
|
||||
currentEntries.remove(index);
|
||||
orgRowEntry = orgIter.hasNext() ? orgIter.next() : null;
|
||||
} else if (diff > 0) {
|
||||
index = orgRowEntry != null? currentEntries.indexOf(orgRowEntry):
|
||||
currentEntries.size();
|
||||
currentEntries.add(index, newRowEntry);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, String.format("notifyItemInserted called (%d)%s", index,
|
||||
newRowEntry.titleSectionName));
|
||||
}
|
||||
newRowEntry = newIter.hasNext() ? newIter.next() : null;
|
||||
mListener.notifyItemInserted(index);
|
||||
|
||||
} else {
|
||||
// same package name but,
|
||||
// did the icon, title, etc, change?
|
||||
// or did the widget size and desc, span, etc change?
|
||||
if (!isSamePackageItemInfo(orgRowEntry.pkgItem, newRowEntry.pkgItem) ||
|
||||
!orgRowEntry.widgets.equals(newRowEntry.widgets)) {
|
||||
index = currentEntries.indexOf(orgRowEntry);
|
||||
currentEntries.set(index, newRowEntry);
|
||||
mListener.notifyItemChanged(index);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, String.format("notifyItemChanged called (%d)%s", index,
|
||||
newRowEntry.titleSectionName));
|
||||
}
|
||||
}
|
||||
orgRowEntry = orgIter.hasNext() ? orgIter.next() : null;
|
||||
newRowEntry = newIter.hasNext() ? newIter.next() : null;
|
||||
}
|
||||
} while(orgRowEntry != null || newRowEntry != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare package name using the same comparator as in {@link WidgetsListAdapter}.
|
||||
* Also handle null row pointers.
|
||||
*/
|
||||
private int comparePackageName(WidgetListRowEntry curRow, WidgetListRowEntry newRow,
|
||||
WidgetListRowEntryComparator comparator) {
|
||||
if (curRow == null && newRow == null) {
|
||||
throw new IllegalStateException("Cannot compare PackageItemInfo if both rows are null.");
|
||||
}
|
||||
|
||||
if (curRow == null && newRow != null) {
|
||||
return 1; // new row needs to be inserted
|
||||
} else if (curRow != null && newRow == null) {
|
||||
return -1; // old row needs to be deleted
|
||||
}
|
||||
return comparator.compare(curRow, newRow);
|
||||
}
|
||||
|
||||
private boolean isSamePackageItemInfo(PackageItemInfo curInfo, PackageItemInfo newInfo) {
|
||||
return curInfo.iconBitmap.equals(newInfo.iconBitmap) &&
|
||||
!mIconCache.isDefaultIcon(curInfo.iconBitmap, curInfo.user);
|
||||
}
|
||||
}
|
||||
@@ -21,9 +21,10 @@ import android.support.v7.widget.RecyclerView.Adapter;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnLongClickListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.WidgetPreviewLoader;
|
||||
import com.android.launcher3.compat.AlphabeticIndexCompat;
|
||||
@@ -55,40 +56,67 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> {
|
||||
|
||||
private final WidgetPreviewLoader mWidgetPreviewLoader;
|
||||
private final LayoutInflater mLayoutInflater;
|
||||
|
||||
private final View.OnClickListener mIconClickListener;
|
||||
private final View.OnLongClickListener mIconLongClickListener;
|
||||
|
||||
private final ArrayList<WidgetListRowEntry> mEntries = new ArrayList<>();
|
||||
private final AlphabeticIndexCompat mIndexer;
|
||||
|
||||
private final OnClickListener mIconClickListener;
|
||||
private final OnLongClickListener mIconLongClickListener;
|
||||
private final int mIndent;
|
||||
private ArrayList<WidgetListRowEntry> mEntries = new ArrayList<>();
|
||||
private final WidgetsDiffReporter mDiffReporter;
|
||||
|
||||
public WidgetsListAdapter(View.OnClickListener iconClickListener,
|
||||
View.OnLongClickListener iconLongClickListener,
|
||||
Context context) {
|
||||
mLayoutInflater = LayoutInflater.from(context);
|
||||
mWidgetPreviewLoader = LauncherAppState.getInstance(context).getWidgetCache();
|
||||
|
||||
mIndexer = new AlphabeticIndexCompat(context);
|
||||
|
||||
public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
|
||||
WidgetPreviewLoader widgetPreviewLoader, AlphabeticIndexCompat indexCompat,
|
||||
OnClickListener iconClickListener, OnLongClickListener iconLongClickListener,
|
||||
WidgetsDiffReporter diffReporter) {
|
||||
mLayoutInflater = layoutInflater;
|
||||
mWidgetPreviewLoader = widgetPreviewLoader;
|
||||
mIndexer = indexCompat;
|
||||
mIconClickListener = iconClickListener;
|
||||
mIconLongClickListener = iconLongClickListener;
|
||||
mIndent = context.getResources().getDimensionPixelSize(R.dimen.widget_section_indent);
|
||||
mDiffReporter = diffReporter;
|
||||
}
|
||||
|
||||
public void setWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets) {
|
||||
mEntries.clear();
|
||||
WidgetItemComparator widgetComparator = new WidgetItemComparator();
|
||||
public void setNotifyListener() {
|
||||
mDiffReporter.setListener(new WidgetsDiffReporter.NotifyListener() {
|
||||
@Override
|
||||
public void notifyDataSetChanged() {
|
||||
WidgetsListAdapter.this.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyItemChanged(int index) {
|
||||
WidgetsListAdapter.this.notifyItemChanged(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyItemInserted(int index) {
|
||||
WidgetsListAdapter.this.notifyItemInserted(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyItemRemoved(int index) {
|
||||
WidgetsListAdapter.this.notifyItemRemoved(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the widget list.
|
||||
*/
|
||||
public void setWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets) {
|
||||
ArrayList<WidgetListRowEntry> tempEntries = new ArrayList<>();
|
||||
|
||||
WidgetItemComparator widgetComparator = new WidgetItemComparator();
|
||||
for (Map.Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : widgets.entrySet()) {
|
||||
WidgetListRowEntry row = new WidgetListRowEntry(entry.getKey(), entry.getValue());
|
||||
row.titleSectionName = mIndexer.computeSectionName(row.pkgItem.title);
|
||||
Collections.sort(row.widgets, widgetComparator);
|
||||
mEntries.add(row);
|
||||
tempEntries.add(row);
|
||||
}
|
||||
|
||||
Collections.sort(mEntries, new WidgetListRowEntryComparator());
|
||||
WidgetListRowEntryComparator rowComparator = new WidgetListRowEntryComparator();
|
||||
Collections.sort(tempEntries, rowComparator);
|
||||
mDiffReporter.process(mEntries, tempEntries, rowComparator);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -141,7 +141,7 @@ public class RequestPinItemTest extends AbstractLauncherUiTest {
|
||||
|
||||
private void runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher,
|
||||
Intent... commandIntents) throws Throwable {
|
||||
if (!Utilities.isAtLeastO()) {
|
||||
if (!Utilities.ATLEAST_OREO) {
|
||||
return;
|
||||
}
|
||||
lockRotation(true);
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open 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.widget;
|
||||
|
||||
import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
import com.android.launcher3.IconCache;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
||||
import com.android.launcher3.WidgetPreviewLoader;
|
||||
import com.android.launcher3.compat.AlphabeticIndexCompat;
|
||||
import com.android.launcher3.compat.AppWidgetManagerCompat;
|
||||
import com.android.launcher3.model.PackageItemInfo;
|
||||
import com.android.launcher3.model.WidgetItem;
|
||||
import com.android.launcher3.util.MultiHashMap;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class WidgetsListAdapterTest {
|
||||
|
||||
private final String TAG = "WidgetsListAdapterTest";
|
||||
|
||||
@Mock private LayoutInflater mMockLayoutInflater;
|
||||
@Mock private WidgetPreviewLoader mMockWidgetCache;
|
||||
@Mock private WidgetsDiffReporter.NotifyListener mListener;
|
||||
@Mock private IconCache mIconCache;
|
||||
|
||||
private WidgetsListAdapter mAdapter;
|
||||
private AlphabeticIndexCompat mIndexCompat;
|
||||
private InvariantDeviceProfile mTestProfile;
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = InstrumentationRegistry.getTargetContext();
|
||||
mTestProfile = new InvariantDeviceProfile();
|
||||
mTestProfile.numRows = 5;
|
||||
mTestProfile.numColumns = 5;
|
||||
mIndexCompat = new AlphabeticIndexCompat(mContext);
|
||||
WidgetsDiffReporter reporter = new WidgetsDiffReporter(mIconCache);
|
||||
reporter.setListener(mListener);
|
||||
mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, mMockWidgetCache,
|
||||
mIndexCompat, null, null, reporter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_notifyDataSetChanged() throws Exception {
|
||||
mAdapter.setWidgets(generateSampleMap(1));
|
||||
verify(mListener, times(1)).notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_notifyItemInserted() throws Exception {
|
||||
mAdapter.setWidgets(generateSampleMap(1));
|
||||
mAdapter.setWidgets(generateSampleMap(2));
|
||||
verify(mListener, times(1)).notifyDataSetChanged();
|
||||
verify(mListener, times(1)).notifyItemInserted(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_notifyItemRemoved() throws Exception {
|
||||
mAdapter.setWidgets(generateSampleMap(2));
|
||||
mAdapter.setWidgets(generateSampleMap(1));
|
||||
verify(mListener, times(1)).notifyDataSetChanged();
|
||||
verify(mListener, times(1)).notifyItemRemoved(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyItemChanged_PackageIconDiff() throws Exception {
|
||||
mAdapter.setWidgets(generateSampleMap(1));
|
||||
mAdapter.setWidgets(generateSampleMap(1));
|
||||
verify(mListener, times(1)).notifyDataSetChanged();
|
||||
verify(mListener, times(1)).notifyItemChanged(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyItemChanged_widgetItemInfoDiff() throws Exception {
|
||||
// TODO: same package name but item number changed
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyItemInsertedRemoved_hodgepodge() throws Exception {
|
||||
// TODO: insert and remove combined. curMap
|
||||
// newMap [A, C, D] [A, B, E]
|
||||
// B - C < 0, removed B from index 1 [A, E]
|
||||
// E - C > 0, C inserted to index 1 [A, C, E]
|
||||
// E - D > 0, D inserted to index 2 [A, C, D, E]
|
||||
// E - null = -1, E deleted from index 3 [A, C, D]
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to generate the sample widget model map that can be used for the tests
|
||||
* @param num the number of WidgetItem the map should contain
|
||||
* @return
|
||||
*/
|
||||
private MultiHashMap<PackageItemInfo, WidgetItem> generateSampleMap(int num) {
|
||||
MultiHashMap<PackageItemInfo, WidgetItem> newMap = new MultiHashMap();
|
||||
if (num <= 0) return newMap;
|
||||
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(mContext);
|
||||
for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders(null)) {
|
||||
WidgetItem wi = new WidgetItem(LauncherAppWidgetProviderInfo
|
||||
.fromProviderInfo(mContext, widgetInfo), pm, mTestProfile);
|
||||
|
||||
PackageItemInfo pInfo = new PackageItemInfo(wi.componentName.getPackageName());
|
||||
pInfo.title = pInfo.packageName;
|
||||
pInfo.user = wi.user;
|
||||
pInfo.iconBitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8);
|
||||
newMap.addToList(pInfo, wi);
|
||||
if (newMap.size() == num) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return newMap;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user