Merge "Merge Android 12 QPR1"

This commit is contained in:
Xin Li
2021-12-14 20:25:21 +00:00
committed by Gerrit Code Review
62 changed files with 1489 additions and 660 deletions
+1
View File
@@ -112,6 +112,7 @@ android_library {
"androidx.preference_preference",
"androidx.slice_slice-view",
"androidx.cardview_cardview",
"com.google.android.material_material",
"iconloader_base",
],
manifest: "AndroidManifest-common.xml",
+1 -1
View File
@@ -145,7 +145,7 @@
<activity
android:name="com.android.launcher3.settings.SettingsActivity"
android:label="@string/settings_button_text"
android:theme="@style/HomeSettingsTheme"
android:theme="@style/HomeSettings.Theme"
android:exported="true"
android:autoRemoveFromRecents="true">
<intent-filter>
-20
View File
@@ -22,11 +22,6 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.android.launcher3">
<permission
android:name="${packageName}.permission.HOTSEAT_EDU"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="signatureOrSystem" />
<uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" />
<uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
<uses-permission android:name="android.permission.VIBRATE"/>
@@ -41,7 +36,6 @@
<uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY"/>
<uses-permission android:name="android.permission.MONITOR_INPUT"/>
<uses-permission android:name="${packageName}.permission.HOTSEAT_EDU" />
<uses-permission android:name="android.permission.SYSTEM_APPLICATION_OVERLAY" />
<application android:backupAgent="com.android.launcher3.LauncherBackupAgent"
@@ -133,20 +127,6 @@
</intent-filter>
</activity>
<activity
android:name=".hybridhotseat.HotseatEduActivity"
android:theme="@android:style/Theme.NoDisplay"
android:noHistory="true"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
android:permission="${packageName}.permission.HOTSEAT_EDU"
android:exported="true">
<intent-filter>
<action android:name="com.android.launcher3.action.SHOW_HYBRID_HOTSEAT_EDU"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
+21
View File
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2021, 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:pathData="M 0, 0 C 0.1217, 0.0462, 0.15, 0.4686, 0.1667, 0.66 C 0.1834, 0.8878, 0.1667, 1, 1, 1"/>
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2021, 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:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
@@ -200,6 +200,17 @@ public abstract class BaseQuickstepLauncher extends Launcher
}
}
/**
* {@code LauncherOverlayCallbacks} scroll amount.
* Indicates transition progress to -1 screen.
* @param progress From 0 to 1.
*/
@Override
public void onScrollChanged(float progress) {
super.onScrollChanged(progress);
mDepthController.onOverlayScrollChanged(progress);
}
@Override
public void startIntentSenderForResult(IntentSender intent, int requestCode,
Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) {
@@ -32,7 +32,6 @@ import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SCRIM_FOR_APP_LAUNCH;
import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
@@ -72,6 +71,7 @@ import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
@@ -142,21 +142,11 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION =
"android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS";
private static final long APP_LAUNCH_DURATION = 450;
// Use a shorter duration for x or y translation to create a curve effect
private static final long APP_LAUNCH_CURVED_DURATION = 250;
private static final long APP_LAUNCH_DURATION = 500;
private static final long APP_LAUNCH_ALPHA_DURATION = 50;
private static final long APP_LAUNCH_ALPHA_START_DELAY = 25;
// We scale the durations for the downward app launch animations (minus the scale animation).
private static final float APP_LAUNCH_DOWN_DUR_SCALE_FACTOR = 0.8f;
private static final long APP_LAUNCH_DOWN_DURATION =
(long) (APP_LAUNCH_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
private static final long APP_LAUNCH_DOWN_CURVED_DURATION =
(long) (APP_LAUNCH_CURVED_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
private static final long APP_LAUNCH_ALPHA_DOWN_DURATION =
(long) (APP_LAUNCH_ALPHA_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
public static final int ANIMATION_NAV_FADE_IN_DURATION = 266;
public static final int ANIMATION_NAV_FADE_OUT_DURATION = 133;
public static final long ANIMATION_DELAY_NAV_FADE_IN =
@@ -166,9 +156,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
public static final Interpolator NAV_FADE_OUT_INTERPOLATOR =
new PathInterpolator(0.2f, 0f, 1f, 1f);
private static final long CROP_DURATION = 375;
private static final long RADIUS_DURATION = 375;
public static final int RECENTS_LAUNCH_DURATION = 336;
private static final int LAUNCHER_RESUME_START_DELAY = 100;
private static final int CLOSING_TRANSITION_DURATION_MS = 250;
@@ -222,6 +209,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
// Will never be larger than MAX_NUM_TASKS
private LinkedHashMap<Integer, Pair<Integer, Integer>> mTaskStartParams;
private final Interpolator mOpeningXInterpolator;
private final Interpolator mOpeningInterpolator;
public QuickstepTransitionManager(Context context) {
mLauncher = Launcher.cast(Launcher.getLauncher(context));
mDragLayer = mLauncher.getDragLayer();
@@ -248,6 +238,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(
mStartingWindowListener);
}
mOpeningXInterpolator = AnimationUtils.loadInterpolator(context, R.interpolator.app_open_x);
mOpeningInterpolator = AnimationUtils.loadInterpolator(context,
R.interpolator.three_point_fast_out_extra_slow_in);
}
@Override
@@ -651,27 +645,29 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
final float finalShadowRadius = appTargetsAreTranslucent ? 0 : mMaxShadowRadius;
MultiValueUpdateListener listener = new MultiValueUpdateListener() {
FloatProp mDx = new FloatProp(0, prop.dX, 0, prop.xDuration, AGGRESSIVE_EASE);
FloatProp mDy = new FloatProp(0, prop.dY, 0, prop.yDuration, AGGRESSIVE_EASE);
FloatProp mDx = new FloatProp(0, prop.dX, 0, APP_LAUNCH_DURATION,
mOpeningXInterpolator);
FloatProp mDy = new FloatProp(0, prop.dY, 0, APP_LAUNCH_DURATION,
mOpeningInterpolator);
FloatProp mIconScaleToFitScreen = new FloatProp(prop.initialAppIconScale,
prop.finalAppIconScale, 0, APP_LAUNCH_DURATION, EXAGGERATED_EASE);
prop.finalAppIconScale, 0, APP_LAUNCH_DURATION, mOpeningInterpolator);
FloatProp mIconAlpha = new FloatProp(prop.iconAlphaStart, 0f,
APP_LAUNCH_ALPHA_START_DELAY, prop.alphaDuration, LINEAR);
APP_LAUNCH_ALPHA_START_DELAY, APP_LAUNCH_ALPHA_DURATION, LINEAR);
FloatProp mWindowRadius = new FloatProp(initialWindowRadius, finalWindowRadius, 0,
RADIUS_DURATION, EXAGGERATED_EASE);
APP_LAUNCH_DURATION, mOpeningInterpolator);
FloatProp mShadowRadius = new FloatProp(0, finalShadowRadius, 0,
APP_LAUNCH_DURATION, EXAGGERATED_EASE);
APP_LAUNCH_DURATION, mOpeningInterpolator);
FloatProp mCropRectCenterX = new FloatProp(prop.cropCenterXStart, prop.cropCenterXEnd,
0, CROP_DURATION, EXAGGERATED_EASE);
0, APP_LAUNCH_DURATION, mOpeningInterpolator);
FloatProp mCropRectCenterY = new FloatProp(prop.cropCenterYStart, prop.cropCenterYEnd,
0, CROP_DURATION, EXAGGERATED_EASE);
0, APP_LAUNCH_DURATION, mOpeningInterpolator);
FloatProp mCropRectWidth = new FloatProp(prop.cropWidthStart, prop.cropWidthEnd, 0,
CROP_DURATION, EXAGGERATED_EASE);
APP_LAUNCH_DURATION, mOpeningInterpolator);
FloatProp mCropRectHeight = new FloatProp(prop.cropHeightStart, prop.cropHeightEnd, 0,
CROP_DURATION, EXAGGERATED_EASE);
APP_LAUNCH_DURATION, mOpeningInterpolator);
FloatProp mNavFadeOut = new FloatProp(1f, 0f, 0, ANIMATION_NAV_FADE_OUT_DURATION,
NAV_FADE_OUT_INTERPOLATOR);
@@ -873,22 +869,23 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
WIDGET_CROSSFADE_DURATION_MILLIS / 2 /* delay */,
WIDGET_CROSSFADE_DURATION_MILLIS / 2 /* duration */, LINEAR);
final FloatProp mWindowRadius = new FloatProp(initialWindowRadius, finalWindowRadius,
0 /* start */, RADIUS_DURATION, LINEAR);
final FloatProp mCornerRadiusProgress = new FloatProp(0, 1, 0, RADIUS_DURATION, LINEAR);
0 /* start */, APP_LAUNCH_DURATION, mOpeningInterpolator);
final FloatProp mCornerRadiusProgress = new FloatProp(0, 1, 0, APP_LAUNCH_DURATION,
mOpeningInterpolator);
// Window & widget background positioning bounds
final FloatProp mDx = new FloatProp(widgetBackgroundBounds.centerX(),
windowTargetBounds.centerX(), 0 /* delay */, APP_LAUNCH_CURVED_DURATION,
EXAGGERATED_EASE);
windowTargetBounds.centerX(), 0 /* delay */, APP_LAUNCH_DURATION,
mOpeningXInterpolator);
final FloatProp mDy = new FloatProp(widgetBackgroundBounds.centerY(),
windowTargetBounds.centerY(), 0 /* delay */, APP_LAUNCH_DURATION,
EXAGGERATED_EASE);
mOpeningInterpolator);
final FloatProp mWidth = new FloatProp(widgetBackgroundBounds.width(),
windowTargetBounds.width(), 0 /* delay */, APP_LAUNCH_DURATION,
EXAGGERATED_EASE);
mOpeningInterpolator);
final FloatProp mHeight = new FloatProp(widgetBackgroundBounds.height(),
windowTargetBounds.height(), 0 /* delay */, APP_LAUNCH_DURATION,
EXAGGERATED_EASE);
mOpeningInterpolator);
final FloatProp mNavFadeOut = new FloatProp(1f, 0f, 0, ANIMATION_NAV_FADE_OUT_DURATION,
NAV_FADE_OUT_INTERPOLATOR);
@@ -1424,10 +1421,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
public final float dX;
public final float dY;
public final long xDuration;
public final long yDuration;
public final long alphaDuration;
public final float initialAppIconScale;
public final float finalAppIconScale;
@@ -1459,14 +1452,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
dX = centerX - launcherIconBounds.centerX();
dY = centerY - launcherIconBounds.centerY();
boolean useUpwardAnimation = launcherIconBounds.top > centerY
|| Math.abs(dY) < dp.cellHeightPx;
xDuration = useUpwardAnimation ? APP_LAUNCH_CURVED_DURATION
: APP_LAUNCH_DOWN_DURATION;
yDuration = useUpwardAnimation ? APP_LAUNCH_DURATION
: APP_LAUNCH_DOWN_CURVED_DURATION;
alphaDuration = useUpwardAnimation ? APP_LAUNCH_ALPHA_DURATION
: APP_LAUNCH_ALPHA_DOWN_DURATION;
iconAlphaStart = hasSplashScreen && !hasDifferentAppIcon ? 0 : 1f;
final int windowIconSize = ResourceUtils.getDimenByName("starting_surface_icon_size",
@@ -1,60 +0,0 @@
/*
* Copyright (C) 2020 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.hybridhotseat;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.Launcher;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.ActivityTracker;
/**
* Proxy activity to return user to home screen and show halfsheet education
*/
public class HotseatEduActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent homeIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.setPackage(getPackageName())
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Launcher.ACTIVITY_TRACKER.registerCallback(new HotseatActivityTracker());
startActivity(homeIntent);
finish();
}
static class HotseatActivityTracker<T extends QuickstepLauncher> implements
ActivityTracker.SchedulerCallback {
@Override
public boolean init(BaseActivity activity, boolean alreadyOnHome) {
QuickstepLauncher launcher = (QuickstepLauncher) activity;
if (launcher != null) {
launcher.getHotseatPredictionController().showEdu();
}
return false;
}
}
}
@@ -26,6 +26,7 @@ import android.animation.ObjectAnimator;
import android.os.IBinder;
import android.os.SystemProperties;
import android.util.FloatProperty;
import android.view.AttachedSurfaceControl;
import android.view.CrossWindowBlurListeners;
import android.view.SurfaceControl;
import android.view.View;
@@ -108,6 +109,13 @@ public class DepthController implements StateHandler<LauncherState>,
}
};
private final Runnable mOpaquenessListener = new Runnable() {
@Override
public void run() {
dispatchTransactionSurface(mDepth);
}
};
private final Launcher mLauncher;
/**
* Blur radius when completely zoomed out, in pixels.
@@ -116,6 +124,10 @@ public class DepthController implements StateHandler<LauncherState>,
private boolean mCrossWindowBlursEnabled;
private WallpaperManagerCompat mWallpaperManager;
private SurfaceControl mSurface;
/**
* How visible the -1 overlay is, from 0 to 1.
*/
private float mOverlayScrollProgress;
/**
* Ratio from 0 to 1, where 0 is fully zoomed out, and 1 is zoomed in.
* @see android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)
@@ -150,23 +162,28 @@ public class DepthController implements StateHandler<LauncherState>,
if (windowToken != null) {
mWallpaperManager.setWallpaperZoomOut(windowToken, mDepth);
}
CrossWindowBlurListeners.getInstance().addListener(mLauncher.getMainExecutor(),
mCrossWindowBlurListener);
onAttached();
}
@Override
public void onViewDetachedFromWindow(View view) {
CrossWindowBlurListeners.getInstance().removeListener(mCrossWindowBlurListener);
mLauncher.getScrimView().removeOpaquenessListener(mOpaquenessListener);
}
};
mLauncher.getRootView().addOnAttachStateChangeListener(mOnAttachListener);
if (mLauncher.getRootView().isAttachedToWindow()) {
CrossWindowBlurListeners.getInstance().addListener(mLauncher.getMainExecutor(),
mCrossWindowBlurListener);
onAttached();
}
}
}
private void onAttached() {
CrossWindowBlurListeners.getInstance().addListener(mLauncher.getMainExecutor(),
mCrossWindowBlurListener);
mLauncher.getScrimView().addOpaquenessListener(mOpaquenessListener);
}
/**
* Sets if the underlying activity is started or not
*/
@@ -251,12 +268,24 @@ public class DepthController implements StateHandler<LauncherState>,
}
}
public void onOverlayScrollChanged(float progress) {
// Round out the progress to dedupe frequent, non-perceptable updates
int progressI = (int) (progress * 256);
float progressF = Utilities.boundToRange(progressI / 256f, 0f, 1f);
if (Float.compare(mOverlayScrollProgress, progressF) == 0) {
return;
}
mOverlayScrollProgress = progressF;
dispatchTransactionSurface(mDepth);
}
private boolean dispatchTransactionSurface(float depth) {
boolean supportsBlur = BlurUtils.supportsBlursOnWindows();
if (supportsBlur && (mSurface == null || !mSurface.isValid())) {
return false;
}
ensureDependencies();
depth = Math.max(depth, mOverlayScrollProgress);
IBinder windowToken = mLauncher.getRootView().getWindowToken();
if (windowToken != null) {
mWallpaperManager.setWallpaperZoomOut(windowToken, depth);
@@ -270,10 +299,15 @@ public class DepthController implements StateHandler<LauncherState>,
int blur = opaque || isOverview || !mCrossWindowBlursEnabled
|| mBlurDisabledForAppLaunch ? 0 : (int) (depth * mMaxBlurRadius);
new SurfaceControl.Transaction()
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()
.setBackgroundBlurRadius(mSurface, blur)
.setOpaque(mSurface, opaque)
.apply();
.setOpaque(mSurface, opaque);
AttachedSurfaceControl rootSurfaceControl =
mLauncher.getRootView().getRootSurfaceControl();
if (rootSurfaceControl != null) {
rootSurfaceControl.applyTransactionOnDraw(transaction);
}
}
return true;
}
@@ -71,7 +71,7 @@ class QuickstepInteractionHandler implements RemoteViews.InteractionHandler {
}
}
activityOptions.options.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activityOptions.options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
activityOptions.options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_EMPTY);
Object itemInfo = hostView.getTag();
if (itemInfo instanceof ItemInfo) {
mLauncher.addLaunchCookie((ItemInfo) itemInfo, activityOptions.options);
@@ -173,7 +173,9 @@ public class FallbackSwipeHandler extends
@Override
protected void notifyGestureAnimationStartToRecents() {
if (mRunningOverHome) {
mRecentsView.onGestureAnimationStartOnHome(mGestureState.getRunningTask());
if (SysUINavigationMode.getMode(mContext).hasGestures) {
mRecentsView.onGestureAnimationStartOnHome(mGestureState.getRunningTask());
}
} else {
super.notifyGestureAnimationStartToRecents();
}
@@ -331,12 +331,14 @@ public class TouchInteractionService extends Service implements PluginListener<O
mDeviceState = new RecentsAnimationDeviceState(this, true);
mDisplayManager = getSystemService(DisplayManager.class);
mTaskbarManager = new TaskbarManager(this);
mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
mDeviceState.addOneHandedModeChangedCallback(this::onOneHandedModeOverlayChanged);
// Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
mDeviceState.runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
mDeviceState.addOneHandedModeChangedCallback(this::onOneHandedModeOverlayChanged);
ProtoTracer.INSTANCE.get(this).add(this);
sConnected = true;
}
@@ -1729,10 +1729,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
int runningIndex = getCurrentPage();
AnimatorSet as = new AnimatorSet();
for (int i = 0; i < getTaskViewCount(); i++) {
if (runningIndex == i) {
View taskView = getTaskViewAt(i);
if (runningIndex == i && taskView.getAlpha() != 0) {
continue;
}
View taskView = getTaskViewAt(i);
as.play(ObjectAnimator.ofFloat(taskView, View.ALPHA, fadeInChildren ? 0 : 1));
}
return as;
@@ -1768,8 +1768,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
// When switching to tasks in quick switch, ensures the snapped page's scroll maintain
// invariant between quick switch and overview, to ensure a smooth animation transition.
updateGridProperties();
} else if (endTarget == GestureState.GestureEndTarget.RECENTS) {
setEnableFreeScroll(true);
}
}
@@ -1779,7 +1777,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
public void onGestureAnimationEnd() {
mGestureActive = false;
if (mOrientationState.setGestureActive(false)) {
updateOrientationHandler();
updateOrientationHandler(/* forceRecreateDragLayerControllers = */ false);
}
setEnableFreeScroll(true);
@@ -2910,8 +2908,11 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
boolean isStartShift;
if (midpointIndex > -1) {
// When there is a midpoint reference task, adjacent tasks have less distance to travel
// to reach offscreen. Offset the task position to the task's starting point.
int midpointScroll = getScrollForPage(midpointIndex);
// to reach offscreen. Offset the task position to the task's starting point, and offset
// by current page's scroll diff.
int midpointScroll = getScrollForPage(midpointIndex)
+ mOrientationHandler.getPrimaryScroll(this) - getScrollForPage(mCurrentPage);
getPersistentChildPosition(midpointIndex, midpointScroll, taskPosition);
float midpointStart = mOrientationHandler.getStart(taskPosition);
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Disabled status of thumb -->
<item android:state_enabled="false"
android:color="@color/home_settings_thumb_off_color" />
<!-- Toggle off status of thumb -->
<item android:state_checked="false"
android:color="@color/home_settings_thumb_off_color" />
<!-- Enabled or toggle on status of thumb -->
<item android:color="@color/home_settings_state_on_color" />
</selector>
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Disabled status of thumb -->
<item android:state_enabled="false"
android:color="@color/home_settings_track_off_color"
android:alpha="?android:attr/disabledAlpha" />
<!-- Toggle off status of thumb -->
<item android:state_checked="false"
android:color="@color/home_settings_track_off_color" />
<!-- Enabled or toggle on status of thumb -->
<item android:color="@color/home_settings_track_on_color" />
</selector>
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 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.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:top="4dp"
android:left="4dp"
android:right="4dp"
android:bottom="4dp">
<shape android:shape="oval" >
<size android:height="20dp" android:width="20dp" />
<solid android:color="@color/home_settings_switch_thumb_color" />
</shape>
</item>
</layer-list>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 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.
-->
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
android:width="52dp"
android:height="28dp">
<solid android:color="@color/home_settings_switch_track_color" />
<corners android:radius="35dp" />
</shape>
+3 -10
View File
@@ -14,18 +14,11 @@
~ limitations under the License.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/rounded_action_button"
android:left="@dimen/padded_rounded_button_padding"
android:top="@dimen/padded_rounded_button_padding"
android:right="@dimen/padded_rounded_button_padding"
android:bottom="@dimen/padded_rounded_button_padding">
<shape android:shape="rectangle">
<corners android:radius="@dimen/rounded_button_radius" />
<stroke android:width="1dp"
android:color="?androidprv:attr/colorAccentPrimaryVariant" />
</shape>
</item>
android:bottom="@dimen/padded_rounded_button_padding" />
</layer-list>
+3 -1
View File
@@ -19,7 +19,9 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/rounded_button_radius" />
<stroke android:width="1dp" android:color="@color/all_apps_tab_background_selected" />
<stroke
android:width="1dp"
android:color="?androidprv:attr/colorAccentPrimaryVariant" />
<padding
android:left="@dimen/rounded_button_padding"
android:right="@dimen/rounded_button_padding" />
+69
View File
@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/content_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/colorPrimary"
android:fitsSystemWindows="true"
android:outlineAmbientShadowColor="@android:color/transparent"
android:outlineSpotShadowColor="@android:color/transparent"
android:theme="@style/HomeSettings.CollapsingToolbar">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="226dp"
android:clipToPadding="false"
app:collapsedTitleTextAppearance="@style/HomeSettings.CollapsedToolbarTitle"
app:contentScrim="@color/home_settings_header_collapsed"
app:expandedTitleMarginEnd="24dp"
app:expandedTitleMarginStart="24dp"
app:expandedTitleTextAppearance="@style/HomeSettings.ExpandedToolbarTitle"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
app:maxLines="3"
app:scrimAnimationDuration="50"
app:scrimVisibleHeightTrigger="174dp"
app:statusBarScrim="@null"
app:titleCollapseMode="fade"
app:toolbarId="@id/action_bar">
<Toolbar
android:id="@+id/action_bar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="?android:attr/actionBarTheme"
android:transitionName="shared_element_view"
app:layout_collapseMode="pin" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
+2 -4
View File
@@ -27,15 +27,13 @@
android:gravity="center"
android:padding="16dp"
android:background="@drawable/arrow_toast_rounded_background"
android:elevation="2dp"
android:outlineProvider="none"
android:elevation="@dimen/arrow_toast_elevation"
android:textColor="@color/arrow_tip_view_content"
android:textSize="14sp"/>
<View
android:id="@+id/arrow"
android:elevation="2dp"
android:outlineProvider="none"
android:elevation="@dimen/arrow_toast_elevation"
android:layout_width="@dimen/arrow_toast_arrow_width"
android:layout_height="10dp"/>
</merge>
+6 -6
View File
@@ -14,10 +14,11 @@
limitations under the License.
-->
<merge
<com.android.launcher3.notification.NotificationMainView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- header -->
<FrameLayout
@@ -49,7 +50,7 @@
</FrameLayout>
<!-- Main view -->
<com.android.launcher3.notification.NotificationMainView
<FrameLayout
android:id="@+id/main_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -59,7 +60,6 @@
android:id="@+id/text_and_background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/popupColorPrimary"
android:gravity="center_vertical"
android:orientation="vertical"
android:paddingTop="@dimen/notification_padding"
@@ -95,5 +95,5 @@
android:layout_marginTop="@dimen/notification_padding"
android:layout_marginStart="@dimen/notification_icon_padding" />
</com.android.launcher3.notification.NotificationMainView>
</merge>
</FrameLayout>
</com.android.launcher3.notification.NotificationMainView>
+2 -5
View File
@@ -31,12 +31,9 @@
android:elevation="@dimen/deep_shortcuts_elevation"
android:orientation="vertical"/>
<LinearLayout
<com.android.launcher3.notification.NotificationContainer
android:id="@+id/notification_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:background="?attr/popupColorPrimary"
android:elevation="@dimen/deep_shortcuts_elevation"
android:orientation="vertical"/>
android:visibility="gone"/>
</com.android.launcher3.popup.PopupContainerWithArrow>
+2 -1
View File
@@ -18,7 +18,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="@dimen/bg_popup_item_width"
android:layout_height="@dimen/bg_popup_item_height"
android:layout_height="wrap_content"
android:minHeight="@dimen/bg_popup_item_height"
android:elevation="@dimen/deep_shortcuts_elevation"
android:background="@drawable/middle_item_primary"
android:theme="@style/PopupItem" >
+47
View File
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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.
-->
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto" >
<com.android.launcher3.BubbleTextView
style="@style/BaseIconUnBounded"
android:id="@+id/bubble_text"
android:background="?android:attr/selectableItemBackground"
android:gravity="start|center_vertical"
android:paddingTop="@dimen/bg_popup_item_vertical_padding"
android:paddingBottom="@dimen/bg_popup_item_vertical_padding"
android:minHeight="@dimen/bg_popup_item_height"
android:textAlignment="viewStart"
android:paddingStart="@dimen/deep_shortcuts_text_padding_start"
android:paddingEnd="@dimen/popup_padding_end"
android:textSize="14sp"
android:minLines="1"
android:maxLines="2"
android:ellipsize="end"
android:textColor="?android:attr/textColorPrimary"
launcher:iconDisplay="shortcut_popup"
launcher:layoutHorizontal="true"
android:focusable="false" />
<View
android:id="@+id/icon"
android:layout_width="@dimen/system_shortcut_icon_size"
android:layout_height="@dimen/system_shortcut_icon_size"
android:layout_marginStart="@dimen/system_shortcut_margin_start"
android:layout_gravity="start|center_vertical"
android:backgroundTint="?android:attr/textColorPrimary"/>
</merge>
+1 -1
View File
@@ -28,7 +28,7 @@
android:layout_marginTop="40dp"
android:text="@string/work_apps_paused_title"
android:textAlignment="center"
android:textSize="22sp" />
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
+27
View File
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?><!--
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-->
<resources>
<color name="home_settings_header_accent">@android:color/system_accent1_100</color>
<color name="home_settings_header_collapsed">@android:color/system_neutral1_700</color>
<color name="home_settings_header_expanded">@android:color/system_neutral1_900</color>
<color name="home_settings_thumb_off_color">@android:color/system_neutral2_300</color>
<color name="home_settings_track_on_color">@android:color/system_accent2_700</color>
<color name="home_settings_track_off_color">@android:color/system_neutral1_700</color>
</resources>
+10
View File
@@ -42,6 +42,16 @@
<color name="folder_dot_color">@android:color/system_accent2_50</color>
<color name="home_settings_header_accent">@android:color/system_accent1_600</color>
<color name="home_settings_header_collapsed">@android:color/system_neutral1_100</color>
<color name="home_settings_header_expanded">@android:color/system_neutral1_50</color>
<color name="home_settings_state_on_color">@android:color/system_accent1_100</color>
<color name="home_settings_state_off_color">@android:color/system_accent2_100</color>
<color name="home_settings_thumb_off_color">@android:color/system_neutral2_100</color>
<color name="home_settings_track_on_color">@android:color/system_accent1_600</color>
<color name="home_settings_track_off_color">@android:color/system_neutral2_600</color>
<color name="workspace_accent_color_light">@android:color/system_accent1_100</color>
<color name="workspace_accent_color_dark">@android:color/system_accent2_600</color>
</resources>
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 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="home_settings_icon_space_reserved">false</bool>
<bool name="home_settings_allow_divider">false</bool>
</resources>
+95
View File
@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-->
<resources>
<style name="HomeSettings.Theme" parent="@android:style/Theme.DeviceDefault.Settings">
<item name="android:listPreferredItemPaddingEnd">16dp</item>
<item name="android:listPreferredItemPaddingStart">24dp</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:switchStyle">@style/HomeSettings.SwitchStyle</item>
<item name="android:textAppearanceListItem">@style/HomeSettings.PreferenceTitle</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<item name="preferenceTheme">@style/HomeSettings.PreferenceTheme</item>
</style>
<style name="HomeSettings.PreferenceTheme" parent="@style/PreferenceThemeOverlay">
<item name="preferenceCategoryStyle">@style/HomeSettings.CategoryStyle</item>
<item name="preferenceCategoryTitleTextAppearance">@style/HomeSettings.CategoryTitle</item>
<item name="preferenceFragmentCompatStyle">@style/HomeSettings.FragmentCompatStyle</item>
<item name="preferenceScreenStyle">@style/HomeSettings.PreferenceScreenStyle</item>
<item name="preferenceStyle">@style/HomeSettings.PreferenceStyle</item>
<item name="switchPreferenceStyle">@style/HomeSettings.SwitchPreferenceStyle</item>
</style>
<style name="HomeSettings.CategoryStyle" parent="@style/Preference.Category.Material">
<item name="allowDividerAbove">@bool/home_settings_allow_divider</item>
<item name="allowDividerBelow">@bool/home_settings_allow_divider</item>
<item name="iconSpaceReserved">@bool/home_settings_icon_space_reserved</item>
</style>
<style name="HomeSettings.PreferenceStyle" parent="@style/Preference.Material">
<item name="iconSpaceReserved">@bool/home_settings_icon_space_reserved</item>
</style>
<style name="HomeSettings.PreferenceScreenStyle"
parent="@style/Preference.PreferenceScreen.Material">
<item name="iconSpaceReserved">@bool/home_settings_icon_space_reserved</item>
</style>
<style name="HomeSettings.SwitchPreferenceStyle"
parent="@style/Preference.SwitchPreference.Material">
<item name="iconSpaceReserved">@bool/home_settings_icon_space_reserved</item>
</style>
<style name="HomeSettings.SwitchStyle"
parent="@android:style/Widget.Material.CompoundButton.Switch">
<item name="android:switchMinWidth">52dp</item>
<item name="android:thumb">@drawable/home_settings_switch_thumb</item>
<item name="android:track">@drawable/home_settings_switch_track</item>
</style>
<style name="HomeSettings.PreferenceTitle"
parent="@android:style/TextAppearance.Material.Subhead">
<item name="android:fontFamily">google-sans</item>
<item name="android:textSize">20sp</item>
</style>
<style name="HomeSettings.CategoryTitle" parent="@android:style/TextAppearance.Material.Body2">
<item name="android:fontFamily">google-sans-text-medium</item>
</style>
<style name="HomeSettings.CollapsingToolbar" parent="@style/Theme.MaterialComponents.DayNight">
<item name="colorAccent">@color/home_settings_header_accent</item>
<item name="colorPrimary">@color/home_settings_header_expanded</item>
<item name="elevationOverlayColor">?attr/colorPrimary</item>
<item name="elevationOverlayEnabled">true</item>
</style>
<style name="HomeSettings.CollapsedToolbarTitle"
parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
<item name="android:fontFamily">google-sans</item>
</style>
<style name="HomeSettings.ExpandedToolbarTitle" parent="HomeSettings.CollapsedToolbarTitle">
<item name="android:textSize">36sp</item>
</style>
</resources>
+3
View File
@@ -34,6 +34,9 @@
<!-- View tag key used to determine if we should fade in the child views.. -->
<string name="popup_container_iterate_children" translatable="false">popup_container_iterate_children</string>
<!-- config used to determine if header protection is supported in AllApps -->
<bool name="config_header_protection_supported">false</bool>
<!-- Workspace -->
<!-- The duration (in ms) of the fade animation on the object outlines, used when
we are dragging objects around on the home screen. -->
+6
View File
@@ -109,6 +109,7 @@
<dimen name="all_apps_tip_bottom_margin">8dp</dimen>
<!-- The size of corner radius of the arrow in the arrow toast. -->
<dimen name="arrow_toast_corner_radius">2dp</dimen>
<dimen name="arrow_toast_elevation">2dp</dimen>
<dimen name="arrow_toast_arrow_width">10dp</dimen>
<!-- Search bar in All Apps -->
@@ -196,6 +197,7 @@
<dimen name="drop_target_text_size">16sp</dimen>
<dimen name="drop_target_shadow_elevation">2dp</dimen>
<dimen name="drop_target_bar_margin_horizontal">4dp</dimen>
<dimen name="drop_target_button_drawable_padding">8dp</dimen>
<!-- the distance an icon must be dragged before button drop targets accept it -->
<dimen name="drag_distanceThreshold">30dp</dimen>
@@ -241,6 +243,7 @@
<dimen name="bg_popup_padding">2dp</dimen>
<dimen name="bg_popup_item_width">216dp</dimen>
<dimen name="bg_popup_item_height">56dp</dimen>
<dimen name="bg_popup_item_vertical_padding">12dp</dimen>
<dimen name="pre_drag_view_scale">6dp</dimen>
<!-- an icon with shortcuts must be dragged this far before the container is removed. -->
<dimen name="deep_shortcuts_start_drag_threshold">16dp</dimen>
@@ -271,6 +274,8 @@
<!-- Notifications -->
<dimen name="bg_round_rect_radius">8dp</dimen>
<dimen name="notification_max_trans">8dp</dimen>
<dimen name="notification_space">8dp</dimen>
<dimen name="notification_padding">16dp</dimen>
<dimen name="notification_padding_top">18dp</dimen>
<dimen name="notification_header_text_size">14sp</dimen>
@@ -331,4 +336,5 @@
<dimen name="search_row_icon_size">48dp</dimen>
<dimen name="search_row_small_icon_size">32dp</dimen>
<dimen name="padded_rounded_button_padding">8dp</dimen>
</resources>
+5 -1
View File
@@ -153,6 +153,7 @@
<!-- All applications label -->
<string name="all_apps_button_label">Apps list</string>
<string name="all_apps_search_results">Search results</string>
<string name="all_apps_button_personal_label">Personal apps list</string>
<string name="all_apps_button_work_label">Work apps list</string>
@@ -199,8 +200,11 @@
<!-- Error text that lets a user know that the widget can't load. -->
<string name="gadget_error_text">Can\'t load widget</string>
<!-- Button text. This button lets a user change a widget's settings. -->
<string name="gadget_setup_text">Widget settings</string>
<!-- Instructional text to encourage a user to finish setting up the widget. -->
<string name="gadget_setup_text">Tap to finish setup</string>
<string name="gadget_complete_setup_text">Tap to finish setup</string>
<!-- Text to inform the user that they can't uninstall a system application -->
<string name="uninstall_system_app_text">This is a system app and can\'t be uninstalled.</string>
+6 -6
View File
@@ -147,18 +147,18 @@
<style name="AppTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark.DarkMainColor" />
<style name="AppTheme.Dark.DarkText" parent="@style/LauncherTheme.Dark.DarkText" />
<style name="HomeSettingsTheme" parent="@android:style/Theme.DeviceDefault.Settings">
<style name="HomeSettings.Theme" parent="@android:style/Theme.DeviceDefault.Settings">
<item name="android:navigationBarColor">?android:colorPrimaryDark</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<item name="preferenceTheme">@style/HomeSettingsPreferenceTheme</item>
<item name="preferenceTheme">@style/HomeSettings.PreferenceTheme</item>
</style>
<style name="HomeSettingsPreferenceTheme" parent="@style/PreferenceThemeOverlay.v14.Material">
<item name="preferenceFragmentCompatStyle">@style/HomeSettingsFragmentCompatStyle</item>
<style name="HomeSettings.PreferenceTheme" parent="@style/PreferenceThemeOverlay.v14.Material">
<item name="preferenceFragmentCompatStyle">@style/HomeSettings.FragmentCompatStyle</item>
</style>
<style name="HomeSettingsFragmentCompatStyle" parent="@style/PreferenceFragment.Material">
<style name="HomeSettings.FragmentCompatStyle" parent="@style/PreferenceFragment.Material">
<item name="android:layout">@layout/home_settings</item>
</style>
@@ -259,7 +259,7 @@
<!-- Drop targets -->
<style name="DropTargetButtonBase" parent="@android:style/TextAppearance.DeviceDefault">
<item name="android:drawablePadding">8dp</item>
<item name="android:drawablePadding">@dimen/drop_target_button_drawable_padding</item>
<item name="android:padding">14dp</item>
<item name="android:textColor">@color/drop_target_text</item>
<item name="android:textSize">@dimen/drop_target_text_size</item>
@@ -66,6 +66,8 @@ public abstract class ButtonDropTarget extends TextView
private final int mDragDistanceThreshold;
/** The size of the drawable shown in the drop target. */
private final int mDrawableSize;
/** The padding, in pixels, between the text and drawable. */
private final int mDrawablePadding;
protected CharSequence mText;
protected Drawable mDrawable;
@@ -85,6 +87,8 @@ public abstract class ButtonDropTarget extends TextView
Resources resources = getResources();
mDragDistanceThreshold = resources.getDimensionPixelSize(R.dimen.drag_distanceThreshold);
mDrawableSize = resources.getDimensionPixelSize(R.dimen.drop_target_text_size);
mDrawablePadding = resources.getDimensionPixelSize(
R.dimen.drop_target_button_drawable_padding);
}
@Override
@@ -303,6 +307,8 @@ public abstract class ButtonDropTarget extends TextView
mTextVisible = isVisible;
setText(newText);
setCompoundDrawablesRelative(mDrawable, null, null, null);
int drawablePadding = mTextVisible ? mDrawablePadding : 0;
setCompoundDrawablePadding(drawablePadding);
}
}
@@ -99,8 +99,18 @@ public class ExtendedEditText extends EditText {
}
}
// inherited class can override to change the appearance of the edit text.
public void show() {}
/**
* Sets whether EditText background should be visible
* @param maxAlpha defines the maximum alpha the background should animates to
*/
public void setBackgroundVisibility(boolean visible, float maxAlpha) {}
/**
* Returns whether a visible background is set on EditText
*/
public boolean getBackgroundVisibility() {
return getBackground() != null;
}
public void showKeyboard() {
mShowImeAfterFirstLayout = !showSoftInput();
+12 -8
View File
@@ -218,7 +218,8 @@ import java.util.stream.Stream;
* Default launcher application.
*/
public class Launcher extends StatefulActivity<LauncherState> implements LauncherExterns,
Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin> {
Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin>,
LauncherOverlayCallbacks {
public static final String TAG = "Launcher";
public static final ActivityTracker<Launcher> ACTIVITY_TRACKER = new ActivityTracker<>();
@@ -619,7 +620,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
@Override
public void setLauncherOverlay(LauncherOverlay overlay) {
if (overlay != null) {
overlay.setOverlayCallbacks(new LauncherOverlayCallbacksImpl());
overlay.setOverlayCallbacks(this);
}
mWorkspace.setLauncherOverlay(overlay);
}
@@ -1123,12 +1124,15 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
mAppWidgetHost.setActivityResumed(false);
}
class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
public void onScrollChanged(float progress) {
if (mWorkspace != null) {
mWorkspace.onOverlayScrollChanged(progress);
}
/**
* {@code LauncherOverlayCallbacks} scroll amount.
* Indicates transition progress to -1 screen.
* @param progress From 0 to 1.
*/
@Override
public void onScrollChanged(float progress) {
if (mWorkspace != null) {
mWorkspace.onOverlayScrollChanged(progress);
}
}
@@ -31,7 +31,7 @@ import android.view.ViewGroup;
import com.android.launcher3.CellLayout.ContainerType;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.NavigableAppWidgetHostView;
public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon.FolderIconParent {
static final String TAG = "ShortcutAndWidgetContainer";
@@ -104,9 +104,9 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon.
public void setupLp(View child) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
if (child instanceof LauncherAppWidgetHostView) {
if (child instanceof NavigableAppWidgetHostView) {
DeviceProfile profile = mActivity.getDeviceProfile();
((LauncherAppWidgetHostView) child).getWidgetInset(profile, mTempRect);
((NavigableAppWidgetHostView) child).getWidgetInset(profile, mTempRect);
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
profile.appWidgetScale.x, profile.appWidgetScale.y, mBorderSpacing, mTempRect);
} else {
@@ -129,8 +129,8 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon.
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
final DeviceProfile dp = mActivity.getDeviceProfile();
if (child instanceof LauncherAppWidgetHostView) {
((LauncherAppWidgetHostView) child).getWidgetInset(dp, mTempRect);
if (child instanceof NavigableAppWidgetHostView) {
((NavigableAppWidgetHostView) child).getWidgetInset(dp, mTempRect);
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
dp.appWidgetScale.x, dp.appWidgetScale.y, mBorderSpacing, mTempRect);
} else {
@@ -178,16 +178,16 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon.
*/
public void layoutChild(View child) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
if (child instanceof LauncherAppWidgetHostView) {
LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) child;
if (child instanceof NavigableAppWidgetHostView) {
NavigableAppWidgetHostView nahv = (NavigableAppWidgetHostView) child;
// Scale and center the widget to fit within its cells.
DeviceProfile profile = mActivity.getDeviceProfile();
float scaleX = profile.appWidgetScale.x;
float scaleY = profile.appWidgetScale.y;
lahv.setScaleToFit(Math.min(scaleX, scaleY));
lahv.setTranslationForCentering(-(lp.width - (lp.width * scaleX)) / 2.0f,
nahv.setScaleToFit(Math.min(scaleX, scaleY));
nahv.setTranslationForCentering(-(lp.width - (lp.width * scaleX)) / 2.0f,
-(lp.height - (lp.height * scaleY)) / 2.0f);
}
+3 -2
View File
@@ -687,8 +687,9 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId);
// If the final screen is empty, convert it to the extra empty screen
if (finalScreen.getShortcutsAndWidgets().getChildCount() == 0 &&
!finalScreen.isDropPending()) {
if (finalScreen != null
&& finalScreen.getShortcutsAndWidgets().getChildCount() == 0
&& !finalScreen.isDropPending()) {
mWorkspaceScreens.remove(finalScreenId);
mScreenOrder.removeValue(finalScreenId);
@@ -59,6 +59,7 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Insettable;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.R;
@@ -118,7 +119,8 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
private SpannableStringBuilder mSearchQueryBuilder = null;
protected boolean mUsingTabs;
private boolean mSearchModeWhileUsingTabs = false;
private boolean mIsSearching;
private boolean mHasWorkApps;
protected RecyclerViewFastScroller mTouchHandler;
protected final Point mFastScrollerOffset = new Point();
@@ -132,6 +134,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
private final float mHeaderThreshold;
private ScrimView mScrimView;
private int mHeaderColor;
private int mTabsProtectionAlpha;
public AllAppsContainerView(Context context) {
this(context, null);
@@ -189,7 +192,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
int currentPage = state.getInt(BUNDLE_KEY_CURRENT_PAGE, 0);
if (currentPage != 0 && mViewPager != null) {
mViewPager.setCurrentPage(currentPage);
rebindAdapters(true);
rebindAdapters();
} else {
reset(true);
}
@@ -243,9 +246,10 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
break;
}
}
mHasWorkApps = hasWorkApps;
if (!mAH[AdapterHolder.MAIN].appsList.hasFilter()) {
rebindAdapters(hasWorkApps);
if (hasWorkApps) {
rebindAdapters();
if (mHasWorkApps) {
resetWorkProfile();
}
}
@@ -322,6 +326,8 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
mViewPager.getNextPage() == 0
? R.string.all_apps_button_personal_label
: R.string.all_apps_button_work_label;
} else if (mIsSearching) {
descriptionRes = R.string.all_apps_search_results;
} else {
descriptionRes = R.string.all_apps_button_label;
}
@@ -370,7 +376,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
});
mHeader = findViewById(R.id.all_apps_header);
rebindAdapters(mUsingTabs, true /* force */);
rebindAdapters(true /* force */);
mSearchContainer = findViewById(R.id.search_container_all_apps);
mSearchUiManager = (SearchUiManager) mSearchContainer;
@@ -439,11 +445,12 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
}
}
private void rebindAdapters(boolean showTabs) {
rebindAdapters(showTabs, false /* force */);
private void rebindAdapters() {
rebindAdapters(false /* force */);
}
protected void rebindAdapters(boolean showTabs, boolean force) {
protected void rebindAdapters(boolean force) {
boolean showTabs = mHasWorkApps && !mIsSearching;
if (showTabs == mUsingTabs && !force) {
return;
}
@@ -454,7 +461,6 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
mAllAppsStore.unregisterIconContainer(mAH[AdapterHolder.WORK].recyclerView);
if (mUsingTabs) {
setupWorkToggle();
mAH[AdapterHolder.MAIN].setup(mViewPager.getChildAt(0), mPersonalMatcher);
mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher);
mAH[AdapterHolder.WORK].recyclerView.setId(R.id.apps_list_view_work);
@@ -485,6 +491,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
}
private void setupWorkToggle() {
removeWorkToggle();
if (Utilities.ATLEAST_P) {
mWorkModeSwitch = (WorkModeSwitch) mLauncher.getLayoutInflater().inflate(
R.layout.work_mode_fab, this, false);
@@ -497,10 +504,20 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
}
}
private void removeWorkToggle() {
if (mWorkModeSwitch == null) return;
if (mWorkModeSwitch.getParent() == this) {
this.removeView(mWorkModeSwitch);
}
mWorkModeSwitch = null;
}
private void replaceRVContainer(boolean showTabs) {
for (int i = 0; i < mAH.length; i++) {
if (mAH[i].recyclerView != null) {
mAH[i].recyclerView.setLayoutManager(null);
AllAppsRecyclerView rv = mAH[i].recyclerView;
if (rv != null) {
rv.setLayoutManager(null);
rv.setAdapter(null);
}
}
View oldView = getRecyclerViewContainer();
@@ -517,8 +534,10 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
mViewPager = (AllAppsPagedView) newView;
mViewPager.initParentViews(this);
mViewPager.getPageIndicator().setOnActivePageChangedListener(this);
setupWorkToggle();
} else {
mViewPager = null;
removeWorkToggle();
}
}
@@ -537,14 +556,6 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
mWorkModeSwitch.setWorkTabVisible(currentActivePage == AdapterHolder.WORK
&& mAllAppsStore.hasModelFlag(
FLAG_HAS_SHORTCUT_PERMISSION | FLAG_QUIET_MODE_CHANGE_PERMISSION));
if (currentActivePage == AdapterHolder.WORK) {
if (mWorkModeSwitch.getParent() == null) {
addView(mWorkModeSwitch);
}
} else {
removeView(mWorkModeSwitch);
}
}
}
@@ -621,18 +632,15 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
for (int i = 0; i < mAH.length; i++) {
mAH[i].adapter.setLastSearchQuery(query);
}
if (mUsingTabs) {
mSearchModeWhileUsingTabs = true;
rebindAdapters(false); // hide tabs
}
mIsSearching = true;
rebindAdapters();
mHeader.setCollapsed(true);
}
public void onClearSearchResult() {
if (mSearchModeWhileUsingTabs) {
rebindAdapters(true); // show tabs
mSearchModeWhileUsingTabs = false;
}
mIsSearching = false;
rebindAdapters();
getActiveRecyclerView().scrollToTop();
}
public void onSearchResultsChanged() {
@@ -706,13 +714,12 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
mHeaderPaint.setColor(mHeaderColor);
mHeaderPaint.setAlpha((int) (getAlpha() * Color.alpha(mHeaderColor)));
if (mHeaderPaint.getColor() != mScrimColor && mHeaderPaint.getColor() != 0) {
int bottom = mUsingTabs && mHeader.mHeaderCollapsed ? mHeader.getVisibleBottomBound()
: mSearchContainer.getBottom();
canvas.drawRect(0, 0, canvas.getWidth(), bottom + getTranslationY(),
mHeaderPaint);
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && getTranslationY() == 0) {
mSearchUiManager.getEditText().setBackground(null);
int bottom = (int) (mSearchContainer.getBottom() + getTranslationY());
canvas.drawRect(0, 0, canvas.getWidth(), bottom, mHeaderPaint);
int tabsHeight = getFloatingHeaderView().getPeripheralProtectionHeight();
if (mTabsProtectionAlpha > 0 && tabsHeight != 0) {
mHeaderPaint.setAlpha((int) (getAlpha() * mTabsProtectionAlpha));
canvas.drawRect(0, bottom, canvas.getWidth(), bottom + tabsHeight, mHeaderPaint);
}
}
}
@@ -792,18 +799,29 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
protected void updateHeaderScroll(int scrolledOffset) {
float prog = Math.max(0, Math.min(1, (float) scrolledOffset / mHeaderThreshold));
float prog = Utilities.boundToRange((float) scrolledOffset / mHeaderThreshold, 0f, 1f);
int viewBG = ColorUtils.blendARGB(mScrimColor, mHeaderProtectionColor, prog);
int headerColor = ColorUtils.setAlphaComponent(viewBG,
(int) (getSearchView().getAlpha() * 255));
if (headerColor != mHeaderColor) {
int tabsAlpha = mHeader.getPeripheralProtectionHeight() == 0 ? 0
: (int) (Utilities.boundToRange(
(scrolledOffset + mHeader.mSnappedScrolledY) / mHeaderThreshold, 0f, 1f)
* 255);
if (headerColor != mHeaderColor || mTabsProtectionAlpha != tabsAlpha) {
mHeaderColor = headerColor;
getSearchView().setBackgroundColor(viewBG);
getFloatingHeaderView().setHeaderColor(viewBG);
mTabsProtectionAlpha = tabsAlpha;
invalidateHeader();
if (scrolledOffset == 0 && mSearchUiManager.getEditText() != null) {
mSearchUiManager.getEditText().show();
}
if (mSearchUiManager.getEditText() != null) {
ExtendedEditText editText = mSearchUiManager.getEditText();
boolean bgVisible = editText.getBackgroundVisibility();
if (scrolledOffset == 0 && !mIsSearching) {
bgVisible = true;
} else if (scrolledOffset > mHeaderThreshold) {
bgVisible = false;
}
editText.setBackgroundVisibility(bgVisible, 1 - prog);
}
}
@@ -811,7 +829,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
* redraws header protection
*/
public void invalidateHeader() {
if (mScrimView != null && FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
if (mScrimView != null && mHeader.isHeaderProtectionSupported()) {
mScrimView.invalidate();
}
}
@@ -63,6 +63,13 @@ public class AllAppsRecyclerView extends BaseRecyclerView {
private final SparseIntArray mCachedScrollPositions = new SparseIntArray();
private final AllAppsFastScrollHelper mFastScrollHelper;
private final AdapterDataObserver mObserver = new RecyclerView.AdapterDataObserver() {
public void onChanged() {
mCachedScrollPositions.clear();
}
};
// The empty-search result background
private AllAppsBackgroundDrawable mEmptySearchBackground;
private int mEmptySearchBackgroundTopOffset;
@@ -247,12 +254,13 @@ public class AllAppsRecyclerView extends BaseRecyclerView {
@Override
public void setAdapter(Adapter adapter) {
if (getAdapter() != null) {
getAdapter().unregisterAdapterDataObserver(mObserver);
}
super.setAdapter(adapter);
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
public void onChanged() {
mCachedScrollPositions.clear();
}
});
if (adapter != null) {
adapter.registerAdapterDataObserver(mObserver);
}
}
@Override
@@ -17,9 +17,6 @@ package com.android.launcher3.allapps;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.ArrayMap;
@@ -50,11 +47,10 @@ public class FloatingHeaderView extends LinearLayout implements
ValueAnimator.AnimatorUpdateListener, PluginListener<AllAppsRow>, Insettable,
OnHeightUpdatedListener {
private final Rect mClip = new Rect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
private final Rect mRVClip = new Rect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
private final Rect mHeaderClip = new Rect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
private final ValueAnimator mAnimator = ValueAnimator.ofInt(0, 0);
private final ValueAnimator mHeaderAnimator = ValueAnimator.ofInt(0, 1).setDuration(100);
private final Point mTempOffset = new Point();
private final Paint mBGPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final RecyclerView.OnScrollListener mOnScrollListener =
new RecyclerView.OnScrollListener() {
@Override
@@ -82,19 +78,19 @@ public class FloatingHeaderView extends LinearLayout implements
}
};
private final int mHeaderTopPadding;
protected final Map<AllAppsRow, PluginHeaderRow> mPluginRows = new ArrayMap<>();
private final int mHeaderTopPadding;
private final boolean mHeaderProtectionSupported;
protected ViewGroup mTabLayout;
private AllAppsRecyclerView mMainRV;
private AllAppsRecyclerView mWorkRV;
private AllAppsRecyclerView mCurrentRV;
private ViewGroup mParent;
public boolean mHeaderCollapsed;
private int mSnappedScrolledY;
protected int mSnappedScrolledY;
private int mTranslationY;
private int mHeaderColor;
private boolean mForwardToRecyclerView;
@@ -120,6 +116,8 @@ public class FloatingHeaderView extends LinearLayout implements
super(context, attrs);
mHeaderTopPadding = context.getResources()
.getDimensionPixelSize(R.dimen.all_apps_header_top_padding);
mHeaderProtectionSupported = context.getResources().getBoolean(
R.bool.config_header_protection_supported);
}
@Override
@@ -138,7 +136,6 @@ public class FloatingHeaderView extends LinearLayout implements
}
mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]);
mAllRows = mFixedRows;
mHeaderAnimator.addUpdateListener(valueAnimator -> invalidate());
}
@Override
@@ -285,7 +282,7 @@ public class FloatingHeaderView extends LinearLayout implements
mHeaderCollapsed = false;
}
mTranslationY = currentScrollY;
} else if (!mHeaderCollapsed) {
} else {
mTranslationY = currentScrollY - mSnappedScrolledY - mMaxTranslation;
// update state vars
@@ -295,31 +292,10 @@ public class FloatingHeaderView extends LinearLayout implements
} else if (mTranslationY <= -mMaxTranslation) { // hide or stay hidden
mHeaderCollapsed = true;
mSnappedScrolledY = -mMaxTranslation;
mHeaderAnimator.setCurrentFraction(0);
mHeaderAnimator.start();
}
}
}
/**
* Set current header protection background color
*/
public void setHeaderColor(int color) {
mHeaderColor = color;
invalidate();
}
@Override
protected void dispatchDraw(Canvas canvas) {
if (mHeaderCollapsed && !mCollapsed && mTabLayout.getVisibility() == VISIBLE
&& mHeaderColor != Color.TRANSPARENT && FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
mBGPaint.setColor(mHeaderColor);
mBGPaint.setAlpha((int) (255 * mHeaderAnimator.getAnimatedFraction()));
canvas.drawRect(0, 0, getWidth(), getHeight() + mTranslationY, mBGPaint);
}
super.dispatchDraw(canvas);
}
protected void applyVerticalMove() {
int uncappedTranslationY = mTranslationY;
mTranslationY = Math.max(mTranslationY, -mMaxTranslation);
@@ -336,11 +312,15 @@ public class FloatingHeaderView extends LinearLayout implements
}
mTabLayout.setTranslationY(mTranslationY);
mClip.top = mMaxTranslation + mTranslationY;
int clipHeight = mHeaderTopPadding - getPaddingBottom();
mRVClip.top = mTabsHidden ? clipHeight : 0;
mHeaderClip.top = clipHeight;
// clipping on a draw might cause additional redraw
mMainRV.setClipBounds(mClip);
setClipBounds(mHeaderClip);
mMainRV.setClipBounds(mRVClip);
if (mWorkRV != null) {
mWorkRV.setClipBounds(mClip);
mWorkRV.setClipBounds(mRVClip);
}
}
@@ -421,6 +401,10 @@ public class FloatingHeaderView extends LinearLayout implements
return false;
}
public boolean isHeaderProtectionSupported() {
return mHeaderProtectionSupported;
}
@Override
public boolean hasOverlappingRendering() {
return false;
@@ -444,10 +428,19 @@ public class FloatingHeaderView extends LinearLayout implements
}
/**
* Returns visible height of FloatingHeaderView contents
* Returns visible height of FloatingHeaderView contents requiring header protection
*/
public int getVisibleBottomBound() {
return getBottom() + mTranslationY;
public int getPeripheralProtectionHeight() {
if (!mHeaderProtectionSupported) {
return 0;
}
// we only want to show protection when work tab is available and header is either
// collapsed or animating to/from collapsed state
if (mTabsHidden || !mHeaderCollapsed) {
return 0;
}
return Math.max(getHeight() - getPaddingTop() + mTranslationY, 0);
}
}
@@ -22,7 +22,6 @@ import android.view.MotionEvent;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.statemanager.StateManager.StateListener;
/**
* AllAppsContainerView with launcher specific callbacks
@@ -31,8 +30,6 @@ public class LauncherAllAppsContainerView extends AllAppsContainerView {
private final Launcher mLauncher;
private StateListener<LauncherState> mWorkTabListener;
public LauncherAllAppsContainerView(Context context) {
this(context, null);
}
@@ -74,14 +71,6 @@ public class LauncherAllAppsContainerView extends AllAppsContainerView {
mLauncher.getAllAppsController().setScrollRangeDelta(allAppsStartingPositionY);
}
@Override
public void setupHeader() {
super.setupHeader();
if (mWorkTabListener != null && !mUsingTabs) {
mLauncher.getStateManager().removeStateListener(mWorkTabListener);
}
}
@Override
public void onActivePageChanged(int currentActivePage) {
super.onActivePageChanged(currentActivePage);
@@ -51,6 +51,7 @@ public class WorkModeSwitch extends Button implements Insettable, View.OnClickLi
@Nullable
private KeyboardInsetAnimationCallback mKeyboardInsetAnimationCallback;
private boolean mWorkTabVisible;
public WorkModeSwitch(Context context) {
this(context, null, 0);
@@ -91,11 +92,10 @@ public class WorkModeSwitch extends Button implements Insettable, View.OnClickLi
*/
public void setWorkTabVisible(boolean workTabVisible) {
clearAnimation();
if (workTabVisible) {
mWorkTabVisible = workTabVisible;
if (workTabVisible && mWorkEnabled) {
setEnabled(true);
if (mWorkEnabled) {
setVisibility(VISIBLE);
}
setVisibility(VISIBLE);
setAlpha(0);
animate().alpha(1).start();
} else {
@@ -105,7 +105,7 @@ public class WorkModeSwitch extends Button implements Insettable, View.OnClickLi
@Override
public void onClick(View view) {
if (Utilities.ATLEAST_P) {
if (Utilities.ATLEAST_P && mWorkTabVisible) {
setEnabled(false);
Launcher.fromContext(getContext()).getStatsLogManager().logger().log(
LAUNCHER_TURN_OFF_WORK_APPS_TAP);
@@ -137,7 +137,7 @@ public class WorkModeSwitch extends Button implements Insettable, View.OnClickLi
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
if (Utilities.ATLEAST_R) {
if (Utilities.ATLEAST_R && mWorkTabVisible) {
setTranslationY(0);
if (insets.isVisible(WindowInsets.Type.ime())) {
Insets keyboardInsets = insets.getInsets(WindowInsets.Type.ime());
@@ -18,8 +18,10 @@ package com.android.launcher3.allapps.search;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_FOCUSED_ITEM_SELECTED_WITH_IME;
import android.text.Editable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.style.SuggestionSpan;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnFocusChangeListener;
@@ -47,6 +49,7 @@ public class AllAppsSearchBarController
protected SearchCallback<AdapterItem> mCallback;
protected ExtendedEditText mInput;
protected String mQuery;
private String[] mTextConversions;
protected SearchAlgorithm<AdapterItem> mSearchAlgorithm;
@@ -78,7 +81,20 @@ public class AllAppsSearchBarController
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Do nothing
mTextConversions = extractTextConversions(s);
}
private static String[] extractTextConversions(CharSequence text) {
if (text instanceof SpannableStringBuilder) {
SpannableStringBuilder spanned = (SpannableStringBuilder) text;
SuggestionSpan[] suggestionSpans =
spanned.getSpans(0, text.length(), SuggestionSpan.class);
if (suggestionSpans != null && suggestionSpans.length > 0) {
spanned.removeSpan(suggestionSpans[0]);
return suggestionSpans[0].getSuggestions();
}
}
return null;
}
@Override
@@ -89,7 +105,7 @@ public class AllAppsSearchBarController
mCallback.clearSearchResult();
} else {
mSearchAlgorithm.cancel(false);
mSearchAlgorithm.doSearch(mQuery, mCallback);
mSearchAlgorithm.doSearch(mQuery, mTextConversions, mCallback);
}
}
@@ -154,4 +170,4 @@ public class AllAppsSearchBarController
public boolean isSearchFieldFocused() {
return mInput.isFocused();
}
}
}
@@ -181,7 +181,7 @@ public final class FeatureFlags {
+ "Ignored if ENABLE_SMARTSPACE_UNIVERSAL is enabled.");
public static final BooleanFlag ENABLE_SMARTSPACE_FEEDBACK = getDebugFlag(
"ENABLE_SMARTSPACE_FEEDBACK", true,
"ENABLE_SMARTSPACE_FEEDBACK", false,
"Adds a menu option to send feedback for Enhanced Smartspace.");
public static final BooleanFlag ENABLE_SMARTSPACE_DISMISS = getDebugFlag(
@@ -254,6 +254,10 @@ public final class FeatureFlags {
"ENABLE_WALLPAPER_SCRIM", false,
"Enables scrim over wallpaper for text protection.");
public static final BooleanFlag WIDGETS_IN_LAUNCHER_PREVIEW = getDebugFlag(
"WIDGETS_IN_LAUNCHER_PREVIEW", true,
"Enables widgets in Launcher preview for the Wallpaper app.");
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
@@ -29,6 +29,7 @@ import android.annotation.TargetApi;
import android.app.Fragment;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
@@ -87,8 +88,11 @@ import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.widget.BaseLauncherAppWidgetHostView;
import com.android.launcher3.widget.LauncherAppWidgetHost;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.LocalColorExtractor;
import com.android.launcher3.widget.NavigableAppWidgetHostView;
import com.android.launcher3.widget.custom.CustomWidgetManager;
import java.util.ArrayList;
@@ -207,6 +211,7 @@ public class LauncherPreviewRenderer extends ContextWrapper
private final Hotseat mHotseat;
private final CellLayout mWorkspace;
private final SparseIntArray mWallpaperColorResources;
private final AppWidgetHost mAppWidgetHost;
public LauncherPreviewRenderer(Context context,
InvariantDeviceProfile idp,
@@ -273,6 +278,9 @@ public class LauncherPreviewRenderer extends ContextWrapper
} else {
mWallpaperColorResources = null;
}
mAppWidgetHost = FeatureFlags.WIDGETS_IN_LAUNCHER_PREVIEW.get()
? new LauncherPreviewAppWidgetHost(context)
: null;
}
/** Populate preview and render it. */
@@ -372,9 +380,20 @@ public class LauncherPreviewRenderer extends ContextWrapper
private void inflateAndAddWidgets(
LauncherAppWidgetInfo info, LauncherAppWidgetProviderInfo providerInfo) {
AppWidgetHostView view = new AppWidgetHostView(mContext);
view.setAppWidget(-1, providerInfo);
view.updateAppWidget(null);
AppWidgetHostView view;
if (FeatureFlags.WIDGETS_IN_LAUNCHER_PREVIEW.get()) {
view = mAppWidgetHost.createView(mContext, info.appWidgetId, providerInfo);
} else {
view = new NavigableAppWidgetHostView(this) {
@Override
protected boolean shouldAllowDirectClick() {
return false;
}
};
view.setAppWidget(-1, providerInfo);
view.updateAppWidget(null);
}
view.setTag(info);
if (mWallpaperColorResources != null) {
@@ -504,4 +523,31 @@ public class LauncherPreviewRenderer extends ContextWrapper
return true;
}
}
private class LauncherPreviewAppWidgetHost extends AppWidgetHost {
private LauncherPreviewAppWidgetHost(Context context) {
super(context, LauncherAppWidgetHost.APPWIDGET_HOST_ID);
}
@Override
protected AppWidgetHostView onCreateView(
Context context,
int appWidgetId,
AppWidgetProviderInfo appWidget) {
return new LauncherPreviewAppWidgetHostView(LauncherPreviewRenderer.this);
}
}
private static class LauncherPreviewAppWidgetHostView extends BaseLauncherAppWidgetHostView {
private LauncherPreviewAppWidgetHostView(Context context) {
super(context);
}
@Override
protected boolean shouldAllowDirectClick() {
return false;
}
}
}
@@ -21,7 +21,6 @@ import android.content.pm.PackageManager;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.util.InstantAppResolver;
@@ -73,13 +72,7 @@ public class PackageInstallStateChangedTask extends BaseModelUpdateTask {
dataModel.forAllWorkspaceItemInfos(mInstallInfo.user, si -> {
if (si.hasPromiseIconUi()
&& mInstallInfo.packageName.equals(si.getTargetPackage())) {
int installProgress = mInstallInfo.progress;
si.setProgressLevel(installProgress, PackageInstallInfo.STATUS_INSTALLING);
if (mInstallInfo.state == PackageInstallInfo.STATUS_FAILED) {
// Mark this info as broken.
si.runtimeStatusFlags &= ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
}
si.setProgressLevel(mInstallInfo);
updates.add(si);
}
});
@@ -24,6 +24,7 @@ import androidx.annotation.Nullable;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.util.PackageManagerHelper;
@@ -179,6 +180,12 @@ public abstract class ItemInfoWithIcon extends ItemInfo {
*/
public void setProgressLevel(PackageInstallInfo installInfo) {
setProgressLevel(installInfo.progress, installInfo.state);
if (installInfo.state == PackageInstallInfo.STATUS_FAILED) {
FileLog.d(TAG,
"Icon info: " + this + " marked broken with install info: " + installInfo,
new Exception());
}
}
/**
@@ -0,0 +1,283 @@
/*
* Copyright (C) 2021 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.notification;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.OverScroll;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Class to manage the notification UI in a {@link PopupContainerWithArrow}.
*
* - Has two {@link NotificationMainView} that represent the top two notifications
* - Handles dismissing a notification
*/
public class NotificationContainer extends FrameLayout implements SingleAxisSwipeDetector.Listener {
private static final FloatProperty<NotificationContainer> DRAG_TRANSLATION_X =
new FloatProperty<NotificationContainer>("notificationProgress") {
@Override
public void setValue(NotificationContainer view, float transX) {
view.setDragTranslationX(transX);
}
@Override
public Float get(NotificationContainer view) {
return view.mDragTranslationX;
}
};
private static final Rect sTempRect = new Rect();
private final SingleAxisSwipeDetector mSwipeDetector;
private final List<NotificationInfo> mNotificationInfos = new ArrayList<>();
private boolean mIgnoreTouch = false;
private final ObjectAnimator mContentTranslateAnimator;
private float mDragTranslationX = 0;
private final NotificationMainView mPrimaryView;
private final NotificationMainView mSecondaryView;
private PopupContainerWithArrow mPopupContainer;
public NotificationContainer(Context context) {
this(context, null, 0);
}
public NotificationContainer(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public NotificationContainer(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mSwipeDetector = new SingleAxisSwipeDetector(getContext(), this, HORIZONTAL);
mSwipeDetector.setDetectableScrollConditions(SingleAxisSwipeDetector.DIRECTION_BOTH, false);
mContentTranslateAnimator = ObjectAnimator.ofFloat(this, DRAG_TRANSLATION_X, 0);
mPrimaryView = (NotificationMainView) View.inflate(getContext(),
R.layout.notification_content, null);
mSecondaryView = (NotificationMainView) View.inflate(getContext(),
R.layout.notification_content, null);
mSecondaryView.setAlpha(0);
addView(mSecondaryView);
addView(mPrimaryView);
}
public void setPopupView(PopupContainerWithArrow popupView) {
mPopupContainer = popupView;
}
/**
* Returns true if we should intercept the swipe.
*/
public boolean onInterceptSwipeEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
sTempRect.set(getLeft(), getTop(), getRight(), getBottom());
mIgnoreTouch = !sTempRect.contains((int) ev.getX(), (int) ev.getY());
if (!mIgnoreTouch) {
mPopupContainer.getParent().requestDisallowInterceptTouchEvent(true);
}
}
if (mIgnoreTouch) {
return false;
}
if (mPrimaryView.getNotificationInfo() == null) {
// The notification hasn't been populated yet.
return false;
}
mSwipeDetector.onTouchEvent(ev);
return mSwipeDetector.isDraggingOrSettling();
}
/**
* Returns true when we should handle the swipe.
*/
public boolean onSwipeEvent(MotionEvent ev) {
if (mIgnoreTouch) {
return false;
}
if (mPrimaryView.getNotificationInfo() == null) {
// The notification hasn't been populated yet.
return false;
}
return mSwipeDetector.onTouchEvent(ev);
}
/**
* Applies the list of @param notificationInfos to this container.
*/
public void applyNotificationInfos(final List<NotificationInfo> notificationInfos) {
mNotificationInfos.clear();
if (notificationInfos.isEmpty()) {
mPrimaryView.applyNotificationInfo(null);
mSecondaryView.applyNotificationInfo(null);
return;
}
mNotificationInfos.addAll(notificationInfos);
NotificationInfo mainNotification = notificationInfos.get(0);
mPrimaryView.applyNotificationInfo(mainNotification);
mSecondaryView.applyNotificationInfo(notificationInfos.size() > 1
? notificationInfos.get(1)
: null);
}
/**
* Trims the notifications.
* @param notificationKeys List of all valid notification keys.
*/
public void trimNotifications(final List<String> notificationKeys) {
Iterator<NotificationInfo> iterator = mNotificationInfos.iterator();
while (iterator.hasNext()) {
if (!notificationKeys.contains(iterator.next().notificationKey)) {
iterator.remove();
}
}
NotificationInfo primaryInfo = mNotificationInfos.size() > 0
? mNotificationInfos.get(0)
: null;
NotificationInfo secondaryInfo = mNotificationInfos.size() > 1
? mNotificationInfos.get(1)
: null;
mPrimaryView.applyNotificationInfo(primaryInfo);
mSecondaryView.applyNotificationInfo(secondaryInfo);
mPrimaryView.onPrimaryDrag(0);
mSecondaryView.onSecondaryDrag(0);
}
private void setDragTranslationX(float translationX) {
mDragTranslationX = translationX;
float progress = translationX / getWidth();
mPrimaryView.onPrimaryDrag(progress);
if (mSecondaryView.getNotificationInfo() == null) {
mSecondaryView.setAlpha(0f);
} else {
mSecondaryView.onSecondaryDrag(progress);
}
}
// SingleAxisSwipeDetector.Listener's
@Override
public void onDragStart(boolean start, float startDisplacement) {
mPopupContainer.showArrow(false);
}
@Override
public boolean onDrag(float displacement) {
if (!mPrimaryView.canChildBeDismissed()) {
displacement = OverScroll.dampedScroll(displacement, getWidth());
}
float progress = displacement / getWidth();
mPrimaryView.onPrimaryDrag(progress);
if (mSecondaryView.getNotificationInfo() == null) {
mSecondaryView.setAlpha(0f);
} else {
mSecondaryView.onSecondaryDrag(progress);
}
mContentTranslateAnimator.cancel();
return true;
}
@Override
public void onDragEnd(float velocity) {
final boolean willExit;
final float endTranslation;
final float startTranslation = mPrimaryView.getTranslationX();
final float width = getWidth();
if (!mPrimaryView.canChildBeDismissed()) {
willExit = false;
endTranslation = 0;
} else if (mSwipeDetector.isFling(velocity)) {
willExit = true;
endTranslation = velocity < 0 ? -width : width;
} else if (Math.abs(startTranslation) > width / 2f) {
willExit = true;
endTranslation = (startTranslation < 0 ? -width : width);
} else {
willExit = false;
endTranslation = 0;
}
long duration = BaseSwipeDetector.calculateDuration(velocity,
(endTranslation - startTranslation) / width);
mContentTranslateAnimator.removeAllListeners();
mContentTranslateAnimator.setDuration(duration)
.setInterpolator(scrollInterpolatorForVelocity(velocity));
mContentTranslateAnimator.setFloatValues(startTranslation, endTranslation);
NotificationMainView current = mPrimaryView;
mContentTranslateAnimator.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
mSwipeDetector.finishedScrolling();
if (willExit) {
current.onChildDismissed();
}
mPopupContainer.showArrow(true);
}
});
mContentTranslateAnimator.start();
}
/**
* Animates the background color to a new color.
* @param color The color to change to.
* @param animatorSetOut The AnimatorSet where we add the color animator to.
*/
public void updateBackgroundColor(int color, AnimatorSet animatorSetOut) {
mPrimaryView.updateBackgroundColor(color, animatorSetOut);
mSecondaryView.updateBackgroundColor(color, animatorSetOut);
}
/**
* Updates the header with a new @param notificationCount.
*/
public void updateHeader(int notificationCount) {
mPrimaryView.updateHeader(notificationCount);
mSecondaryView.updateHeader(notificationCount - 1);
}
}
@@ -1,179 +0,0 @@
/*
* 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.notification;
import android.animation.AnimatorSet;
import android.content.Context;
import android.graphics.Outline;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.ViewOutlineProvider;
import android.widget.TextView;
import com.android.launcher3.R;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.util.Themes;
import java.util.ArrayList;
import java.util.List;
/**
* Utility class to manage notification UI
*/
public class NotificationItemView {
private static final Rect sTempRect = new Rect();
private final Context mContext;
private final PopupContainerWithArrow mPopupContainer;
private final ViewGroup mRootView;
private final TextView mHeaderCount;
private final NotificationMainView mMainView;
private final View mHeader;
private View mGutter;
private boolean mIgnoreTouch = false;
private List<NotificationInfo> mNotificationInfos = new ArrayList<>();
public NotificationItemView(PopupContainerWithArrow container, ViewGroup rootView) {
mPopupContainer = container;
mRootView = rootView;
mContext = container.getContext();
mHeaderCount = container.findViewById(R.id.notification_count);
mMainView = container.findViewById(R.id.main_view);
mHeader = container.findViewById(R.id.header);
float radius = Themes.getDialogCornerRadius(mContext);
rootView.setClipToOutline(true);
rootView.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), radius);
}
});
}
/**
* Animates the background color to a new color.
* @param color The color to change to.
* @param animatorSetOut The AnimatorSet where we add the color animator to.
*/
public void updateBackgroundColor(int color, AnimatorSet animatorSetOut) {
mMainView.updateBackgroundColor(color, animatorSetOut);
}
public void addGutter() {
if (mGutter == null) {
mGutter = mPopupContainer.inflateAndAdd(R.layout.notification_gutter, mRootView);
}
}
public void inverseGutterMargin() {
MarginLayoutParams lp = (MarginLayoutParams) mGutter.getLayoutParams();
int top = lp.topMargin;
lp.topMargin = lp.bottomMargin;
lp.bottomMargin = top;
}
public void removeAllViews() {
mRootView.removeView(mMainView);
mRootView.removeView(mHeader);
if (mGutter != null) {
mRootView.removeView(mGutter);
}
}
/**
* Updates the header text.
* @param notificationCount The number of notifications.
*/
public void updateHeader(int notificationCount) {
final String text;
final int visibility;
if (notificationCount <= 1) {
text = "";
visibility = View.INVISIBLE;
} else {
text = String.valueOf(notificationCount);
visibility = View.VISIBLE;
}
mHeaderCount.setText(text);
mHeaderCount.setVisibility(visibility);
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
sTempRect.set(mRootView.getLeft(), mRootView.getTop(),
mRootView.getRight(), mRootView.getBottom());
mIgnoreTouch = !sTempRect.contains((int) ev.getX(), (int) ev.getY());
if (!mIgnoreTouch) {
mPopupContainer.getParent().requestDisallowInterceptTouchEvent(true);
}
}
if (mIgnoreTouch) {
return false;
}
if (mMainView.getNotificationInfo() == null) {
// The notification hasn't been populated yet.
return false;
}
return false;
}
public void applyNotificationInfos(final List<NotificationInfo> notificationInfos) {
mNotificationInfos.clear();
if (notificationInfos.isEmpty()) {
return;
}
mNotificationInfos.addAll(notificationInfos);
NotificationInfo mainNotification = notificationInfos.get(0);
mMainView.applyNotificationInfo(mainNotification, false);
}
public void trimNotifications(final List<String> notificationKeys) {
NotificationInfo currentMainNotificationInfo = mMainView.getNotificationInfo();
boolean shouldUpdateMainNotification = !notificationKeys.contains(
currentMainNotificationInfo.notificationKey);
if (shouldUpdateMainNotification) {
int size = notificationKeys.size();
NotificationInfo nextNotification = null;
// We get the latest notification by finding the notification after the one that was
// just dismissed.
for (int i = 0; i < size; ++i) {
if (currentMainNotificationInfo == mNotificationInfos.get(i) && i + 1 < size) {
nextNotification = mNotificationInfos.get(i + 1);
break;
}
}
if (nextNotification != null) {
mMainView.applyNotificationInfo(nextNotification, true);
}
}
}
}
@@ -16,62 +16,70 @@
package com.android.launcher3.notification;
import static com.android.launcher3.Utilities.mapToRange;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DISMISSED;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.view.ViewOutlineProvider;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.util.Themes;
/**
* A {@link android.widget.FrameLayout} that contains a single notification,
* e.g. icon + title + text.
*/
@TargetApi(Build.VERSION_CODES.N)
public class NotificationMainView extends FrameLayout {
private static final FloatProperty<NotificationMainView> CONTENT_TRANSLATION =
new FloatProperty<NotificationMainView>("contentTranslation") {
@Override
public void setValue(NotificationMainView view, float v) {
view.setContentTranslation(v);
}
@Override
public Float get(NotificationMainView view) {
return view.mTextAndBackground.getTranslationX();
}
};
public class NotificationMainView extends LinearLayout {
// This is used only to track the notification view, so that it can be properly logged.
public static final ItemInfo NOTIFICATION_ITEM_INFO = new ItemInfo();
// Value when the primary notification main view will be gone (zero alpha).
private static final float PRIMARY_GONE_PROGRESS = 0.7f;
private static final float PRIMARY_MIN_PROGRESS = 0.40f;
private static final float PRIMARY_MAX_PROGRESS = 0.60f;
private static final float SECONDARY_MIN_PROGRESS = 0.30f;
private static final float SECONDARY_MAX_PROGRESS = 0.50f;
private static final float SECONDARY_CONTENT_MAX_PROGRESS = 0.6f;
private NotificationInfo mNotificationInfo;
private ViewGroup mTextAndBackground;
private int mBackgroundColor;
private TextView mTitleView;
private TextView mTextView;
private View mIconView;
private SingleAxisSwipeDetector mSwipeDetector;
private View mHeader;
private View mMainView;
private final ColorDrawable mColorDrawable;
private TextView mHeaderCount;
private final Rect mOutline = new Rect();
// Space between notifications during swipe
private final int mNotificationSpace;
private final int mMaxTransX;
private final int mMaxElevation;
private final GradientDrawable mBackground;
public NotificationMainView(Context context) {
this(context, null, 0);
@@ -82,28 +90,77 @@ public class NotificationMainView extends FrameLayout {
}
public NotificationMainView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this(context, attrs, defStyle, 0);
}
mColorDrawable = new ColorDrawable(Color.TRANSPARENT);
public NotificationMainView(Context context, AttributeSet attrs, int defStyle, int defStylRes) {
super(context, attrs, defStyle, defStylRes);
float outlineRadius = Themes.getDialogCornerRadius(context);
mBackground = new GradientDrawable();
mBackground.setColor(Themes.getAttrColor(context, R.attr.popupColorPrimary));
mBackground.setCornerRadius(outlineRadius);
setBackground(mBackground);
mMaxElevation = getResources().getDimensionPixelSize(R.dimen.deep_shortcuts_elevation);
setElevation(mMaxElevation);
mMaxTransX = getResources().getDimensionPixelSize(R.dimen.notification_max_trans);
mNotificationSpace = getResources().getDimensionPixelSize(R.dimen.notification_space);
setClipToOutline(true);
setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(mOutline, outlineRadius);
}
});
}
/**
* Updates the header text.
* @param notificationCount The number of notifications.
*/
public void updateHeader(int notificationCount) {
final String text;
final int visibility;
if (notificationCount <= 1) {
text = "";
visibility = View.INVISIBLE;
} else {
text = String.valueOf(notificationCount);
visibility = View.VISIBLE;
}
mHeaderCount.setText(text);
mHeaderCount.setVisibility(visibility);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mTextAndBackground = findViewById(R.id.text_and_background);
mTitleView = mTextAndBackground.findViewById(R.id.title);
mTextView = mTextAndBackground.findViewById(R.id.text);
ViewGroup textAndBackground = findViewById(R.id.text_and_background);
mTitleView = textAndBackground.findViewById(R.id.title);
mTextView = textAndBackground.findViewById(R.id.text);
mIconView = findViewById(R.id.popup_item_icon);
mHeaderCount = findViewById(R.id.notification_count);
ColorDrawable colorBackground = (ColorDrawable) mTextAndBackground.getBackground();
updateBackgroundColor(colorBackground.getColor());
mHeader = findViewById(R.id.header);
mMainView = findViewById(R.id.main_view);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mOutline.set(0, 0, getWidth(), getHeight());
invalidateOutline();
}
private void updateBackgroundColor(int color) {
mBackgroundColor = color;
mColorDrawable.setColor(color);
mTextAndBackground.setBackground(mColorDrawable);
mBackground.setColor(color);
if (mNotificationInfo != null) {
mIconView.setBackground(mNotificationInfo.getIconForBackground(getContext(),
mBackgroundColor));
@@ -128,8 +185,11 @@ public class NotificationMainView extends FrameLayout {
/**
* Sets the content of this view, animating it after a new icon shifts up if necessary.
*/
public void applyNotificationInfo(NotificationInfo mainNotification, boolean animate) {
mNotificationInfo = mainNotification;
public void applyNotificationInfo(NotificationInfo notificationInfo) {
mNotificationInfo = notificationInfo;
if (notificationInfo == null) {
return;
}
NotificationListener listener = NotificationListener.getInstanceIfConnected();
if (listener != null) {
listener.setNotificationsShown(new String[] {mNotificationInfo.notificationKey});
@@ -149,25 +209,112 @@ public class NotificationMainView extends FrameLayout {
if (mNotificationInfo.intent != null) {
setOnClickListener(mNotificationInfo);
}
setContentTranslation(0);
// Add a stub ItemInfo so that logging populates the correct container and item types
// instead of DEFAULT_CONTAINERTYPE and DEFAULT_ITEMTYPE, respectively.
setTag(NOTIFICATION_ITEM_INFO);
if (animate) {
ObjectAnimator.ofFloat(mTextAndBackground, ALPHA, 0, 1).setDuration(150).start();
}
/**
* Sets the alpha of only the child views.
*/
public void setContentAlpha(float alpha) {
mHeader.setAlpha(alpha);
mMainView.setAlpha(alpha);
}
/**
* Sets the translation of only the child views.
*/
public void setContentTranslationX(float transX) {
mHeader.setTranslationX(transX);
mMainView.setTranslationX(transX);
}
/**
* Updates the alpha, content alpha, and elevation of this view.
*
* @param progress Range from [0, 1] or [-1, 0]
* When 0: Full alpha
* When 1/-1: zero alpha
*/
public void onPrimaryDrag(float progress) {
float absProgress = Math.abs(progress);
final int width = getWidth();
float min = PRIMARY_MIN_PROGRESS;
float max = PRIMARY_MAX_PROGRESS;
if (absProgress < min) {
setAlpha(1f);
setContentAlpha(1);
setElevation(mMaxElevation);
} else if (absProgress < max) {
setAlpha(1f);
setContentAlpha(mapToRange(absProgress, min, max, 1f, 0f, LINEAR));
setElevation(Utilities.mapToRange(absProgress, min, max, mMaxElevation, 0, LINEAR));
} else {
setAlpha(mapToRange(absProgress, max, PRIMARY_GONE_PROGRESS, 1f, 0f, LINEAR));
setContentAlpha(0f);
setElevation(0f);
}
setTranslationX(width * progress);
}
public void setContentTranslation(float translation) {
mTextAndBackground.setTranslationX(translation);
mIconView.setTranslationX(translation);
/**
* Updates the alpha, content alpha, elevation, and clipping of this view.
* @param progress Range from [0, 1] or [-1, 0]
* When 0: Smallest clipping, zero alpha
* When 1/-1: Full clip, full alpha
*/
public void onSecondaryDrag(float progress) {
final float absProgress = Math.abs(progress);
float min = SECONDARY_MIN_PROGRESS;
float max = SECONDARY_MAX_PROGRESS;
float contentMax = SECONDARY_CONTENT_MAX_PROGRESS;
if (absProgress < min) {
setAlpha(0f);
setContentAlpha(0);
setElevation(0f);
} else if (absProgress < max) {
setAlpha(mapToRange(absProgress, min, max, 0, 1f, LINEAR));
setContentAlpha(0f);
setElevation(0f);
} else {
setAlpha(1f);
setContentAlpha(absProgress > contentMax
? 1f
: mapToRange(absProgress, max, contentMax, 0, 1f, LINEAR));
setElevation(Utilities.mapToRange(absProgress, max, 1, 0, mMaxElevation, LINEAR));
}
final int width = getWidth();
int crop = (int) (width * absProgress);
int space = (int) (absProgress > PRIMARY_GONE_PROGRESS
? mapToRange(absProgress, PRIMARY_GONE_PROGRESS, 1f, mNotificationSpace, 0, LINEAR)
: mNotificationSpace);
if (progress < 0) {
mOutline.left = Math.max(0, getWidth() - crop + space);
mOutline.right = getWidth();
} else {
mOutline.right = Math.min(getWidth(), crop - space);
mOutline.left = 0;
}
float contentTransX = mMaxTransX * (1f - absProgress);
setContentTranslationX(progress < 0
? contentTransX
: -contentTransX);
invalidateOutline();
}
public NotificationInfo getNotificationInfo() {
public @Nullable NotificationInfo getNotificationInfo() {
return mNotificationInfo;
}
public boolean canChildBeDismissed() {
return mNotificationInfo != null && mNotificationInfo.dismissable;
}
@@ -57,4 +57,28 @@ public final class PackageInstallInfo {
public static PackageInstallInfo fromState(int state, String packageName, UserHandle user) {
return new PackageInstallInfo(packageName, state, 0 /* progress */, user);
}
@Override
public String toString() {
return getClass().getSimpleName() + "(" + dumpProperties() + ")";
}
private String dumpProperties() {
return "componentName=" + componentName
+ "packageName=" + packageName
+ " state=" + stateToString()
+ " progress=" + progress
+ " user=" + user;
}
private String stateToString() {
switch (state) {
case STATUS_INSTALLED : return "STATUS_INSTALLED";
case STATUS_INSTALLING : return "STATUS_INSTALLING";
case STATUS_INSTALLED_DOWNLOADING : return "STATUS_INSTALLED_DOWNLOADING";
case STATUS_FAILED : return "STATUS_FAILED";
default : return "INVALID STATE";
}
}
}
@@ -487,6 +487,13 @@ public abstract class ArrowPopup<T extends StatefulActivity<LauncherState>>
return getMeasuredWidth() - mArrowOffsetHorizontal - mArrowWidth;
}
/**
* @param show If true, shows arrow (when applicable), otherwise hides arrow.
*/
public void showArrow(boolean show) {
mArrow.setVisibility(show && shouldAddArrow() ? VISIBLE : INVISIBLE);
}
private void addArrow() {
getPopupContainer().addView(mArrow);
mArrow.setX(getX() + getArrowLeft());
@@ -58,8 +58,8 @@ import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.notification.NotificationContainer;
import com.android.launcher3.notification.NotificationInfo;
import com.android.launcher3.notification.NotificationItemView;
import com.android.launcher3.notification.NotificationKeyData;
import com.android.launcher3.popup.PopupDataProvider.PopupDataChangeListener;
import com.android.launcher3.shortcuts.DeepShortcutView;
@@ -92,9 +92,8 @@ public class PopupContainerWithArrow<T extends StatefulActivity<LauncherState>>
private final int mStartDragThreshold;
private BubbleTextView mOriginalIcon;
private NotificationItemView mNotificationItemView;
private int mNumNotifications;
private ViewGroup mNotificationContainer;
private NotificationContainer mNotificationContainer;
private ViewGroup mWidgetContainer;
@@ -128,8 +127,8 @@ public class PopupContainerWithArrow<T extends StatefulActivity<LauncherState>>
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mInterceptTouchDown.set(ev.getX(), ev.getY());
}
if (mNotificationItemView != null
&& mNotificationItemView.onInterceptTouchEvent(ev)) {
if (mNotificationContainer != null
&& mNotificationContainer.onInterceptSwipeEvent(ev)) {
return true;
}
// Stop sending touch events to deep shortcut views if user moved beyond touch slop.
@@ -137,6 +136,14 @@ public class PopupContainerWithArrow<T extends StatefulActivity<LauncherState>>
> squaredTouchSlop(getContext());
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mNotificationContainer != null) {
return mNotificationContainer.onSwipeEvent(ev) || super.onTouchEvent(ev);
}
return super.onTouchEvent(ev);
}
@Override
protected boolean isOfType(int type) {
return (type & TYPE_ACTION_POPUP) != 0;
@@ -172,8 +179,8 @@ public class PopupContainerWithArrow<T extends StatefulActivity<LauncherState>>
@Override
protected void setChildColor(View view, int color, AnimatorSet animatorSetOut) {
super.setChildColor(view, color, animatorSetOut);
if (view.getId() == R.id.notification_container && mNotificationItemView != null) {
mNotificationItemView.updateBackgroundColor(color, animatorSetOut);
if (view.getId() == R.id.notification_container && mNotificationContainer != null) {
mNotificationContainer.updateBackgroundColor(color, animatorSetOut);
}
}
@@ -232,13 +239,6 @@ public class PopupContainerWithArrow<T extends StatefulActivity<LauncherState>>
mNotificationContainer);
}
@Override
protected void onInflationComplete(boolean isReversed) {
if (isReversed && mNotificationItemView != null) {
mNotificationItemView.inverseGutterMargin();
}
}
@TargetApi(Build.VERSION_CODES.P)
public void populateAndShow(final BubbleTextView originalIcon, int shortcutCount,
final List<NotificationKeyData> notificationKeys, List<SystemShortcut> systemShortcuts) {
@@ -261,9 +261,10 @@ public class PopupContainerWithArrow<T extends StatefulActivity<LauncherState>>
if (mNotificationContainer == null) {
mNotificationContainer = findViewById(R.id.notification_container);
mNotificationContainer.setVisibility(VISIBLE);
mNotificationContainer.setPopupView(this);
} else {
mNotificationContainer.setVisibility(GONE);
}
View.inflate(getContext(), R.layout.notification_content, mNotificationContainer);
mNotificationItemView = new NotificationItemView(this, mNotificationContainer);
updateNotificationHeader();
}
int viewsToFlip = getChildCount();
@@ -274,10 +275,6 @@ public class PopupContainerWithArrow<T extends StatefulActivity<LauncherState>>
if (hasDeepShortcuts) {
mDeepShortcutContainer.setVisibility(View.VISIBLE);
if (mNotificationItemView != null) {
mNotificationItemView.addGutter();
}
for (int i = shortcutCount; i > 0; i--) {
DeepShortcutView v = inflateAndAdd(R.layout.deep_shortcut, mDeepShortcutContainer);
v.getLayoutParams().width = containerWidth;
@@ -309,10 +306,6 @@ public class PopupContainerWithArrow<T extends StatefulActivity<LauncherState>>
} else {
mDeepShortcutContainer.setVisibility(View.GONE);
if (!systemShortcuts.isEmpty()) {
if (mNotificationItemView != null) {
mNotificationItemView.addGutter();
}
for (SystemShortcut shortcut : systemShortcuts) {
initializeSystemShortcut(R.layout.system_shortcut, this, shortcut);
}
@@ -355,13 +348,13 @@ public class PopupContainerWithArrow<T extends StatefulActivity<LauncherState>>
}
public void applyNotificationInfos(List<NotificationInfo> notificationInfos) {
if (mNotificationItemView != null) {
mNotificationItemView.applyNotificationInfos(notificationInfos);
if (mNotificationContainer != null) {
mNotificationContainer.applyNotificationInfos(notificationInfos);
}
}
private void updateHiddenShortcuts() {
int allowedCount = mNotificationItemView != null
int allowedCount = mNotificationContainer != null
? MAX_SHORTCUTS_IF_NOTIFICATIONS : MAX_SHORTCUTS;
int total = mShortcuts.size();
@@ -447,8 +440,8 @@ public class PopupContainerWithArrow<T extends StatefulActivity<LauncherState>>
private void updateNotificationHeader() {
ItemInfoWithIcon itemInfo = (ItemInfoWithIcon) mOriginalIcon.getTag();
DotInfo dotInfo = mLauncher.getDotInfoForItem(itemInfo);
if (mNotificationItemView != null && dotInfo != null) {
mNotificationItemView.updateHeader(dotInfo.getNotificationCount());
if (mNotificationContainer != null && dotInfo != null) {
mNotificationContainer.updateHeader(dotInfo.getNotificationCount());
}
}
@@ -590,20 +583,18 @@ public class PopupContainerWithArrow<T extends StatefulActivity<LauncherState>>
@Override
public void trimNotifications(Map<PackageUserKey, DotInfo> updatedDots) {
if (mNotificationItemView == null) {
if (mNotificationContainer == null) {
return;
}
ItemInfo originalInfo = (ItemInfo) mOriginalIcon.getTag();
DotInfo dotInfo = updatedDots.get(PackageUserKey.fromItemInfo(originalInfo));
if (dotInfo == null || dotInfo.getNotificationKeys().size() == 0) {
// No more notifications, remove the notification views and expand all shortcuts.
mNotificationItemView.removeAllViews();
mNotificationItemView = null;
mNotificationContainer.setVisibility(GONE);
updateHiddenShortcuts();
assignMarginsAndBackgrounds(PopupContainerWithArrow.this);
} else {
mNotificationItemView.trimNotifications(
mNotificationContainer.trimNotifications(
NotificationKeyData.extractKeysOnly(dotInfo.getNotificationKeys()));
}
}
@@ -27,6 +27,13 @@ public interface SearchAlgorithm<T> {
*/
void doSearch(String query, SearchCallback<T> callback);
/**
* Performs search with {@code query} and the {@code suggestedQueries}/
*/
default void doSearch(String query, String[] suggestedQueries, SearchCallback<T> callback) {
doSearch(query, callback);
}
/**
* Cancels any active request.
*/
@@ -62,8 +62,9 @@ public class SettingsActivity extends FragmentActivity
SharedPreferences.OnSharedPreferenceChangeListener{
/** List of fragments that can be hosted by this activity. */
private static final List<String> VALID_PREFERENCE_FRAGMENTS = Collections.singletonList(
DeveloperOptionsFragment.class.getName());
private static final List<String> VALID_PREFERENCE_FRAGMENTS =
!Utilities.IS_DEBUG_DEVICE ? Collections.emptyList()
: Collections.singletonList(DeveloperOptionsFragment.class.getName());
private static final String DEVELOPER_OPTIONS_KEY = "pref_developer_options";
private static final String FLAGS_PREFERENCE_KEY = "flag_toggler";
+35 -1
View File
@@ -25,23 +25,27 @@ import android.graphics.drawable.ColorDrawable;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.core.graphics.ColorUtils;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.Insettable;
import com.android.launcher3.util.SystemUiController;
import java.util.ArrayList;
/**
* Simple scrim which draws a flat color
*/
public class ScrimView extends View implements Insettable {
private static final float STATUS_BAR_COLOR_FORCE_UPDATE_THRESHOLD = 0.9f;
private final ArrayList<Runnable> mOpaquenessListeners = new ArrayList<>(1);
private SystemUiController mSystemUiController;
private ScrimDrawingController mDrawingController;
private int mBackgroundColor;
private boolean mIsVisible = true;
private boolean mLastDispatchedOpaqueness;
public ScrimView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -60,6 +64,7 @@ public class ScrimView extends View implements Insettable {
@Override
protected boolean onSetAlpha(int alpha) {
updateSysUiColors();
dispatchVisibilityListenersIfNeeded();
return super.onSetAlpha(alpha);
}
@@ -67,6 +72,7 @@ public class ScrimView extends View implements Insettable {
public void setBackgroundColor(int color) {
mBackgroundColor = color;
updateSysUiColors();
dispatchVisibilityListenersIfNeeded();
super.setBackgroundColor(color);
}
@@ -74,6 +80,7 @@ public class ScrimView extends View implements Insettable {
public void onVisibilityAggregated(boolean isVisible) {
super.onVisibilityAggregated(isVisible);
mIsVisible = isVisible;
dispatchVisibilityListenersIfNeeded();
}
public boolean isFullyOpaque() {
@@ -108,6 +115,17 @@ public class ScrimView extends View implements Insettable {
}
}
private void dispatchVisibilityListenersIfNeeded() {
boolean fullyOpaque = isFullyOpaque();
if (mLastDispatchedOpaqueness == fullyOpaque) {
return;
}
mLastDispatchedOpaqueness = fullyOpaque;
for (int i = 0; i < mOpaquenessListeners.size(); i++) {
mOpaquenessListeners.get(i).run();
}
}
private SystemUiController getSystemUiController() {
if (mSystemUiController == null) {
mSystemUiController = BaseActivity.fromContext(getContext()).getSystemUiController();
@@ -135,6 +153,22 @@ public class ScrimView extends View implements Insettable {
}
}
/**
* Registers a listener to be notified of whether the scrim is occluding other UI elements.
* @see #isFullyOpaque()
*/
public void addOpaquenessListener(@NonNull Runnable listener) {
mOpaquenessListeners.add(listener);
}
/**
* Removes previously registered listener.
* @see #addOpaquenessListener(Runnable)
*/
public void removeOpaquenessListener(@NonNull Runnable listener) {
mOpaquenessListeners.remove(listener);
}
/**
* A Utility interface allowing for other surfaces to draw on ScrimView
*/
@@ -0,0 +1,119 @@
/*
* Copyright (C) 2021 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.content.Context;
import android.graphics.Outline;
import android.graphics.Rect;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.RemoteViews;
import androidx.annotation.UiThread;
import com.android.launcher3.R;
import com.android.launcher3.util.Executors;
/**
* Launcher AppWidgetHostView with support for rounded corners and a fallback View.
*/
public abstract class BaseLauncherAppWidgetHostView extends NavigableAppWidgetHostView {
protected final LayoutInflater mInflater;
private final Rect mEnforcedRectangle = new Rect();
private final float mEnforcedCornerRadius;
private final ViewOutlineProvider mCornerRadiusEnforcementOutline = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
if (mEnforcedRectangle.isEmpty() || mEnforcedCornerRadius <= 0) {
outline.setEmpty();
} else {
outline.setRoundRect(mEnforcedRectangle, mEnforcedCornerRadius);
}
}
};
public BaseLauncherAppWidgetHostView(Context context) {
super(context);
setExecutor(Executors.THREAD_POOL_EXECUTOR);
mInflater = LayoutInflater.from(context);
mEnforcedCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(getContext());
}
@Override
protected View getErrorView() {
return mInflater.inflate(R.layout.appwidget_error, this, false);
}
/**
* Fall back to error layout instead of showing widget.
*/
public void switchToErrorView() {
// Update the widget with 0 Layout id, to reset the view to error view.
updateAppWidget(new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0));
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
try {
super.onLayout(changed, left, top, right, bottom);
} catch (final RuntimeException e) {
post(this::switchToErrorView);
}
enforceRoundedCorners();
}
@UiThread
private void resetRoundedCorners() {
setOutlineProvider(ViewOutlineProvider.BACKGROUND);
setClipToOutline(false);
}
@UiThread
private void enforceRoundedCorners() {
if (mEnforcedCornerRadius <= 0 || !RoundedCornerEnforcement.isRoundedCornerEnabled()) {
resetRoundedCorners();
return;
}
View background = RoundedCornerEnforcement.findBackground(this);
if (background == null
|| RoundedCornerEnforcement.hasAppWidgetOptedOut(this, background)) {
resetRoundedCorners();
return;
}
RoundedCornerEnforcement.computeRoundedRectangle(this,
background,
mEnforcedRectangle);
setOutlineProvider(mCornerRadiusEnforcementOutline);
setClipToOutline(true);
}
/** Returns the corner radius currently enforced, in pixels. */
public float getEnforcedCornerRadius() {
return mEnforcedCornerRadius;
}
/** Returns true if the corner radius are enforced for this App Widget. */
public boolean hasEnforcedCornerRadius() {
return getClipToOutline();
}
}
@@ -20,19 +20,16 @@ import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Handler;
import android.os.SystemClock;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.AdapterView;
import android.widget.Advanceable;
@@ -40,7 +37,6 @@ import android.widget.RemoteViews;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.launcher3.CheckLongPressHelper;
import com.android.launcher3.Launcher;
@@ -51,7 +47,6 @@ import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener;
import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
@@ -61,7 +56,7 @@ import java.util.List;
/**
* {@inheritDoc}
*/
public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView
implements TouchCompleteListener, View.OnLongClickListener,
LocalColorExtractor.Listener {
@@ -76,8 +71,6 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
// Maximum duration for which updates can be deferred.
private static final long UPDATE_LOCK_TIMEOUT_MILLIS = 1000;
protected final LayoutInflater mInflater;
private final CheckLongPressHelper mLongPressHelper;
protected final Launcher mLauncher;
private final Workspace mWorkspace;
@@ -101,18 +94,6 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
private final Rect mWidgetSizeAtDrag = new Rect();
private final RectF mTempRectF = new RectF();
private final Rect mEnforcedRectangle = new Rect();
private final float mEnforcedCornerRadius;
private final ViewOutlineProvider mCornerRadiusEnforcementOutline = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
if (mEnforcedRectangle.isEmpty() || mEnforcedCornerRadius <= 0) {
outline.setEmpty();
} else {
outline.setRoundRect(mEnforcedRectangle, mEnforcedCornerRadius);
}
}
};
private final Object mUpdateLock = new Object();
private final ViewGroupFocusHelper mDragLayerRelativeCoordinateHelper;
private long mDeferUpdatesUntilMillis = 0;
@@ -126,18 +107,15 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
mLauncher = Launcher.getLauncher(context);
mWorkspace = mLauncher.getWorkspace();
mLongPressHelper = new CheckLongPressHelper(this, this);
mInflater = LayoutInflater.from(context);
setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
setBackgroundResource(R.drawable.widget_internal_focus_bg);
setExecutor(Executors.THREAD_POOL_EXECUTOR);
if (Utilities.ATLEAST_Q && Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText)) {
setOnLightBackground(true);
}
mColorExtractor = LocalColorExtractor.newInstance(getContext());
mColorExtractor.setListener(this);
mEnforcedCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(getContext());
mDragLayerRelativeCoordinateHelper = new ViewGroupFocusHelper(mLauncher.getDragLayer());
}
@@ -168,11 +146,6 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
return true;
}
@Override
protected View getErrorView() {
return mInflater.inflate(R.layout.appwidget_error, this, false);
}
@Override
public void updateAppWidget(RemoteViews remoteViews) {
synchronized (mUpdateLock) {
@@ -329,28 +302,12 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
}
}
public void switchToErrorView() {
// Update the widget with 0 Layout id, to reset the view to error view.
updateAppWidget(new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0));
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
try {
super.onLayout(changed, left, top, right, bottom);
} catch (final RuntimeException e) {
post(new Runnable() {
@Override
public void run() {
switchToErrorView();
}
});
}
super.onLayout(changed, left, top, right, bottom);
mIsScrollable = checkScrollableRecursively(this);
updateColorExtraction();
enforceRoundedCorners();
}
/** Starts the drag mode. */
@@ -564,40 +521,4 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
}
return false;
}
@UiThread
private void resetRoundedCorners() {
setOutlineProvider(ViewOutlineProvider.BACKGROUND);
setClipToOutline(false);
}
@UiThread
private void enforceRoundedCorners() {
if (mEnforcedCornerRadius <= 0 || !RoundedCornerEnforcement.isRoundedCornerEnabled()) {
resetRoundedCorners();
return;
}
View background = RoundedCornerEnforcement.findBackground(this);
if (background == null
|| RoundedCornerEnforcement.hasAppWidgetOptedOut(this, background)) {
resetRoundedCorners();
return;
}
RoundedCornerEnforcement.computeRoundedRectangle(this,
background,
mEnforcedRectangle);
setOutlineProvider(mCornerRadiusEnforcementOutline);
setClipToOutline(true);
}
/** Returns the corner radius currently enforced, in pixels. */
public float getEnforcedCornerRadius() {
return mEnforcedCornerRadius;
}
/** Returns true if the corner radius are enforced for this App Widget. */
public boolean hasEnforcedCornerRadius() {
return getClipToOutline();
}
}
@@ -26,7 +26,6 @@ import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Reorderable;
import com.android.launcher3.dragndrop.DraggableView;
@@ -59,7 +58,7 @@ public abstract class NavigableAppWidgetHostView extends AppWidgetHostView
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mChildrenFocused;
protected final BaseActivity mActivity;
protected final ActivityContext mActivity;
public NavigableAppWidgetHostView(Context context) {
super(context);
@@ -268,8 +268,8 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
if (availableWidth > 0) {
// Recreate the setup text.
mSetupTextLayout = new StaticLayout(
getResources().getText(R.string.gadget_setup_text), mPaint, availableWidth,
Layout.Alignment.ALIGN_CENTER, 1, 0, true);
getResources().getText(R.string.gadget_complete_setup_text), mPaint,
availableWidth, Layout.Alignment.ALIGN_CENTER, 1, 0, true);
int textHeight = mSetupTextLayout.getHeight();
// Extra icon size due to the setting icon