添加口袋模式

口袋模式

之前有做过一个给手机添加口袋模式的功能,在settings里做一个控制开关,现在回顾记录一下。

先加个Setting secure属性值用于记录口袋模式状态
frameworks/base / core/java/android/provider/Settings.java
/**
         * DISABLE POCKET MODE
         * 1 don't do close proximity detection on lock screen
         * 0 do close proximity detection on lock screen
         * @hide
         */
        @Readable
        public static final String DISABLE_POCKET_MODE = "disable_pocket_mode";
        
frameworks/base / packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
Settings.Secure.DISABLE_POCKET_MODE,

frameworks/base / packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
VALIDATORS.put(Secure.DISABLE_POCKET_MODE, BOOLEAN_VALIDATOR);

frameworks/base / packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
loadSecureSettings:
//disable pocket mode
            loadBooleanSetting(stmt, Settings.Secure.DISABLE_POCKET_MODE,
                      R.bool.def_disable_pocket_mode);

frameworks/base / packages/SettingsProvider/res/values/defaults.xml
<!-- Default for Settings.Secure.DISABLE_POCKET_MODE -->
    <bool name="def_disable_pocket_mode">false</bool>
    
frameworks/base / services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
SECURE_SETTINGS_DEVICEOWNER_ALLOWLIST.add(Settings.Secure.DISABLE_POCKET_MODE);

在SystemUI里监听P-sensor,设置个遮盖页面
frameworks/base / packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
import com.android.systemui.shade.NotificationPanelView;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.widget.RelativeLayout;

private static final int HINT_RESET_DELAY_MS = 1200;
+private static final int PROXIMITY_DEBOUNCE_MS = 30;

protected NotificationPanelViewController mNotificationPanelViewController;
+protected RelativeLayout mProximityDetectedView;
+protected NotificationPanelView mNotificationPanelView;

makeStatusBarView:
    mAmbientIndicationContainer = mNotificationShadeWindowView.findViewById(
                R.id.ambient_indication_container);
    +mProximityDetectedView = mNotificationShadeWindowView.findViewById(
                R.id.proximity_view);
    +mNotificationPanelView = mNotificationShadeWindowView.findViewById(
                R.id.notification_panel);
                
showKeyguard:
    updateIsKeyguard();
    +enableProximityDetectionIfNecessary();

