Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ebf2577f authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Basic workspace wireless charging animation"

parents 5511634f ae79ab9d
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -35,6 +35,8 @@ oneway interface IStatusBar
    void animateCollapsePanels();
    void togglePanel();

    void showChargingAnimation(int batteryLevel);

    /**
     * Notifies the status bar of a System UI visibility flag change.
     *
+56 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 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.
*/
-->

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- Circle animation -->
    <com.android.systemui.charging.WirelessChargingView
        android:id="@+id/wireless_charging_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:elevation="4dp"/>

    <!-- Text inside circle -->
    <LinearLayout
        android:id="@+id/wireless_charging_text_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:orientation="vertical">

        <TextView
            android:id="@+id/wireless_charging_percentage"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:textSize="32sp"
            android:textColor="?attr/wallpaperTextColor"/>

        <TextView
            android:id="@+id/wireless_charging_secondary_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:textColor="?attr/wallpaperTextColor"/>
    </LinearLayout>

</FrameLayout>
 No newline at end of file
+8 −0
Original line number Diff line number Diff line
@@ -895,4 +895,12 @@
    <dimen name="fingerprint_dialog_icon_size">44dp</dimen>
    <dimen name="fingerprint_dialog_fp_icon_size">60dp</dimen>
    <dimen name="fingerprint_dialog_animation_translation_offset">350dp</dimen>

    <!-- WirelessCharging Animation values -->
    <!-- Starting text size of batteryLevel for wireless charging animation -->
    <dimen name="config_batteryLevelTextSizeStart" format="float">5.0</dimen>
    <!-- Ending text size of batteryLevel for wireless charging animation -->
    <dimen name="config_batteryLevelTextSizeEnd" format="float">32.0</dimen>
    <!-- Wireless Charging battery level text animation duration -->
    <integer name="config_batteryLevelTextAnimationDuration">400</integer>
</resources>
+213 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.systemui.charging;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.util.Slog;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;

/**
 * A WirelessChargingAnimation is a view containing view + animation for wireless charging.
 * @hide
 */
public class WirelessChargingAnimation {

    public static final long DURATION = 1400;
    private static final String TAG = "WirelessChargingView";
    private static final boolean LOCAL_LOGV = false;

    private final WirelessChargingView mCurrentWirelessChargingView;
    private static WirelessChargingView mPreviousWirelessChargingView;

