Commit 436f044b authored by Amit Kumar's avatar Amit Kumar 💻
Browse files

Add notification badge feature

parent 04905aec
Pipeline #128421 passed with stage
in 4 minutes and 47 seconds
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>
\ No newline at end of file
......@@ -99,7 +99,7 @@ public class ActivityManagerCompatVQ extends ActivityManagerCompat {
@Override
public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException {
ActivityTaskManager.getService().registerTaskStackListener(listener);
//ActivityTaskManager.getService().registerTaskStackListener(listener);
}
@Override
......
......@@ -27,6 +27,8 @@ import android.os.Build;
import android.view.ViewConfiguration;
import android.view.WindowManagerPolicyConstants;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.shared.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.StringJoiner;
......@@ -222,7 +224,7 @@ public class QuickStepContract {
*/
public static float getWindowCornerRadius(Resources resources) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return ScreenDecorationsUtils.getWindowCornerRadius(resources);
return resources.getDimension(R.dimen.rounded_corner);
} else {
return 0;
}
......@@ -233,7 +235,7 @@ public class QuickStepContract {
*/
public static boolean supportsRoundedCornersOnWindows(Resources resources) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return ScreenDecorationsUtils.supportsRoundedCornersOnWindows(resources);
return true;
} else {
return true;
}
......
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="rounded_corner">25px</dimen>
</resources>
\ No newline at end of file
......@@ -124,7 +124,7 @@
android:theme="@style/WidgetPickerDialog" />
<service
android:name=".features.notification.NotificationService"
android:name=".features.notification.NotificationListener"
android:enabled="true"
android:label="@string/icon_badging_service_title"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
......
......@@ -45,7 +45,9 @@ import foundation.e.blisslauncher.core.touch.ItemLongClickListener;
import foundation.e.blisslauncher.core.utils.Constants;
import foundation.e.blisslauncher.core.utils.GraphicsUtil;
import foundation.e.blisslauncher.core.utils.LongArrayMap;
import foundation.e.blisslauncher.core.utils.PackageUserKey;
import foundation.e.blisslauncher.features.launcher.Hotseat;
import foundation.e.blisslauncher.features.notification.FolderDotInfo;
import foundation.e.blisslauncher.features.test.Alarm;
import foundation.e.blisslauncher.features.test.CellLayout;
import foundation.e.blisslauncher.features.test.IconTextView;
......@@ -65,7 +67,10 @@ import foundation.e.blisslauncher.features.test.dragndrop.DropTarget;
import foundation.e.blisslauncher.features.test.dragndrop.SpringLoadedDragController;
import foundation.e.blisslauncher.features.test.graphics.DragPreviewProvider;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import org.jetbrains.annotations.NotNull;
public class LauncherPagedView extends PagedView<PageIndicatorDots> implements View.OnTouchListener,
......@@ -1022,7 +1027,7 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
private boolean isScrollingOverlay() {
return mLauncherOverlay != null &&
((mIsRtl && getUnboundedScrollX() > mMaxScroll)
|| (!mIsRtl && getUnboundedScrollX() <mMinScroll));
|| (!mIsRtl && getUnboundedScrollX() < mMinScroll));
}
@Override
......@@ -2219,6 +2224,20 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
return null;
}
/**
* Map the operator over the shortcuts and widgets, return the first-non-null value.
*
* @param recurse true: iterate over folder children. false: op get the folders themselves.
* @param op the operator to map over the shortcuts
*/
public void mapOverItems(boolean recurse, ItemOperator op) {
for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) {
if (mapOverCellLayout(recurse, layout, op)) {
return;
}
}
}
private boolean mapOverCellLayout(boolean recurse, CellLayout layout, ItemOperator op) {
// TODO(b/128460496) Potential race condition where layout is not yet loaded
if (layout == null) {
......@@ -2249,6 +2268,36 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
return false;
}
public void updateNotificationBadge(Predicate<PackageUserKey> updatedDots) {
final PackageUserKey packageUserKey = new PackageUserKey(null, null);
final Set<String> folderIds = new HashSet<>();
mapOverItems(MAP_RECURSE, (info, v) -> {
if ((info instanceof ApplicationItem || info instanceof ShortcutItem) && v instanceof IconTextView) {
if (!packageUserKey.updateFromItemInfo(info)
|| updatedDots.test(packageUserKey)) {
((IconTextView) v).applyDotState(info, true /* animate */);
folderIds.add(String.valueOf(info.container));
}
}
// process all the shortcuts
return false;
});
// Update folder icons
mapOverItems(MAP_NO_RECURSE, (info, v) -> {
if (info instanceof FolderItem && folderIds.contains(info.id)
&& v instanceof IconTextView) {
FolderDotInfo folderDotInfo = new FolderDotInfo();
for (LauncherItem si : ((FolderItem) info).items) {
folderDotInfo.addDotInfo(mLauncher.getDotInfoForItem(si));
}
((IconTextView) v).applyDotState(info, true /* animate */);
}
// process all the shortcuts
return false;
});
}
/**
* The overlay scroll is being controlled locally, just update our overlay effect
*/
......
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package foundation.e.blisslauncher.core.utils;
import java.util.Arrays;
/**
* Copy of the platform hidden implementation of android.util.IntArray.
* Implements a growing array of int primitives.
*/
public class IntArray implements Cloneable {
private static final int MIN_CAPACITY_INCREMENT = 12;
private static final int[] EMPTY_INT = new int[0];
/* package private */ int[] mValues;
/* package private */ int mSize;
private IntArray(int[] array, int size) {
mValues = array;
mSize = size;
}
/**
* Creates an empty IntArray with the default initial capacity.
*/
public IntArray() {
this(10);
}
/**
* Creates an empty IntArray with the specified initial capacity.
*/
public IntArray(int initialCapacity) {
if (initialCapacity == 0) {
mValues = EMPTY_INT;
} else {
mValues = new int[initialCapacity];
}
mSize = 0;
}
/**
* Creates an IntArray wrapping the given primitive int array.
*/
public static IntArray wrap(int... array) {
return new IntArray(array, array.length);
}
/**
* Appends the specified value to the end of this array.
*/
public void add(int value) {
add(mSize, value);
}
/**
* Inserts a value at the specified position in this array. If the specified index is equal to
* the length of the array, the value is added at the end.
*
* @throws IndexOutOfBoundsException when index &lt; 0 || index &gt; size()
*/
public void add(int index, int value) {
ensureCapacity(1);
int rightSegment = mSize - index;
mSize++;
checkBounds(mSize, index);
if (rightSegment != 0) {
// Move by 1 all values from the right of 'index'
System.arraycopy(mValues, index, mValues, index + 1, rightSegment);
}
mValues[index] = value;
}
/**
* Adds the values in the specified array to this array.
*/
public void addAll(IntArray values) {
final int count = values.mSize;
ensureCapacity(count);
System.arraycopy(values.mValues, 0, mValues, mSize, count);
mSize += count;
}
/**
* Sets the array to be same as {@param other}
*/
public void copyFrom(IntArray other) {
clear();
addAll(other);
}
/**
* Ensures capacity to append at least <code>count</code> values.
*/
private void ensureCapacity(int count) {
final int currentSize = mSize;
final int minCapacity = currentSize + count;
if (minCapacity >= mValues.length) {
final int targetCap = currentSize + (currentSize < (MIN_CAPACITY_INCREMENT / 2) ?
MIN_CAPACITY_INCREMENT : currentSize >> 1);
final int newCapacity = targetCap > minCapacity ? targetCap : minCapacity;
final int[] newValues = new int[newCapacity];
System.arraycopy(mValues, 0, newValues, 0, currentSize);
mValues = newValues;
}
}
/**
* Removes all values from this array.
*/
public void clear() {
mSize = 0;
}
@Override
public IntArray clone() {
return wrap(toArray());
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof IntArray) {
IntArray arr = (IntArray) obj;
if (mSize == arr.mSize) {
for (int i = 0; i < mSize; i++) {
if (arr.mValues[i] != mValues[i]) {
return false;
}
}
return true;
}
}
return false;
}
/**
* Returns the value at the specified position in this array.
*/
public int get(int index) {
checkBounds(mSize, index);
return mValues[index];
}
/**
* Sets the value at the specified position in this array.
*/
public void set(int index, int value) {
checkBounds(mSize, index);
mValues[index] = value;
}
/**
* Returns the index of the first occurrence of the specified value in this
* array, or -1 if this array does not contain the value.
*/
public int indexOf(int value) {
final int n = mSize;
for (int i = 0; i < n; i++) {
if (mValues[i] == value) {
return i;
}
}
return -1;
}
public boolean contains(int value) {
return indexOf(value) >= 0;
}
public boolean isEmpty() {
return mSize == 0;
}
/**
* Removes the value at the specified index from this array.
*/
public void removeIndex(int index) {
checkBounds(mSize, index);
System.arraycopy(mValues, index + 1, mValues, index, mSize - index - 1);
mSize--;
}
/**
* Removes the values if it exists
*/
public void removeValue(int value) {
int index = indexOf(value);
if (index >= 0) {
removeIndex(index);
}
}
/**
* Removes the values if it exists
*/
public void removeAllValues(IntArray values) {
for (int i = 0; i < values.mSize; i++) {
removeValue(values.mValues[i]);
}
}
/**
* Returns the number of values in this array.
*/
public int size() {
return mSize;
}
/**
* Returns a new array with the contents of this IntArray.
*/
public int[] toArray() {
return mSize == 0 ? EMPTY_INT : Arrays.copyOf(mValues, mSize);
}
/**
* Returns a comma separate list of all values.
*/
public String toConcatString() {
StringBuilder b = new StringBuilder();
for (int i = 0; i < mSize ; i++) {
if (i > 0) {
b.append(", ");
}
b.append(mValues[i]);
}
return b.toString();
}
/**
* Throws {@link ArrayIndexOutOfBoundsException} if the index is out of bounds.
*
* @param len length of the array. Must be non-negative
* @param index the index to check
* @throws ArrayIndexOutOfBoundsException if the {@code index} is out of bounds of the array
*/
private static void checkBounds(int len, int index) {
if (index < 0 || len <= index) {
throw new ArrayIndexOutOfBoundsException("length=" + len + "; index=" + index);
}
}
}
\ No newline at end of file
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package foundation.e.blisslauncher.core.utils;
import java.util.Arrays;
/**
* A wrapper over IntArray implementing a growing set of int primitives.
*/
public class IntSet {
final IntArray mArray = new IntArray();
/**
* Appends the specified value to the set if it does not exist.
*/
public void add(int value) {
int index = Arrays.binarySearch(mArray.mValues, 0, mArray.mSize, value);
if (index < 0) {
mArray.add(-index - 1, value);
}
}
public boolean contains(int value) {
return Arrays.binarySearch(mArray.mValues, 0, mArray.mSize, value) >= 0;
}
public boolean isEmpty() {
return mArray.isEmpty();
}
/**
* Returns the number of values in this set.
*/
public int size() {
return mArray.size();
}
public void clear() {
mArray.clear();
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
return (obj instanceof IntSet) && ((IntSet) obj).mArray.equals(mArray);
}
public IntArray getArray() {
return mArray;
}
/**
* Sets this set to be same as {@param other}
*/
public void copyFrom(IntSet other) {
mArray.copyFrom(other.mArray);
}
public static IntSet wrap(IntArray array) {
IntSet set = new IntSet();
set.mArray.addAll(array);
Arrays.sort(set.mArray.mValues, 0, set.mArray.mSize);
return set;
}
}
package foundation.e.blisslauncher.core.utils;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import java.util.Arrays;
import foundation.e.blisslauncher.core.database.model.LauncherItem;
import foundation.e.blisslauncher.features.shortcuts.DeepShortcutManager;
/** Creates a hash key based on package name and user. */
public class PackageUserKey {
public String mPackageName;
public UserHandle mUser;
private int mHashCode;
public static PackageUserKey fromItemInfo(LauncherItem info) {
return new PackageUserKey(info.getTargetComponent().getPackageName(), info.user.getRealHandle());
}
public static PackageUserKey fromNotification(StatusBarNotification notification) {
return new PackageUserKey(notification.getPackageName(), notification.getUser());
}
public PackageUserKey(String packageName, UserHandle user) {
update(packageName, user);
}
private void update(String packageName, UserHandle user) {
mPackageName = packageName;
mUser = user;
mHashCode = Arrays.hashCode(new Object[] {packageName, user});
}
/**
* This should only be called to avoid new object creations in a loop.
* @return Whether this PackageUserKey was successfully updated - it shouldn't be used if not.
*/
public boolean updateFromItemInfo(LauncherItem info) {
if (DeepShortcutManager.supportsShortcuts(info)) {
update(info.getTargetComponent().getPackageName(), info.user.getRealHandle());
return true;
}
return false;
}
@Override
public int hashCode() {
return mHashCode;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof PackageUserKey)) return false;
PackageUserKey otherKey = (PackageUserKey) obj;
return mPackageName.equals(otherKey.mPackageName) && mUser.equals(otherKey.mUser);
}
}
/*
* 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 foundation.e.blisslauncher.core.utils;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;