hideKeyguardImpl:
    +disableProximityDetectionState();
    +restoreProximityWakelockIfNecessary();
    Trace.endSection();

    private Handler proximityDebounceHandler = new Handler();
    private SensorEventListener sensorEventListener = new SensorEventListener() {
        @Override
        public final void onAccuracyChanged(Sensor sensor, int accuracy) {
        }

        @Override
        public final void onSensorChanged(SensorEvent event) {
            proximityDebounceHandler.removeCallbacksAndMessages(null);
            boolean isPn = isKeyguardProximityDetectionNecessary();
            if (!isPn) {
                disableProximityDetectionState();
                return;
            }

            final boolean proximityDetected = event.values[0] == 0.0;
            // For initial proximity state, set right awayvalue
            if (mProximityDetected == null) {
                setProximityDetectedShowing(proximityDetected);
                return;
            }

            proximityDebounceHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    setProximityDetectedShowing(proximityDetected);
                }
            }, PROXIMITY_DEBOUNCE_MS);
        }
    };

    private SensorManager sensorManager;
    private Boolean mProximityDetected = null;
    private boolean mProximityListening;

    public boolean getProximityDetected() {
        return mProximityDetected == null ? false :mProximityDetected.booleanValue();
    }

    private void registerSensorListener() {
        if (mProximityListening) return;
        sensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
        Sensor proximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
        sensorManager.registerListener(sensorEventListener, proximity, SensorManager.SENSOR_DELAY_NORMAL);
        mProximityListening = true;
    }

    private boolean isKeyguardProximityDetectionNecessary() {
        boolean isDreaming = false;
        try {
            isDreaming = mDreamManager.isDreaming();
        } catch (RemoteException e) {
        }
        return
                isKeyguardShowing()
                        && mExpandedVisible  //mStatusBarWindowController.isCurrentlyExpanded()
                        && !mDozing
                        && !mDozeServiceHost.getDozingRequested()//mDozingRequested
                        && !isGoingToSleepOrAsleep()
                        && !isDreaming
                        && mDeviceProvisionedController.isDeviceProvisioned()
                        && (mUserSetup || mUserSwitcherController == null || !mUserSwitcherController.isSimpleUserSwitcher());
    }

    private void enableProximityDetectionIfNecessary() {
        int pocket_mode = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.DISABLE_POCKET_MODE, 0);
        if (pocket_mode == 0){
            return;
        }

        if (isKeyguardProximityDetectionNecessary()) {
            registerSensorListener();
            mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER");
        }
    }

    private void restoreProximityWakelockIfNecessary() {
        boolean isDreaming = false;
        try {
            isDreaming = mDreamManager.isDreaming();
        } catch (RemoteException e) {
        }
        if (!isGoingToSleepOrAsleep() && !mDozing && !mDozeServiceHost.getDozingRequested()/*mDozingRequested*/ && !isDreaming) {
            mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_UNKNOWN, "android.policy:RESTORE_PROXIMITY_WAKEFULNESS");
        }
    }

    private void unregisterSensorListener() {
        mProximityDetected = null;
        if (!mProximityListening) return;
        if (sensorManager == null) return;
        try {
            sensorManager.unregisterListener(sensorEventListener);
        } catch (Exception e) {
        }
        mProximityListening = false;
    }

    private void disableProximityDetectionState() {
        unregisterSensorListener();
        proximityDebounceHandler.removeCallbacksAndMessages(null);
        setProximityDetectedShowing(false);
    }

    private void setProximityDetectedShowing(boolean show) {
        mProximityDetected = show;
        if (show) {
            onBackPressed();
            mNotificationPanelView.setVisibility(View.GONE);
            mNotificationPanelView.setAlpha(0f);
            mProximityDetectedView.setVisibility(View.VISIBLE);
        } else {
            mNotificationPanelView.setAlpha(1f);
            mNotificationPanelView.setVisibility(View.VISIBLE);
            mProximityDetectedView.setVisibility(View.GONE);
        }
    }

onFinishedWakingUp:
    mWakeUpCoordinator.setWakingUp(false);
    +enableProximityDetectionIfNecessary();
    
onScreenTurnedOff:
    +disableProximityDetectionState();

isGoingToSleep:
    private boolean isGoingToSleepOrAsleep() {
        int wakefulness = mWakefulnessLifecycle.getWakefulness();
        return wakefulness == WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP || wakefulness == WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
    }


frameworks/base / packages/SystemUI/res/layout/status_bar_proximity_detected.xml
+xml:
<?xml version="1.0" encoding="utf-8"?>

<!-- This is the view that appears when proximity is detected on the lock screen -->
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:sysui="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent"
    android:id="@+id/proximity_view"
  >
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:textColor="?attr/wallpaperTextColor"
        android:text="@string/proximity_detected"
    />
</RelativeLayout>

frameworks/base / packages/SystemUI/res/layout/super_notification_shade.xml
    <include layout="@layout/status_bar_expanded"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="invisible" />

    +<include layout="@layout/status_bar_proximity_detected"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone" />

frameworks/base / packages/SystemUI/res/values/strings.xml
    +<string name="proximity_detected">Close proximity detected</string>
    
在Settings里添加一个开关
+packages/apps/Settings / src/com/android/settings/display/PocketModePreferenceController.java

package com.android.settings.display;

import static android.provider.Settings.Secure.DISABLE_POCKET_MODE;

import android.content.Context;
import android.provider.Settings;

import androidx.preference.Preference;
import androidx.preference.SwitchPreference;

import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;