    /**
     * Constructs an empty WirelessChargingAnimation object.  If looper is null,
     * Looper.myLooper() is used.  Must set
     * {@link WirelessChargingAnimation#mCurrentWirelessChargingView}
     * before calling {@link #show} - can be done through {@link #makeWirelessChargingAnimation}.
     * @hide
     */
    public WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper, int
            batteryLevel) {
        mCurrentWirelessChargingView = new WirelessChargingView(context, looper,
                batteryLevel);
    }

    /**
     * Creates a wireless charging animation object populated with next view.
     * @hide
     */
    public static WirelessChargingAnimation makeWirelessChargingAnimation(@NonNull Context context,
            @Nullable Looper looper, int batteryLevel) {
        return new WirelessChargingAnimation(context, looper, batteryLevel);
    }

    /**
     * Show the view for the specified duration.
     */
    public void show() {
        if (mCurrentWirelessChargingView == null ||
                mCurrentWirelessChargingView.mNextView == null) {
            throw new RuntimeException("setView must have been called");
        }

        if (mPreviousWirelessChargingView != null) {
            mPreviousWirelessChargingView.hide(0);
        }

        mPreviousWirelessChargingView = mCurrentWirelessChargingView;
        mCurrentWirelessChargingView.show();
        mCurrentWirelessChargingView.hide(DURATION);
    }

    private static class WirelessChargingView {
        private static final int SHOW = 0;
        private static final int HIDE = 1;

        private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
        private final Handler mHandler;

        private int mGravity;

        private View mView;
        private View mNextView;
        private WindowManager mWM;

        public WirelessChargingView(Context context, @Nullable Looper looper, int batteryLevel) {
            mNextView = new WirelessChargingLayout(context, batteryLevel);
            mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;

            final WindowManager.LayoutParams params = mParams;
            params.height = WindowManager.LayoutParams.WRAP_CONTENT;
            params.width = WindowManager.LayoutParams.WRAP_CONTENT;
            params.format = PixelFormat.TRANSLUCENT;

            params.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
            params.setTitle("Charging Animation");
            params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                    | WindowManager.LayoutParams.FLAG_DIM_BEHIND;

            params.dimAmount = .3f;

            if (looper == null) {
                // Use Looper.myLooper() if looper is not specified.
                looper = Looper.myLooper();
                if (looper == null) {
                    throw new RuntimeException(
                            "Can't display wireless animation on a thread that has not called "
                                    + "Looper.prepare()");
                }
            }

            mHandler = new Handler(looper, null) {
                @Override
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                        case SHOW: {
                            handleShow();
                            break;
                        }
                        case HIDE: {
                            handleHide();
                            // Don't do this in handleHide() because it is also invoked by
                            // handleShow()
                            mNextView = null;
                            break;
                        }
                    }
                }
            };
        }

        public void show() {
            if (LOCAL_LOGV) Log.v(TAG, "SHOW: " + this);
            mHandler.obtainMessage(SHOW).sendToTarget();
        }

        public void hide(long duration) {
            if (LOCAL_LOGV) Log.v(TAG, "HIDE: " + this);
            mHandler.sendMessageDelayed(Message.obtain(mHandler, HIDE), duration);
        }

        private void handleShow() {
            if (LOCAL_LOGV) {
                Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView + " mNextView="
                        + mNextView);
            }

            if (mView != mNextView) {
                // remove the old view if necessary
                handleHide();
                mView = mNextView;
                Context context = mView.getContext().getApplicationContext();
                String packageName = mView.getContext().getOpPackageName();
                if (context == null) {
                    context = mView.getContext();
                }
                mWM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
                // We can resolve the Gravity here by using the Locale for getting
                // the layout direction
                final Configuration config = mView.getContext().getResources().getConfiguration();
                final int gravity = Gravity.getAbsoluteGravity(mGravity,
                        config.getLayoutDirection());
                mParams.gravity = gravity;
                if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
                    mParams.horizontalWeight = 1.0f;
                }
                if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
                    mParams.verticalWeight = 1.0f;
                }
                mParams.packageName = packageName;
                mParams.hideTimeoutMilliseconds = DURATION;

                if (mView.getParent() != null) {
                    if (LOCAL_LOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
                    mWM.removeView(mView);
                }
                if (LOCAL_LOGV) Log.v(TAG, "ADD! " + mView + " in " + this);

                try {
                    mWM.addView(mView, mParams);
                } catch (WindowManager.BadTokenException e) {
                    Slog.d(TAG, "Unable to add wireless charging view. " + e);
                }
            }
        }

        private void handleHide() {
            if (LOCAL_LOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
            if (mView != null) {
                if (mView.getParent() != null) {
                    if (LOCAL_LOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
                    mWM.removeViewImmediate(mView);
                }

                mView = null;
            }
        }
    }
}
+81 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.systemui.charging;

import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import android.widget.TextView;

import com.android.systemui.R;

import java.text.NumberFormat;

/**
 * @hide
 */
public class WirelessChargingLayout extends FrameLayout {
    private final static int UNKNOWN_BATTERY_LEVEL = -1;

    public WirelessChargingLayout(Context context) {
        super(context);
        init(context, null);
    }

    public WirelessChargingLayout(Context context, int batterylLevel) {
        super(context);
        init(context, null, batterylLevel);
    }

    public WirelessChargingLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    private void init(Context c, AttributeSet attrs) {
        init(c, attrs, -1);
    }

    private void init(Context c, AttributeSet attrs, int batteryLevel) {
        final int mBatteryLevel = batteryLevel;

        inflate(c, R.layout.wireless_charging_layout, this);

        // where the circle animation occurs:
        final WirelessChargingView mChargingView = findViewById(R.id.wireless_charging_view);

        // amount of battery:
        final TextView mPercentage = findViewById(R.id.wireless_charging_percentage);

        // (optional) time until full charge if available
        final TextView mSecondaryText = findViewById(R.id.wireless_charging_secondary_text);

        if (batteryLevel != UNKNOWN_BATTERY_LEVEL) {
            mPercentage.setText(NumberFormat.getPercentInstance().format(mBatteryLevel / 100f));

            ValueAnimator animator = ObjectAnimator.ofFloat(mPercentage, "textSize",
                    getContext().getResources().getFloat(R.dimen.config_batteryLevelTextSizeStart),
                    getContext().getResources().getFloat(R.dimen.config_batteryLevelTextSizeEnd));

            animator.setDuration((long) getContext().getResources().getInteger(
                    R.integer.config_batteryLevelTextAnimationDuration));
            animator.start();
        }
    }
}
Loading