Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/SystemUI/Android.bp
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ android_library {
"aconfig_settings_flags_lib",
"smartspace-proto-java",
"smartspace-proto-lite-java",
"vendor.lineage.powershare-V1-java",
],
javacflags: [
"-Aroom.schemaLocation=frameworks/base/packages/SystemUI/schemas",
Expand Down Expand Up @@ -810,6 +811,7 @@ android_library {
"aconfig_settings_flags_lib",
"kairos",
"displaylib",
"vendor.lineage.powershare-V1-java",
],
}

Expand Down
26 changes: 26 additions & 0 deletions packages/SystemUI/res/drawable/ic_qs_powershare.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!--
Copyright (C) 2020 The LineageOS Project

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">

<path
android:fillColor="#ffffff"
android:pathData="M19.77,7.23l0.01,-0.01 -3.72,-3.72L15,4.56l2.11,2.11c-0.94,0.36 -1.61,1.26 -1.61,2.33 0,1.38 1.12,2.5 2.5,2.5 0.36,0 0.69,-0.08 1,-0.21v7.21c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V14c0,-1.1 -0.9,-2 -2,-2h-1V5c0,-1.1 -0.9,-2 -2,-2H6c-1.1,0 -2,0.9 -2,2v16h10v-7.5h1.5v5c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5V9c0,-0.69 -0.28,-1.32 -0.73,-1.77zM18,10c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM8,18v-4.5H6L10,6v5h2l-4,7z" />

</vector>
2 changes: 1 addition & 1 deletion packages/SystemUI/res/values/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@

<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
internet,bt,flashlight,dnd,modes_dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling,record_issue,hearing_devices,notes,desktopeffects,nfc
internet,bt,flashlight,dnd,modes_dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling,record_issue,hearing_devices,notes,desktopeffects,nfc,powershare
</string>

<!-- The tiles to display in QuickSettings -->
Expand Down
6 changes: 6 additions & 0 deletions packages/SystemUI/res/values/lumine_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
<!-- USB tethering QS tile -->
<string name="quick_settings_usb_tether_label">USB tethering</string>

<!-- PowerShare QS tile -->
<string name="quick_settings_powershare_label">Wireless PowerShare</string>
<string name="quick_settings_powershare_off_powersave_label">Wireless PowerShare off\nBattery saver</string>
<string name="quick_settings_powershare_off_low_battery_label">Wireless PowerShare off\nBattery too low</string>
<string name="quick_settings_powershare_enabled_label">Wireless PowerShare is enabled</string>