public class PocketModePreferenceController extends AbstractPreferenceController implements
        PreferenceControllerMixin, Preference.OnPreferenceChangeListener {

    private static final String KEY_POCKET_MODE = "pocket_mode";

    public PocketModePreferenceController(Context context) {
        super(context);
    }

    @Override
    public boolean isAvailable() {
        return true;
    }

    @Override
    public String getPreferenceKey() {
        return KEY_POCKET_MODE;
    }

    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        boolean value = (Boolean) newValue;
        Settings.Secure.putInt(mContext.getContentResolver(), DISABLE_POCKET_MODE, value ? 1 : 0);
        return true;
    }

    @Override
    public void updateState(Preference preference) {
        int value = Settings.Secure.getInt(mContext.getContentResolver(), DISABLE_POCKET_MODE, 0);
        ((SwitchPreference) preference).setChecked(value != 0);
    }
}

packages/apps/Settings / src/com/android/settings/DisplaySettings.java
    +import com.android.settings.display.PocketModePreferenceController;
buildPreferenceControllers:
    +controllers.add(new PocketModePreferenceController(context));

packages/apps/Settings / res/xml/display_settings.xml
    <SwitchPreference
        android:key="pocket_mode"
        android:title="@string/pocket_mode"/>

packages/apps/Settings / res/values/strings.xml
   <string name="pocket_mode">Pocket mode</string>

到这里一个简单的pocket mode算做好了。
然而客户想整个滑动的动画去控制以及来电话时的遮盖处理等,所以后续又升级了一下:

frameworks/base / packages/SystemUI/res/layout/status_bar_proximity_detected.xml
<?xml version="1.0" encoding="utf-8"?>

<!-- This is the view that appears when proximity is detected on the lock screen -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:sysui="http://schemas.android.com/apk/res-auto"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/proximity_view"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="center"
        android:layout_marginTop="100dp"
        android:textColor="?attr/wallpaperTextColor"
        android:text="@string/proximity_top"
        android:textSize="22sp"
        android:singleLine="true"
        android:ellipsize="end"
    />

    <ImageView
        android:id="@+id/pocket_mode_iv"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="8"
        android:gravity="center"
        android:layout_marginTop="15dp"
        android:background="@android:color/transparent"
        android:src="@mipmap/pocket_mode"
        />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="center"
        android:layout_marginTop="20dp"
        android:textColor="?attr/wallpaperTextColor"
        android:text="@string/proximity_mode"
        android:textSize="18sp"
        android:singleLine="true"
        android:ellipsize="end"
    />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="center"
        android:layout_marginTop="10dp"
        android:textColor="?attr/wallpaperTextColor"
        android:text="@string/proximity_action"
        android:textSize="16sp"
        android:singleLine="true"
        android:ellipsize="end"
    />


    <com.android.systemui.ripple.SpreadView
        android:id="@+id/spreadview"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        app:spread_center_color="#31DE80"
        app:spread_delay_milliseconds="800"
        app:spread_distance="3"
        app:spread_max_radius="30"
        app:spread_radius="70"
        android:layout_gravity="center"
        app:spread_spread_color="#fff" />

</LinearLayout>

frameworks/base / packages/SystemUI/res/mipmap-mdpi/pocket_mode.png
这个图根据需求来

自定义动画:frameworks/base / packages/SystemUI/src/com/android/systemui/ripple/SpreadView.java
package com.android.systemui.ripple;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.View;
import android.annotation.Nullable;
import java.util.List;
import java.util.ArrayList;
import android.content.res.TypedArray;
import com.android.systemui.R;
import android.util.Log;
import android.view.MotionEvent;
import android.provider.Settings;
import android.os.UserHandle;
import android.content.Intent;

/**
 * author : suntianhai
 * e-mail : tianhai.sun@t2mobile.com
 * time   : 2023/09/04
 * desc   : add for pocket mode
 * version: 1.0
 */
public class SpreadView extends View {

    private Paint centerPaint;
    private int radius = 100;
    private Paint spreadPaint;
    private float centerX;
    private float centerY;
    private int distance = 3;
    private int maxRadius = 50;
    private int delayMilliseconds = 100;
    private List<Integer> spreadRadius = new ArrayList<>();
    private List<Integer> alphas = new ArrayList<>();

    public SpreadView(Context context) {
        this(context,null,0);
    }

    public SpreadView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public SpreadView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SpreadView, defStyleAttr, 0);
        radius = a.getInt(R.styleable.SpreadView_spread_radius, radius);
        maxRadius = a.getInt(R.styleable.SpreadView_spread_max_radius, maxRadius);
        int centerColor = a.getColor(R.styleable.SpreadView_spread_center_color, android.R.attr.colorAccent);
        int spreadColor = a.getColor(R.styleable.SpreadView_spread_spread_color, android.R.attr.colorAccent);
        distance = a.getInt(R.styleable.SpreadView_spread_distance, distance);
        a.recycle();
        centerPaint = new Paint();
        centerPaint.setColor(centerColor);
        centerPaint.setAntiAlias(true);

        alphas.add(255);
        spreadRadius.add(0);
        spreadPaint = new Paint();
        spreadPaint.setAntiAlias(true);
        spreadPaint.setAlpha(255);
        spreadPaint.setColor(spreadColor);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        centerX = w / 2;
        centerY = h / 2;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < spreadRadius.size(); i++) {
        int alpha = alphas.get(i);
        spreadPaint.setAlpha(alpha);

        int width = spreadRadius.get(i);
        canvas.drawCircle(centerX, centerY, radius + width, spreadPaint);

        if (alpha > 0 && width < 200) {
            alpha = (alpha - distance) > 0 ? (alpha - distance) : 1;
            alphas.set(i, alpha);
            spreadRadius.set(i, width + distance);
            }
        }
        if (spreadRadius.get(spreadRadius.size() - 1) > maxRadius) {
            spreadRadius.add(0);
            alphas.add(255);
        }
        if (spreadRadius.size()  > 2) {
            alphas.remove(0);
            spreadRadius.remove(0);
        }
        canvas.drawCircle(centerX, centerY, radius, centerPaint);
        postInvalidateDelayed(delayMilliseconds);
    }

    private int lastX;
    private int lastY;
    private boolean closePS = false;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastX = x;
                lastY = y;
                break;
            case MotionEvent.ACTION_UP:
                if(closePS){
                    closePS = false;
                    layout(0 , 2351, 1224 , 2700);
                    Settings.Global.putStringForUser(getContext().getContentResolver(),
                    Settings.Global.UPDATE_BATTERY_CHARGING_MODE, System.currentTimeMillis() + "",
                    UserHandle.myUserId());
                }
                break;
            case MotionEvent.ACTION_MOVE:
                int offsetX = x - lastX;
                int offsetY = y - lastY;
                layout(getLeft() + offsetX, getTop(), getRight() + offsetX, getBottom());

                if(Math.abs(offsetX) > 20){
                    closePS = true;
                }
            break;
        }
        return true;
    }

}


frameworks/base / packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java

import android.widget.LinearLayout;
import android.database.ContentObserver;
import android.provider.Settings.Global;
import com.android.systemui.ripple.SpreadView;
import android.telephony.TelephonyManager;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import android.view.Window;
import android.app.Dialog;
import android.view.WindowManager.LayoutParams;
import android.view.LayoutInflater;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;

private NotificationListContainer mNotifListContainer;

    +private boolean isDismissDialog = false;
    +final ContentObserver mDisableProximityObserver = new ContentObserver(new Handler()) {
        @Override
        public void onChange(boolean selfChange, Uri uri) {
            isDismissDialog = true;
            disableProximityDetectionState();
        }
    };

    protected LinearLayout mProximityDetectedView;
    protected View spreadview;
    protected SystemUIDialog mPocketModeDialog;

start:
    +mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);

    mContext.getContentResolver().registerContentObserver(
                Global.getUriFor(Global.UPDATE_BATTERY_CHARGING_MODE),
                false,
                mDisableProximityObserver);

        mNotificationPanelView = mNotificationShadeWindowView.findViewById(
                R.id.notification_panel);
        +spreadview = mProximityDetectedView.findViewById(
                R.id.spreadview);
         