<!-- Status bar network traffic monitor strings -->
<string name="kilobitspersecond_short">kb/s</string>
<string name="megabitspersecond_short">Mb/s</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import com.android.systemui.qs.tiles.HeadsUpTile
import com.android.systemui.qs.tiles.SyncTile
import com.android.systemui.qs.tiles.UsbTetherTile
import com.android.systemui.qs.tiles.WifiTileLegacy
import com.android.systemui.qs.tiles.PowerShareTile
import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig;
import com.android.systemui.qs.tiles.base.shared.model.QSTilePolicy;
import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig;
Expand Down Expand Up @@ -89,6 +90,12 @@ interface LumineModule {
@StringKey(WifiTileLegacy.TILE_SPEC)
fun bindWifiTileLegacy(wifiTileLegacy: WifiTileLegacy): QSTileImpl<*>

/** Inject PowerShareTile into tileMap in QSModule */
@Binds
@IntoMap
@StringKey(PowerShareTile.TILE_SPEC)
fun bindPowerShareTile(powerShareTile: PowerShareTile): QSTileImpl<*>

companion object {
@Provides
@IntoMap
Expand Down Expand Up @@ -209,5 +216,20 @@ interface LumineModule {
category = TileCategory.CONNECTIVITY
)
}

@Provides
@IntoMap
@StringKey(PowerShareTile.TILE_SPEC)
fun providePowerShareTileConfig(uiEventLogger: QsEventLogger): QSTileConfig {
return QSTileConfig(
tileSpec = TileSpec.create(PowerShareTile.TILE_SPEC),
uiConfig = QSTileUIConfig.Resource(
iconRes = R.drawable.ic_qs_powershare,
labelRes = R.string.quick_settings_powershare_label
),
instanceId = uiEventLogger.getNewInstanceId(),
category = TileCategory.UTILITIES
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
/*
* Copyright (C) 2020-2025 The LineageOS 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.systemui.qs.tiles;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Intent;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.Looper;
import android.os.ServiceManager;
import android.service.quicksettings.Tile;
import android.util.Log;

import androidx.annotation.Nullable;

import com.android.internal.logging.MetricsLogger;
import com.android.systemui.animation.Expandable;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.BatteryController;

import com.android.internal.logging.nano.MetricsProto.MetricsEvent;

import vendor.lineage.powershare.IPowerShare;

import javax.inject.Inject;

public class PowerShareTile extends QSTileImpl<BooleanState>
implements BatteryController.BatteryStateChangeCallback {

public static final String TILE_SPEC = "powershare";

private IPowerShare mPowerShare;
private BatteryController mBatteryController;
private NotificationManager mNotificationManager;
private Notification mNotification;
private static final String CHANNEL_ID = TILE_SPEC;
private static final int NOTIFICATION_ID = 273298;

@Nullable
private Icon mIcon = null;

@Inject
public PowerShareTile(
QSHost host,
QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
BatteryController batteryController
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mPowerShare = getPowerShare();
if (mPowerShare == null) {
return;
}

mBatteryController = batteryController;
mNotificationManager = mContext.getSystemService(NotificationManager.class);

NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID,
mContext.getString(R.string.quick_settings_powershare_label),
NotificationManager.IMPORTANCE_DEFAULT);
mNotificationManager.createNotificationChannel(notificationChannel);

Notification.Builder builder = new Notification.Builder(mContext, CHANNEL_ID);
builder.setContentTitle(
mContext.getString(R.string.quick_settings_powershare_enabled_label));
builder.setSmallIcon(R.drawable.ic_qs_powershare);
builder.setOnlyAlertOnce(true);
mNotification = builder.build();
mNotification.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
mNotification.visibility = Notification.VISIBILITY_PUBLIC;

batteryController.addCallback(this);
}

@Override
public void onPowerSaveChanged(boolean isPowerSave) {
refreshState();
}

@Override
public void refreshState() {
updatePowerShareState();

super.refreshState();
}

private void updatePowerShareState() {
if (!isAvailable()) {
return;
}

if (mBatteryController.isPowerSave()) {
try {
mPowerShare.setEnabled(false);
} catch (Exception ex) {
ex.printStackTrace();
}
}

try {
if (mPowerShare.isEnabled()) {
mNotificationManager.notify(NOTIFICATION_ID, mNotification);
} else {
mNotificationManager.cancel(NOTIFICATION_ID);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}

@Override
public boolean isAvailable() {
try {
// Will throw NPE or some other exception if HAL is either missing or broken.
mPowerShare.isEnabled();
return true;
} catch (Exception ex) {
return false;
}
}

@Override
public BooleanState newTileState() {
BooleanState state = new BooleanState();
state.handlesLongClick = false;
return state;
}

@Override
public void handleClick(@Nullable Expandable expandable) {
try {
mPowerShare.setEnabled(!mPowerShare.isEnabled());
refreshState();
} catch (Exception ex) {
ex.printStackTrace();
}
}

@Override
public Intent getLongClickIntent() {
return null;
}

@Override
public CharSequence getTileLabel() {
if (mBatteryController.isPowerSave()) {
return mContext.getString(R.string.quick_settings_powershare_off_powersave_label);
} else {
if (getBatteryLevel() < getMinBatteryLevel()) {
return mContext.getString(R.string.quick_settings_powershare_off_low_battery_label);
}
}

return mContext.getString(R.string.quick_settings_powershare_label);
}

@Override
protected void handleUpdateState(BooleanState state, Object arg) {
if (!isAvailable()) {
return;
}

if (mIcon == null) {
mIcon = maybeLoadResourceIcon(R.drawable.ic_qs_powershare);
}
state.icon = mIcon;
try {
state.value = mPowerShare.isEnabled();
} catch (Exception ex) {
state.value = false;
ex.printStackTrace();
}
state.label = mContext.getString(R.string.quick_settings_powershare_label);

if (mBatteryController.isPowerSave() || getBatteryLevel() < getMinBatteryLevel()) {
state.state = Tile.STATE_UNAVAILABLE;
} else if (!state.value) {
state.state = Tile.STATE_INACTIVE;
} else {
state.state = Tile.STATE_ACTIVE;
}
}

@Override
public int getMetricsCategory() {
return MetricsEvent.LUMINEDROID;
}

@Override
public void handleSetListening(boolean listening) {
}

private synchronized IPowerShare getPowerShare() {
final String fqName = IPowerShare.DESCRIPTOR + "/default";

try {
return IPowerShare.Stub.asInterface(ServiceManager.getService(fqName));
} catch (Exception e) {
// Handle both RemoteException and ServiceNotFoundException
Log.e(TAG, "Failed to get PowerShare service", e);
return null;
}
}

private int getMinBatteryLevel() {
try {
return mPowerShare.getMinBattery();
} catch (Exception ex) {
ex.printStackTrace();
}

return 0;
}

private int getBatteryLevel() {
BatteryManager bm = mContext.getSystemService(BatteryManager.class);
return bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
}
}