@Override
        public final void onSensorChanged(SensorEvent event) {
            proximityDebounceHandler.removeCallbacksAndMessages(null);
            boolean isPn = isKeyguardProximityDetectionNecessary();
            +int callState = mTelephonyManager.getCallState();
            if (!isPn && callState != 1) {
                disableProximityDetectionState();
                return;
            }
            ...

private void setProximityDetectedShowing(boolean show) {
        int callState = mTelephonyManager.getCallState();
        mProximityDetected = show;
        if (show) {
            onBackPressed();
            mNotificationPanelView.setAlpha(0f);
            mNotificationPanelView.setVisibility(View.INVISIBLE);
            if (callState == 1) {
                showDialog();
            }else{
                proximity_top.setText(R.string.proximity_top);
                proximity_mode.setText(R.string.proximity_mode);
                proximity_action.setText(R.string.proximity_action);
                mProximityDetectedView.setVisibility(View.VISIBLE);
                mProximityDetectedView.setAlpha(1f);
                mProximityDetectedView.setBackgroundDrawable(mWallpaperManager.getDrawable());
            }
        } else {
            mNotificationPanelView.setVisibility(View.VISIBLE);
            mNotificationPanelView.setAlpha(1f);
            if (callState == 1 && mPocketModeDialog != null) {
                mPocketModeDialog.dismiss();
                mPocketModeDialog = null;
            }else{
                mProximityDetectedView.setVisibility(View.INVISIBLE);
                mProximityDetectedView.setAlpha(0f);
            }
            isDismissDialog = false;
        }
    }

    private void showDialog(){
        if (mPocketModeDialog != null) {
            return;
        }
        mPocketModeDialog = new SystemUIDialog(mContext,R.style.PocketModeDialog_Fullscreen);
        View view = View.inflate(mContext, R.layout.status_bar_proximity_detected, null);
        view.setBackgroundDrawable(mWallpaperManager.getDrawable());
        TextView top = view.findViewById(R.id.proximity_top);
        TextView mode = view.findViewById(R.id.proximity_mode);
        TextView action = view.findViewById(R.id.proximity_action);
        top.setText(R.string.proximity_top);
        mode.setText(R.string.proximity_mode);
        action.setText(R.string.proximity_action);

        mPocketModeDialog.setView(view);
        Window window = mPocketModeDialog.getWindow();
        if (window != null) {
            WindowManager.LayoutParams params = window.getAttributes();
            params.width = WindowManager.LayoutParams.MATCH_PARENT;
            params.height = WindowManager.LayoutParams.MATCH_PARENT;
            window.setAttributes(params);
        }
        // mPocketModeDialog.getWindow().setBackgroundDrawable(mWallpaperManager.getDrawable());
        mPocketModeDialog.setShowForAllUsers(true);
        mPocketModeDialog.show();
        window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
    }

private void unregisterSensorListener() {
        boolean flag = mKeyguardManager.inKeyguardRestrictedInputMode();
        int callState = mTelephonyManager.getCallState();
        int pocket_mode = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.DISABLE_POCKET_MODE, 0);
        if (flag && callState == 1 && pocket_mode == 1 && !isDismissDialog) {
            return;
        }
        ...

private TelephonyManager mTelephonyManager;
    protected KeyguardManager mKeyguardManager;


frameworks/base / packages/SystemUI/res/values/styles.xml
    <declare-styleable name="SpreadView">
        <!--中心圆颜色-->
        <attr name="spread_center_color" format="color"/>
        <!--中心圆半径-->
        <attr name="spread_radius" format="integer"/>
        <!--扩散圆颜色-->
        <attr name="spread_spread_color" format="color"/>
        <!--扩散间距-->
        <attr name="spread_distance" format="integer"/>
        <!--扩散最大半径-->
        <attr name="spread_max_radius" format="integer"/>
        <!--扩散延迟间隔-->
        <attr name="spread_delay_milliseconds" format="integer"/>
    </declare-styleable>
    
    <style name="PocketModeDialog_Fullscreen">
        <item name="android:windowFullscreen">true</item>
        <item name="android:windowNoTitle">true</item>
    </style>
    

frameworks/base / packages/SystemUI/res/values/strings.xml
    <string name="proximity_top">Don’t cover the top of the screen</string>
    <string name="proximity_mode">Pocket Mode</string>
    <string name="proximity_action">Swipe left or right to force exit</string>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值