diff options
author | d34d <clark@cyngn.com> | 2016-04-25 08:16:03 -0700 |
---|---|---|
committer | Clark Scheff <clark@cyngn.com> | 2016-05-11 16:02:11 -0700 |
commit | db5cf5dc36c629b9afba8f883ef5e33a33608409 (patch) | |
tree | f6beefadd6c1367fa0ba79638038dbce8248a66a /packages | |
parent | 548a36f43dfdd03bb670bde431308b076e0e2241 (diff) | |
download | frameworks_base-db5cf5dc36c629b9afba8f883ef5e33a33608409.zip frameworks_base-db5cf5dc36c629b9afba8f883ef5e33a33608409.tar.gz frameworks_base-db5cf5dc36c629b9afba8f883ef5e33a33608409.tar.bz2 |
SysUI: Use VectorDrawable's for rendering battery
The current implementation of BatteryMeterView uses arrays of points
that are drawn programmatically. This limits the ability to overlay
or theme the battery. By utilizing VectorDrawables and
AnimatedVectorDrawables we can achieve the same look and feel of
stock while allowing for more unique battery meters.
Change-Id: I16a8a949a92827a9e8c1b528981031b6830e97cd
Diffstat (limited to 'packages')
23 files changed, 767 insertions, 538 deletions
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk index d9c9a3a..b614975 100644 --- a/packages/SystemUI/Android.mk +++ b/packages/SystemUI/Android.mk @@ -9,7 +9,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) \ LOCAL_STATIC_JAVA_LIBRARIES := Keyguard \ org.cyanogenmod.platform.internal \ android-support-v7-palette \ - android-support-v4 + android-support-v4 \ + uicommon LOCAL_JAVA_LIBRARIES := telephony-common LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/AndroidManifest_cm.xml diff --git a/packages/SystemUI/res/anim/battery_circle.xml b/packages/SystemUI/res/anim/battery_circle.xml new file mode 100644 index 0000000..16ba771 --- /dev/null +++ b/packages/SystemUI/res/anim/battery_circle.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The CyanogenMod 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. +--> +<objectAnimator + xmlns:android="http://schemas.android.com/apk/res/android" + android:propertyName="trimPathEnd" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"/>
\ No newline at end of file diff --git a/packages/SystemUI/res/anim/battery_landscape.xml b/packages/SystemUI/res/anim/battery_landscape.xml new file mode 100644 index 0000000..30d0341 --- /dev/null +++ b/packages/SystemUI/res/anim/battery_landscape.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The CyanogenMod 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. +--> +<objectAnimator + xmlns:android="http://schemas.android.com/apk/res/android" + android:propertyName="pathData" + android:valueFrom="@string/battery_landscape_path_empty" + android:valueTo="@string/battery_landscape_path_full" + android:valueType="pathType"/>
\ No newline at end of file diff --git a/packages/SystemUI/res/anim/battery_portrait.xml b/packages/SystemUI/res/anim/battery_portrait.xml new file mode 100644 index 0000000..ef21bb7 --- /dev/null +++ b/packages/SystemUI/res/anim/battery_portrait.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The CyanogenMod 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. +--> +<objectAnimator + xmlns:android="http://schemas.android.com/apk/res/android" + android:propertyName="pathData" + android:valueFrom="@string/battery_portrait_path_empty" + android:valueTo="@string/battery_portrait_path_full" + android:valueType="pathType"/>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_battery_bolt.xml b/packages/SystemUI/res/drawable/ic_battery_bolt.xml new file mode 100644 index 0000000..9da9bb5 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_battery_bolt.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The CyanogenMod 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" + android:tint="@color/batterymeter_bolt_color"> + + <path + android:fillColor="#000000" + android:pathData="M10.5,7h5l-2,4h3l-7,6l2-5H8.5L10.5,7z" /> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_battery_circle.xml b/packages/SystemUI/res/drawable/ic_battery_circle.xml new file mode 100644 index 0000000..8f39adb --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_battery_circle.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The CyanogenMod 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:id="@+id/battery_frame" + android:drawable="@drawable/ic_battery_circle_frame"/> + + <item + android:id="@+id/battery_fill" + android:drawable="@drawable/ic_battery_circle_avd"/> + + <item + android:id="@+id/battery_charge_indicator" + android:drawable="@drawable/ic_battery_bolt"/> + +</layer-list>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_battery_circle_avd.xml b/packages/SystemUI/res/drawable/ic_battery_circle_avd.xml new file mode 100644 index 0000000..5e8fb1b --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_battery_circle_avd.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The CyanogenMod 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. +--> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/ic_battery_circle_fill" > + + <target + android:name="battery_level" + android:animation="@anim/battery_circle" /> + +</animated-vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_battery_circle_fill.xml b/packages/SystemUI/res/drawable/ic_battery_circle_fill.xml new file mode 100644 index 0000000..6944535 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_battery_circle_fill.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The CyanogenMod 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 will be tinted based on battery level --> + <path + android:name="battery_level" + android:strokeColor="#000000" + android:strokeLineJoin="round" + android:strokeWidth="3" + android:pathData="@string/battery_circle_path" /> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_battery_circle_frame.xml b/packages/SystemUI/res/drawable/ic_battery_circle_frame.xml new file mode 100644 index 0000000..8528c00 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_battery_circle_frame.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The CyanogenMod 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:name="frame" + android:strokeColor="@color/batterymeter_frame_color" + android:strokeLineJoin="round" + android:strokeWidth="3" + android:pathData="@string/battery_circle_path"/> + +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_battery_landscape.xml b/packages/SystemUI/res/drawable/ic_battery_landscape.xml new file mode 100644 index 0000000..81da041 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_battery_landscape.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The CyanogenMod 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:id="@+id/battery_frame" + android:drawable="@drawable/ic_battery_landscape_frame"/> + + <item + android:id="@+id/battery_fill" + android:drawable="@drawable/ic_battery_landscape_avd"/> + + <item + android:id="@+id/battery_charge_indicator"> + <rotate + android:pivotX="50%" + android:pivotY="50%" + android:fromDegrees="90" + android:toDegrees="90" + android:drawable="@drawable/ic_battery_bolt"/> + </item> + +</layer-list>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_battery_landscape_avd.xml b/packages/SystemUI/res/drawable/ic_battery_landscape_avd.xml new file mode 100644 index 0000000..41060e8 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_battery_landscape_avd.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The CyanogenMod 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. +--> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/ic_battery_landscape_fill" > + + <target + android:name="battery_level" + android:animation="@anim/battery_landscape" /> + +</animated-vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_battery_landscape_fill.xml b/packages/SystemUI/res/drawable/ic_battery_landscape_fill.xml new file mode 100644 index 0000000..5d75bc6 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_battery_landscape_fill.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The CyanogenMod 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="26dp" + android:height="24dp" + android:viewportWidth="26" + android:viewportHeight="24"> + + <clip-path + android:name="mask" + android:pathData="@string/battery_landscape_clip_path" /> + + <!-- Path will be tinted based on battery level --> + <path + android:name="battery_level" + android:fillColor="#000000" + android:pathData="@string/battery_landscape_path_empty" /> + +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_battery_landscape_frame.xml b/packages/SystemUI/res/drawable/ic_battery_landscape_frame.xml new file mode 100644 index 0000000..789316f --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_battery_landscape_frame.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The CyanogenMod 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="26dp" + android:height="24dp" + android:viewportWidth="26" + android:viewportHeight="24"> + + <clip-path + android:name="mask" + android:pathData="@string/battery_landscape_clip_path" /> + + <path + android:name="frame" + android:fillColor="@color/batterymeter_frame_color" + android:pathData="M 1 4 H 25 V 20 H 1 V 4 Z" /> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_battery_portrait.xml b/packages/SystemUI/res/drawable/ic_battery_portrait.xml new file mode 100644 index 0000000..141bf1b --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_battery_portrait.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The CyanogenMod 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:id="@+id/battery_frame" + android:drawable="@drawable/ic_battery_portrait_frame"/> + + <item + android:id="@+id/battery_fill" + android:drawable="@drawable/ic_battery_portrait_avd"/> + + <item + android:id="@+id/battery_charge_indicator" + android:drawable="@drawable/ic_battery_bolt"/> + +</layer-list>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_battery_portrait_avd.xml b/packages/SystemUI/res/drawable/ic_battery_portrait_avd.xml new file mode 100644 index 0000000..2768f5d --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_battery_portrait_avd.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The CyanogenMod 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. +--> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/ic_battery_portrait_fill" > + + <target + android:name="battery_level" + android:animation="@anim/battery_portrait" /> + +</animated-vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_battery_portrait_fill.xml b/packages/SystemUI/res/drawable/ic_battery_portrait_fill.xml new file mode 100644 index 0000000..6742181 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_battery_portrait_fill.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The CyanogenMod 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"> + + <clip-path + android:name="mask" + android:pathData="@string/battery_portrait_clip_path" /> + + <!-- Path will be tinted based on battery level --> + <path + android:name="battery_level" + android:fillColor="#000000" + android:pathData="@string/battery_portrait_path_empty" /> + +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_battery_portrait_frame.xml b/packages/SystemUI/res/drawable/ic_battery_portrait_frame.xml new file mode 100644 index 0000000..3327f53 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_battery_portrait_frame.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The CyanogenMod 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"> + + <clip-path + android:name="mask" + android:pathData="@string/battery_portrait_clip_path" /> + + <path + android:name="frame" + android:fillColor="@color/batterymeter_frame_color" + android:pathData="M19,2v20L5,22V2H19z" /> + +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml index 82e0667..2acf611 100644 --- a/packages/SystemUI/res/layout/system_icons.xml +++ b/packages/SystemUI/res/layout/system_icons.xml @@ -34,14 +34,14 @@ <!-- battery must be padded below to match assets --> <com.android.systemui.BatteryMeterView android:id="@+id/battery" - android:layout_height="14.5dp" - android:layout_width="9.5dp" + android:layout_height="19dp" + android:layout_width="19dp" android:layout_marginBottom="@dimen/battery_margin_bottom" android:layout_marginStart="@dimen/signal_cluster_battery_padding"/> <com.android.systemui.DockBatteryMeterView android:id="@+id/dock_battery" - android:layout_height="14.5dp" - android:layout_width="9.5dp" + android:layout_height="19dp" + android:layout_width="19dp" android:layout_marginBottom="@dimen/battery_margin_bottom" android:layout_marginStart="@dimen/signal_cluster_battery_padding" android:visibility="gone"/> diff --git a/packages/SystemUI/res/values/cm_attrs.xml b/packages/SystemUI/res/values/cm_attrs.xml index 0e4933a..8c2cf91 100644 --- a/packages/SystemUI/res/values/cm_attrs.xml +++ b/packages/SystemUI/res/values/cm_attrs.xml @@ -30,5 +30,9 @@ <attr name="android:title" /> <attr name="defaultValue" format="integer"/> </declare-styleable> + + <declare-styleable name="BatteryMeterViewDrawable"> + <attr name="android:gravity"/> + </declare-styleable> </resources> diff --git a/packages/SystemUI/res/values/cm_strings.xml b/packages/SystemUI/res/values/cm_strings.xml index 678f91f..2bbbb37 100644 --- a/packages/SystemUI/res/values/cm_strings.xml +++ b/packages/SystemUI/res/values/cm_strings.xml @@ -258,4 +258,17 @@ <!-- Play queue --> <string name="play_queue_extention">Show play queue</string> + + <!-- Path data for portrait battery --> + <string name="battery_portrait_path_full" translatable="false">M19,22v-20L5,2V22H19z</string> + <string name="battery_portrait_path_empty" translatable="false">M19,22v0.0L5,22V22H19z</string> + <string name="battery_portrait_clip_path" translatable="false">M5,22V4h4V2h6v2h4v18L5,22z</string> + + <!-- Path data for landscape battery --> + <string name="battery_landscape_path_full" translatable="false">M 1 4 H 25 V 20 H 1 V 4 Z</string> + <string name="battery_landscape_path_empty" translatable="false">M 1 4 H 1 V 20 H 1 V 4 Z</string> + <string name="battery_landscape_clip_path" translatable="false">M25,16h-2v4H1V4h22v4h2V16z</string> + + <!-- Path data for circle battery --> + <string name="battery_circle_path">M 12 3.5 C 16.6944203736 3.5 20.5 7.30557962644 20.5 12 C 20.5 16.6944203736 16.6944203736 20.5 12 20.5 C 7.30557962644 20.5 3.5 16.6944203736 3.5 12 C 3.5 7.30557962644 7.30557962644 3.5 12 3.5 Z</string> </resources> diff --git a/packages/SystemUI/res/values/cm_styles.xml b/packages/SystemUI/res/values/cm_styles.xml index 8caa89d..4fb216c 100644 --- a/packages/SystemUI/res/values/cm_styles.xml +++ b/packages/SystemUI/res/values/cm_styles.xml @@ -41,4 +41,14 @@ <item name="android:textSize">@dimen/qs_detail_item_secondary_text_size</item> </style> + <!-- Base battery metter drawable style --> + <style name="BatteryMeterViewDrawable"> + <!-- gravity used for positioning text within the battery meter --> + <item name="android:gravity">center</item> + </style> + <!-- Battery meter drawable styles --> + <style name="BatteryMeterViewDrawable.Portrait"/> + <style name="BatteryMeterViewDrawable.Landscape"/> + <style name="BatteryMeterViewDrawable.Circle"/> + </resources> diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index e606156..7f29333 100755 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2013-14 The Android Open Source Project + * Copyright (C) 2016 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,43 +17,47 @@ package com.android.systemui; -import com.android.systemui.statusbar.policy.BatteryController; -import com.android.systemui.statusbar.policy.BatteryStateRegistar; - import android.animation.ArgbEvaluator; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.content.res.Resources; +import android.content.res.ThemeConfig; import android.content.res.TypedArray; +import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; -import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; import android.graphics.Rect; -import android.graphics.RectF; import android.graphics.Typeface; +import android.graphics.drawable.AnimatedVectorDrawable; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; import android.os.BatteryManager; import android.os.Bundle; import android.util.AttributeSet; +import android.util.Log; +import android.view.Gravity; import android.view.View; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.BatteryStateRegistar; + +import org.cyanogenmod.graphics.drawable.StopMotionVectorDrawable; + public class BatteryMeterView extends View implements DemoMode, BatteryController.BatteryStateChangeCallback { public static final String TAG = BatteryMeterView.class.getSimpleName(); public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST"; - private static final int FULL = 96; - - private static final float BOLT_LEVEL_THRESHOLD = 0.3f; // opaque bolt below this fraction - private final int[] mColors; protected boolean mShowPercent = true; - private float mButtonHeightFraction; - private float mSubpixelSmoothingLeft; - private float mSubpixelSmoothingRight; public enum BatteryMeterMode { BATTERY_METER_GONE, @@ -66,14 +71,9 @@ public class BatteryMeterView extends View implements DemoMode, private int mWidth; private String mWarningString; private final int mCriticalLevel; - private final int mFrameColor; private boolean mAnimationsEnabled; - private final Path mShapePath = new Path(); - private final Path mClipPath = new Path(); - private final Path mTextPath = new Path(); - private BatteryStateRegistar mBatteryStateRegistar; private BatteryController mBatteryController; private boolean mPowerSaveEnabled; @@ -225,8 +225,6 @@ public class BatteryMeterView extends View implements DemoMode, final Resources res = context.getResources(); TypedArray atts = context.obtainStyledAttributes(attrs, R.styleable.BatteryMeterView, defStyle, 0); - mFrameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor, - res.getColor(R.color.batterymeter_frame_color)); TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels); TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values); @@ -242,12 +240,6 @@ public class BatteryMeterView extends View implements DemoMode, mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol); mCriticalLevel = getContext().getResources().getInteger( com.android.internal.R.integer.config_criticalBatteryWarningLevel); - mButtonHeightFraction = context.getResources().getFraction( - R.fraction.battery_button_height_fraction, 1, 1); - mSubpixelSmoothingLeft = context.getResources().getFraction( - R.fraction.battery_subpixel_smoothing_left, 1, 1); - mSubpixelSmoothingRight = context.getResources().getFraction( - R.fraction.battery_subpixel_smoothing_right, 1, 1); mDarkModeBackgroundColor = context.getColor(R.color.dark_mode_icon_color_dual_tone_background); @@ -260,17 +252,13 @@ public class BatteryMeterView extends View implements DemoMode, } protected BatteryMeterDrawable createBatteryMeterDrawable(BatteryMeterMode mode) { - Resources res = mContext.getResources(); + Resources res = getResources(); switch (mode) { - case BATTERY_METER_CIRCLE: - return new CircleBatteryMeterDrawable(res); - case BATTERY_METER_ICON_LANDSCAPE: - return new NormalBatteryMeterDrawable(res, true); case BATTERY_METER_TEXT: case BATTERY_METER_GONE: return null; default: - return new NormalBatteryMeterDrawable(res, false); + return new AllInOneBatteryMeterDrawable(res, mode); } } @@ -279,13 +267,8 @@ public class BatteryMeterView extends View implements DemoMode, int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); - if (mMeterMode == BatteryMeterMode.BATTERY_METER_CIRCLE) { - height += (CircleBatteryMeterDrawable.STROKE_WITH / 3); - width = height; - } else if (mMeterMode == BatteryMeterMode.BATTERY_METER_TEXT) { + if (mMeterMode == BatteryMeterMode.BATTERY_METER_TEXT) { onSizeChanged(width, height, 0, 0); // Force a size changed event - } else if (mMeterMode == BatteryMeterMode.BATTERY_METER_ICON_LANDSCAPE) { - width = (int)(height * 1.2f); } setMeasuredDimension(width, height); @@ -378,11 +361,6 @@ public class BatteryMeterView extends View implements DemoMode, mBatteryMeterDrawable.onDispose(); } mBatteryMeterDrawable = createBatteryMeterDrawable(mode); - if (mMeterMode == BatteryMeterMode.BATTERY_METER_ICON_PORTRAIT || - mMeterMode == BatteryMeterMode.BATTERY_METER_ICON_LANDSCAPE) { - ((NormalBatteryMeterDrawable)mBatteryMeterDrawable).loadBoltPoints( - mContext.getResources()); - } if (tracker.present) { setVisibility(View.VISIBLE); requestLayout(); @@ -394,7 +372,6 @@ public class BatteryMeterView extends View implements DemoMode, } public int getColorForLevel(int percent) { - // If we are in power save mode, always use the normal color. if (mPowerSaveEnabled) { return mColors[mColors.length-1]; @@ -482,253 +459,73 @@ public class BatteryMeterView extends View implements DemoMode, void setDarkIntensity(int backgroundColor, int fillColor); } - protected class NormalBatteryMeterDrawable implements BatteryMeterDrawable { + protected class AllInOneBatteryMeterDrawable implements BatteryMeterDrawable { private static final boolean SINGLE_DIGIT_PERCENT = false; private static final boolean SHOW_100_PERCENT = false; private boolean mDisposed; - protected final boolean mHorizontal; + private boolean mIsAnimating; // stores charge-animation status to remove callbacks + + private float mTextX, mTextY; // precalculated position for drawText() to appear centered + + private boolean mInitialized; - private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint; - private float mTextHeight, mWarningTextHeight; + private Paint mTextAndBoltPaint; + private Paint mWarningTextPaint; - private int mChargeColor; - private final float[] mBoltPoints; - private final Path mBoltPath = new Path(); + private LayerDrawable mBatteryDrawable; + private Drawable mFrameDrawable; + private StopMotionVectorDrawable mLevelDrawable; + private Drawable mBoltDrawable; - private final RectF mFrame = new RectF(); - private final RectF mButtonFrame = new RectF(); - private final RectF mBoltFrame = new RectF(); + private BatteryMeterMode mMode; + private int mTextGravity; - public NormalBatteryMeterDrawable(Resources res, boolean horizontal) { + public AllInOneBatteryMeterDrawable(Resources res, BatteryMeterMode mode) { super(); - mHorizontal = horizontal; - mDisposed = false; - mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mFramePaint.setColor(mFrameColor); - mFramePaint.setDither(true); - mFramePaint.setStrokeWidth(0); - mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE); + loadBatteryDrawables(res, mode); + + mMode = mode; + mDisposed = false; - mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mBatteryPaint.setDither(true); - mBatteryPaint.setStrokeWidth(0); - mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE); + // load text gravity + int[] textGravityAttr = new int[] {android.R.attr.gravity}; + int resId = getBatteryDrawableStyleResourceForMode(mode); + if (resId != 0) { + TypedArray a = getContext().obtainStyledAttributes( + getBatteryDrawableStyleResourceForMode(mode), textGravityAttr); + mTextGravity = a.getInt(0, Gravity.CENTER); + } else { + mTextGravity = Gravity.CENTER; + } + Log.d(TAG, "mTextGravity=" + mTextGravity); - mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mTextAndBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG); Typeface font = Typeface.create("sans-serif-condensed", Typeface.BOLD); - mTextPaint.setTypeface(font); - mTextPaint.setTextAlign(Paint.Align.CENTER); + mTextAndBoltPaint.setTypeface(font); + mTextAndBoltPaint.setTextAlign(getPaintAlignmentFromGravity(mTextGravity)); + mTextAndBoltPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR)); mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mWarningTextPaint.setColor(mColors[1]); font = Typeface.create("sans-serif", Typeface.BOLD); mWarningTextPaint.setTypeface(font); - mWarningTextPaint.setTextAlign(Paint.Align.CENTER); - - mChargeColor = getResources().getColor(R.color.batterymeter_charge_color); - - mBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mBoltPaint.setColor(res.getColor(R.color.batterymeter_bolt_color)); - mBoltPoints = loadBoltPoints(res); + mWarningTextPaint.setTextAlign(getPaintAlignmentFromGravity(mTextGravity)); } @Override public void onDraw(Canvas c, BatteryTracker tracker) { if (mDisposed) return; - final int level = tracker.level; - - if (level == BatteryTracker.UNKNOWN_LEVEL) return; - - float drawFrac = (float) level / 100f; - final int pt = getPaddingTop() + (mHorizontal ? (int)(mHeight * 0.12f) : 0); - final int pl = getPaddingLeft(); - final int pr = getPaddingRight(); - final int pb = getPaddingBottom() + (mHorizontal ? (int)(mHeight * 0.08f) : 0); - final int height = mHeight - pt - pb; - final int width = mWidth - pl - pr; - - final int buttonHeight = (int) ((mHorizontal ? width : height) * mButtonHeightFraction); - - mFrame.set(0, 0, width, height); - mFrame.offset(pl, pt); - - if (mHorizontal) { - mButtonFrame.set( - /*cover frame border of intersecting area*/ - width - buttonHeight - mFrame.left, - mFrame.top + Math.round(height * 0.25f), - mFrame.right, - mFrame.bottom - Math.round(height * 0.25f)); - - mButtonFrame.top += mSubpixelSmoothingLeft; - mButtonFrame.bottom -= mSubpixelSmoothingRight; - mButtonFrame.right -= mSubpixelSmoothingRight; - } else { - // button-frame: area above the battery body - mButtonFrame.set( - mFrame.left + Math.round(width * 0.25f), - mFrame.top, - mFrame.right - Math.round(width * 0.25f), - mFrame.top + buttonHeight); - - mButtonFrame.top += mSubpixelSmoothingLeft; - mButtonFrame.left += mSubpixelSmoothingLeft; - mButtonFrame.right -= mSubpixelSmoothingRight; - } - - // frame: battery body area - - if (mHorizontal) { - mFrame.right -= buttonHeight; - } else { - mFrame.top += buttonHeight; - } - mFrame.left += mSubpixelSmoothingLeft; - mFrame.top += mSubpixelSmoothingLeft; - mFrame.right -= mSubpixelSmoothingRight; - mFrame.bottom -= mSubpixelSmoothingRight; - - // set the battery charging color - mBatteryPaint.setColor(tracker.plugged ? mChargeColor : getColorForLevel(level)); - - if (level >= FULL) { - drawFrac = 1f; - } else if (level <= mCriticalLevel) { - drawFrac = 0f; + if (!mInitialized) { + init(); } - final float levelTop; - - if (drawFrac == 1f) { - if (mHorizontal) { - levelTop = mButtonFrame.right; - } else { - levelTop = mButtonFrame.top; - } - } else { - if (mHorizontal) { - levelTop = (mFrame.right - (mFrame.width() * (1f - drawFrac))); - } else { - levelTop = (mFrame.top + (mFrame.height() * (1f - drawFrac))); - } - } - - // define the battery shape - mShapePath.reset(); - mShapePath.moveTo(mButtonFrame.left, mButtonFrame.top); - if (mHorizontal) { - mShapePath.lineTo(mButtonFrame.right, mButtonFrame.top); - mShapePath.lineTo(mButtonFrame.right, mButtonFrame.bottom); - mShapePath.lineTo(mButtonFrame.left, mButtonFrame.bottom); - mShapePath.lineTo(mFrame.right, mFrame.bottom); - mShapePath.lineTo(mFrame.left, mFrame.bottom); - mShapePath.lineTo(mFrame.left, mFrame.top); - mShapePath.lineTo(mButtonFrame.left, mFrame.top); - mShapePath.lineTo(mButtonFrame.left, mButtonFrame.top); - } else { - mShapePath.lineTo(mButtonFrame.right, mButtonFrame.top); - mShapePath.lineTo(mButtonFrame.right, mFrame.top); - mShapePath.lineTo(mFrame.right, mFrame.top); - mShapePath.lineTo(mFrame.right, mFrame.bottom); - mShapePath.lineTo(mFrame.left, mFrame.bottom); - mShapePath.lineTo(mFrame.left, mFrame.top); - mShapePath.lineTo(mButtonFrame.left, mFrame.top); - mShapePath.lineTo(mButtonFrame.left, mButtonFrame.top); - } - - if (tracker.plugged) { - // define the bolt shape - final float bl = mFrame.left + mFrame.width() / (mHorizontal ? 9f : 4.5f); - final float bt = mFrame.top + mFrame.height() / (mHorizontal ? 4.5f : 6f); - final float br = mFrame.right - mFrame.width() / (mHorizontal ? 6f : 7f); - final float bb = mFrame.bottom - mFrame.height() / (mHorizontal ? 7f : 10f); - if (mBoltFrame.left != bl || mBoltFrame.top != bt - || mBoltFrame.right != br || mBoltFrame.bottom != bb) { - mBoltFrame.set(bl, bt, br, bb); - mBoltPath.reset(); - mBoltPath.moveTo( - mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(), - mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height()); - for (int i = 2; i < mBoltPoints.length; i += 2) { - mBoltPath.lineTo( - mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(), - mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height()); - } - mBoltPath.lineTo( - mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(), - mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height()); - } - - float boltPct = mHorizontal ? - (mBoltFrame.left - levelTop) / (mBoltFrame.left - mBoltFrame.right) : - (mBoltFrame.bottom - levelTop) / (mBoltFrame.bottom - mBoltFrame.top); - boltPct = Math.min(Math.max(boltPct, 0), 1); - if (boltPct <= BOLT_LEVEL_THRESHOLD) { - // draw the bolt if opaque - c.drawPath(mBoltPath, mBoltPaint); - } else { - // otherwise cut the bolt out of the overall shape - mShapePath.op(mBoltPath, Path.Op.DIFFERENCE); - } - } - - // compute percentage text - boolean pctOpaque = false; - float pctX = 0, pctY = 0; - String pctText = null; - if (!tracker.plugged && level > mCriticalLevel && mShowPercent) { - mTextPaint.setColor(getColorForLevel(level)); - final float full = mHorizontal ? 0.60f : 0.45f; - final float nofull = mHorizontal ? 0.75f : 0.6f; - final float single = mHorizontal ? 0.86f : 0.75f; - mTextPaint.setTextSize(height * - (SINGLE_DIGIT_PERCENT ? single - : (tracker.level == 100 ? full : nofull))); - mTextHeight = -mTextPaint.getFontMetrics().ascent; - pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level); - pctX = mWidth * 0.5f; - pctY = (mHeight + mTextHeight) * 0.47f; - if (mHorizontal) { - pctOpaque = pctX > levelTop; - } else { - pctOpaque = levelTop > pctY; - } - if (!pctOpaque) { - mTextPath.reset(); - mTextPaint.getTextPath(pctText, 0, pctText.length(), pctX, pctY, mTextPath); - // cut the percentage text out of the overall shape - mShapePath.op(mTextPath, Path.Op.DIFFERENCE); - } - } - - // draw the battery shape background - c.drawPath(mShapePath, mFramePaint); - - // draw the battery shape, clipped to charging level - if (mHorizontal) { - mFrame.right = levelTop; - } else { - mFrame.top = levelTop; - } - mClipPath.reset(); - mClipPath.addRect(mFrame, Path.Direction.CCW); - mShapePath.op(mClipPath, Path.Op.INTERSECT); - c.drawPath(mShapePath, mBatteryPaint); - - if (!tracker.plugged) { - if (level <= mCriticalLevel) { - // draw the warning text - final float x = mWidth * 0.5f; - final float y = (mHeight + mWarningTextHeight) * 0.48f; - c.drawText(mWarningString, x, y, mWarningTextPaint); - } else if (pctOpaque) { - // draw the percentage text - c.drawText(pctText, pctX, pctY, mTextPaint); - } + drawBattery(c, tracker); + if (mAnimationsEnabled) { + // TODO: Allow custom animations to be used } } @@ -740,282 +537,275 @@ public class BatteryMeterView extends View implements DemoMode, @Override public void setDarkIntensity(int backgroundColor, int fillColor) { mIconTint = fillColor; - mFramePaint.setColor(backgroundColor); - mBoltPaint.setColor(fillColor); - mChargeColor = fillColor; + mBoltDrawable.setTint(fillColor); + updateBoltDrawableLayer(mBatteryDrawable, mBoltDrawable); invalidate(); } @Override public void onSizeChanged(int w, int h, int oldw, int oldh) { - mHeight = h; - mWidth = w; - mWarningTextPaint.setTextSize(h * 0.75f); - mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent; - } - - private float[] loadBoltPoints(Resources res) { - final int[] pts = res.getIntArray(getBoltPointsArrayResource()); - int maxX = 0, maxY = 0; - for (int i = 0; i < pts.length; i += 2) { - maxX = Math.max(maxX, pts[i]); - maxY = Math.max(maxY, pts[i + 1]); - } - final float[] ptsF = new float[pts.length]; - for (int i = 0; i < pts.length; i += 2) { - ptsF[i] = (float)pts[i] / maxX; - ptsF[i + 1] = (float)pts[i + 1] / maxY; - } - return ptsF; + init(); } - protected int getBoltPointsArrayResource() { - return mHorizontal - ? R.array.batterymeter_inverted_bolt_points - : R.array.batterymeter_bolt_points; + private boolean isThemeApplied() { + ThemeConfig themeConfig = ThemeConfig.getBootTheme(getContext().getContentResolver()); + return themeConfig != null && + !ThemeConfig.SYSTEM_DEFAULT.equals(themeConfig.getOverlayForStatusBar()); } - } - - protected class CircleBatteryMeterDrawable implements BatteryMeterDrawable { - private static final boolean SINGLE_DIGIT_PERCENT = false; - private static final boolean SHOW_100_PERCENT = false; - private static final int FULL = 96; + private void checkBatteryMeterDrawableValid(Resources res, BatteryMeterMode mode) { + final int resId = getBatteryDrawableResourceForMode(mode); + final Drawable batteryDrawable; + try { + batteryDrawable = res.getDrawable(resId); + } catch (Resources.NotFoundException e) { + throw new BatteryMeterDrawableException(res.getResourceName(resId) + " is an " + + "invalid drawable", e); + } - public static final float STROKE_WITH = 6.5f; + // check that the drawable is a LayerDrawable + if (!(batteryDrawable instanceof LayerDrawable)) { + throw new BatteryMeterDrawableException("Expected a LayerDrawable but received a " + + batteryDrawable.getClass().getSimpleName()); + } - private boolean mDisposed; + final LayerDrawable layerDrawable = (LayerDrawable) batteryDrawable; + final Drawable frame = layerDrawable.findDrawableByLayerId(R.id.battery_frame); + final Drawable level = layerDrawable.findDrawableByLayerId(R.id.battery_fill); + final Drawable bolt = layerDrawable.findDrawableByLayerId( + R.id.battery_charge_indicator); + // now check that the required layers exist and are of the correct type + if (frame == null) { + throw new BatteryMeterDrawableException("Missing battery_frame drawble"); + } + if (bolt == null) { + throw new BatteryMeterDrawableException( + "Missing battery_charge_indicator drawable"); + } + if (level != null) { + // check that the level drawable is an AnimatedVectorDrawable + if (!(level instanceof AnimatedVectorDrawable)) { + throw new BatteryMeterDrawableException("Expected a AnimatedVectorDrawable " + + "but received a " + level.getClass().getSimpleName()); + } + // make sure we can stop motion animate the level drawable + try { + StopMotionVectorDrawable smvd = new StopMotionVectorDrawable(level); + smvd.setCurrentFraction(0.5f); + } catch (Exception e) { + throw new BatteryMeterDrawableException("Unable to perform stop motion on " + + "battery_fill drawable", e); + } + } else { + throw new BatteryMeterDrawableException("Missing battery_fill drawable"); + } + } - private int mAnimOffset; - private boolean mIsAnimating; // stores charge-animation status to reliably - //remove callbacks + private void loadBatteryDrawables(Resources res, BatteryMeterMode mode) { + if (isThemeApplied()) { + try { + checkBatteryMeterDrawableValid(res, mode); + } catch (BatteryMeterDrawableException e) { + Log.w(TAG, "Invalid themed battery meter drawable, falling back to system", e); + final Context context = getContext(); + PackageManager pm = getContext().getPackageManager(); + try { + res = pm.getThemedResourcesForApplication(context.getPackageName(), + ThemeConfig.SYSTEM_DEFAULT); + } catch (PackageManager.NameNotFoundException nnfe) { + /* ignore, this should not happen */ + } + } + } - private int mCircleSize; // draw size of circle - private RectF mRectLeft; // contains the precalculated rect used in drawArc(), - // derived from mCircleSize - private float mTextX, mTextY; // precalculated position for drawText() to appear centered + int drawableResId = getBatteryDrawableResourceForMode(mode); + mBatteryDrawable = (LayerDrawable) res.getDrawable(drawableResId); + mFrameDrawable = mBatteryDrawable.findDrawableByLayerId(R.id.battery_frame); + // set the animated vector drawable we will be stop animating + Drawable levelDrawable = mBatteryDrawable.findDrawableByLayerId(R.id.battery_fill); + mLevelDrawable = new StopMotionVectorDrawable(levelDrawable); + mBoltDrawable = mBatteryDrawable.findDrawableByLayerId(R.id.battery_charge_indicator); + } - private Paint mTextPaint; - private Paint mFrontPaint; - private Paint mBackPaint; - private Paint mBoltPaint; - private Paint mWarningTextPaint; + private void drawBattery(Canvas canvas, BatteryTracker tracker) { + boolean unknownStatus = tracker.status == BatteryManager.BATTERY_STATUS_UNKNOWN; + int level = tracker.level; - private final RectF mBoltFrame = new RectF(); + if (unknownStatus || tracker.status == BatteryManager.BATTERY_STATUS_FULL) { + level = 100; + } - private int mChargeColor; - private final float[] mBoltPoints; - private final Path mBoltPath = new Path(); + // Now draw the level indicator + // set the level and tint color of the fill drawable + mLevelDrawable.setCurrentFraction(level / 100f); + mLevelDrawable.setTint(getColorForLevel(level)); + mBatteryDrawable.findDrawableByLayerId(R.id.battery_charge_indicator) + .setAlpha(tracker.plugged ? 255 : 0); + mBatteryDrawable.draw(canvas); - public CircleBatteryMeterDrawable(Resources res) { - super(); - mDisposed = false; + // if chosen by options, draw percentage text in the middle + // always skip percentage when 100, so layout doesnt break + if (unknownStatus) { + mTextAndBoltPaint.setColor(getContext().getColor(R.color.batterymeter_frame_color)); + canvas.drawText("?", mTextX, mTextY, mTextAndBoltPaint); - mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - Typeface font = Typeface.create("sans-serif-condensed", Typeface.BOLD); - mTextPaint.setTypeface(font); - mTextPaint.setTextAlign(Paint.Align.CENTER); - - mFrontPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mFrontPaint.setStrokeCap(Paint.Cap.BUTT); - mFrontPaint.setDither(true); - mFrontPaint.setStrokeWidth(0); - mFrontPaint.setStyle(Paint.Style.STROKE); - - mBackPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mBackPaint.setColor(res.getColor(R.color.batterymeter_frame_color)); - mBackPaint.setStrokeCap(Paint.Cap.BUTT); - mBackPaint.setDither(true); - mBackPaint.setStrokeWidth(0); - mBackPaint.setStyle(Paint.Style.STROKE); + } else if (!tracker.plugged) { + drawPercentageText(canvas, tracker); + } + } - mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mWarningTextPaint.setColor(mColors[1]); - font = Typeface.create("sans-serif", Typeface.BOLD); - mWarningTextPaint.setTypeface(font); - mWarningTextPaint.setTextAlign(Paint.Align.CENTER); + private void drawPercentageText(Canvas canvas, BatteryTracker tracker) { + final int level = tracker.level; + if (level > mCriticalLevel + && (mShowPercent && !(level == 100 && !SHOW_100_PERCENT))) { + // draw the percentage text + String pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level); + mTextAndBoltPaint.setColor(getColorForLevel(level)); + canvas.drawText(pctText, mTextX, mTextY, mTextAndBoltPaint); + } else if (level <= mCriticalLevel) { + // draw the warning text + canvas.drawText(mWarningString, mTextX, mTextY, mWarningTextPaint); + } + } - mChargeColor = getResources().getColor(R.color.batterymeter_charge_color); + /** + * initializes all size dependent variables + */ + private void init() { + // not much we can do with zero width or height, we'll get another pass later + if (mWidth <= 0 || mHeight <=0) return; - mBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mBoltPaint.setColor(res.getColor(R.color.batterymeter_bolt_color)); - mBoltPoints = loadBoltPoints(res); - } + final float widthDiv2 = mWidth / 2f; + mTextAndBoltPaint.setTextSize(widthDiv2); + mWarningTextPaint.setTextSize(widthDiv2); - @Override - public void onDraw(Canvas c, BatteryTracker tracker) { - if (mDisposed) return; + int pLeft = getPaddingLeft(); + Rect iconBounds = new Rect(pLeft, 0, pLeft + mWidth, mHeight); + mBatteryDrawable.setBounds(iconBounds); - if (mRectLeft == null) { - initSizeBasedStuff(); + // calculate text position + Rect bounds = new Rect(); + mTextAndBoltPaint.getTextBounds("99", 0, "99".length(), bounds); + boolean isRtl = isLayoutRtl(); + + // compute mTextX based on text gravity + if ((mTextGravity & Gravity.START) == Gravity.START) { + mTextX = isRtl ? mWidth : 0; + } else if ((mTextGravity & Gravity.END) == Gravity.END) { + mTextX = isRtl ? 0 : mWidth; + } else if ((mTextGravity & Gravity.LEFT) == Gravity.LEFT) { + mTextX = 0; + }else if ((mTextGravity & Gravity.RIGHT) == Gravity.RIGHT) { + mTextX = mWidth; + } else { + mTextX = widthDiv2 + pLeft; } - drawCircle(c, tracker, mTextX, mRectLeft); - if (mAnimationsEnabled) { - updateChargeAnim(tracker); + // compute mTextY based on text gravity + if ((mTextGravity & Gravity.TOP) == Gravity.TOP) { + mTextY = bounds.height(); + } else if ((mTextGravity & Gravity.BOTTOM) == Gravity.BOTTOM) { + mTextY = mHeight; + } else { + mTextY = widthDiv2 + bounds.height() / 2.0f; } - } - @Override - public void onDispose() { - mDisposed = true; + updateBoltDrawableLayer(mBatteryDrawable, mBoltDrawable); + + mInitialized = true; } - @Override - public void setDarkIntensity(int backgroundColor, int fillColor) { - mIconTint = fillColor; - mBoltPaint.setColor(fillColor); - mChargeColor = fillColor; - invalidate(); + private int getBatteryDrawableResourceForMode(BatteryMeterMode mode) { + switch (mode) { + case BATTERY_METER_ICON_LANDSCAPE: + return R.drawable.ic_battery_landscape; + case BATTERY_METER_CIRCLE: + return R.drawable.ic_battery_circle; + case BATTERY_METER_ICON_PORTRAIT: + return R.drawable.ic_battery_portrait; + default: + return 0; + } } - @Override - public void onSizeChanged(int w, int h, int oldw, int oldh) { - initSizeBasedStuff(); + private int getBatteryDrawableStyleResourceForMode(BatteryMeterMode mode) { + switch (mode) { + case BATTERY_METER_ICON_LANDSCAPE: + return R.style.BatteryMeterViewDrawable_Landscape; + case BATTERY_METER_CIRCLE: + return R.style.BatteryMeterViewDrawable_Circle; + case BATTERY_METER_ICON_PORTRAIT: + return R.style.BatteryMeterViewDrawable_Portrait; + default: + return R.style.BatteryMeterViewDrawable; + } } - private float[] loadBoltPoints(Resources res) { - final int[] pts = res.getIntArray(getBoltPointsArrayResource()); - int maxX = 0, maxY = 0; - for (int i = 0; i < pts.length; i += 2) { - maxX = Math.max(maxX, pts[i]); - maxY = Math.max(maxY, pts[i + 1]); + private Paint.Align getPaintAlignmentFromGravity(int gravity) { + boolean isRtl = isLayoutRtl(); + if ((gravity & Gravity.START) == Gravity.START) { + return isRtl ? Paint.Align.RIGHT : Paint.Align.LEFT; } - final float[] ptsF = new float[pts.length]; - for (int i = 0; i < pts.length; i += 2) { - ptsF[i] = (float)pts[i] / maxX; - ptsF[i + 1] = (float)pts[i + 1] / maxY; + if ((gravity & Gravity.END) == Gravity.END) { + return isRtl ? Paint.Align.LEFT : Paint.Align.RIGHT; } - return ptsF; - } + if ((gravity & Gravity.LEFT) == Gravity.LEFT) return Paint.Align.LEFT; + if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) return Paint.Align.RIGHT; - protected int getBoltPointsArrayResource() { - return R.array.batterymeter_bolt_points; + // default to center + return Paint.Align.CENTER; } - private void drawCircle(Canvas canvas, BatteryTracker tracker, - float textX, RectF drawRect) { - boolean unknownStatus = tracker.status == BatteryManager.BATTERY_STATUS_UNKNOWN; - int level = tracker.level; - Paint paint; - - if (unknownStatus) { - paint = mBackPaint; - level = 100; // Draw all the circle; + // Creates a BitmapDrawable of the bolt so we can make use of the XOR xfer mode with vector + // based drawables + private void updateBoltDrawableLayer(LayerDrawable batteryDrawable, Drawable boltDrawable) { + BitmapDrawable newBoltDrawable; + if (boltDrawable instanceof BitmapDrawable) { + newBoltDrawable = (BitmapDrawable) boltDrawable.mutate(); } else { - paint = mFrontPaint; - paint.setColor(getColorForLevel(level)); - if (tracker.status == BatteryManager.BATTERY_STATUS_FULL) { - level = 100; + Bitmap boltBitmap = createBoltBitmap(boltDrawable); + if (boltBitmap == null) { + // not much to do with a null bitmap so keep original bolt for now + return; } + Rect bounds = boltDrawable.getBounds(); + newBoltDrawable = new BitmapDrawable(getResources(), boltBitmap); + newBoltDrawable.setBounds(bounds); } - - // draw thin gray ring first - canvas.drawArc(drawRect, 270, 360, false, mBackPaint); - if (level != 0) { - // draw colored arc representing charge level - canvas.drawArc(drawRect, 270 + mAnimOffset, 3.6f * level, false, paint); - } - // if chosen by options, draw percentage text in the middle - // always skip percentage when 100, so layout doesnt break - if (unknownStatus) { - mTextPaint.setColor(paint.getColor()); - canvas.drawText("?", textX, mTextY, mTextPaint); - - } else if (tracker.plugged) { - canvas.drawPath(mBoltPath, mBoltPaint); - } else { - if (level > mCriticalLevel - && (mShowPercent && !(tracker.level == 100 && !SHOW_100_PERCENT))) { - // draw the percentage text - String pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level); - mTextPaint.setColor(paint.getColor()); - canvas.drawText(pctText, textX, mTextY, mTextPaint); - } else if (level <= mCriticalLevel) { - // draw the warning text - canvas.drawText(mWarningString, textX, mTextY, mWarningTextPaint); + newBoltDrawable.getPaint().setXfermode(new PorterDuffXfermode( + PorterDuff.Mode.XOR)); + batteryDrawable.setDrawableByLayerId(R.id.battery_charge_indicator, newBoltDrawable); + } + + private Bitmap createBoltBitmap(Drawable boltDrawable) { + // not much we can do with zero width or height, we'll get another pass later + if (mWidth <= 0 || mHeight <= 0) return null; + + Bitmap bolt; + if (!(boltDrawable instanceof BitmapDrawable)) { + int pLeft = getPaddingLeft(); + Rect iconBounds = new Rect(pLeft, 0, pLeft + mWidth, mHeight); + bolt = Bitmap.createBitmap(iconBounds.width(), iconBounds.height(), + Bitmap.Config.ARGB_8888); + if (bolt != null) { + Canvas c = new Canvas(bolt); + c.drawColor(-1, PorterDuff.Mode.CLEAR); + boltDrawable.draw(c); } - } - } - - /** - * updates the animation counter - * cares for timed callbacks to continue animation cycles - * uses mInvalidate for delayed invalidate() callbacks - */ - private void updateChargeAnim(BatteryTracker tracker) { - // Stop animation when battery is full or after the meter - // rotated back to 0 after unplugging. - if (!tracker.shouldIndicateCharging() - || tracker.status == BatteryManager.BATTERY_STATUS_FULL - || tracker.level == 0) { - mIsAnimating = false; } else { - mIsAnimating = true; - } - - if (mAnimOffset > 360) { - mAnimOffset = 0; - } - - boolean continueAnimation = mIsAnimating || mAnimOffset != 0; - - if (continueAnimation) { - mAnimOffset += 3; + bolt = ((BitmapDrawable) boltDrawable).getBitmap(); } - if (continueAnimation) { - postInvalidateDelayed(50); - } + return bolt; } - /** - * initializes all size dependent variables - * sets stroke width and text size of all involved paints - * YES! i think the method name is appropriate - */ - private void initSizeBasedStuff() { - mCircleSize = Math.min(getMeasuredWidth(), getMeasuredHeight()); - mTextPaint.setTextSize(mCircleSize / 2f); - mWarningTextPaint.setTextSize(mCircleSize / 2f); - - float strokeWidth = mCircleSize / STROKE_WITH; - mFrontPaint.setStrokeWidth(strokeWidth); - mBackPaint.setStrokeWidth(strokeWidth); - - // calculate rectangle for drawArc calls - int pLeft = getPaddingLeft(); - mRectLeft = new RectF(pLeft + strokeWidth / 2.0f, 0 + strokeWidth / 2.0f, mCircleSize - - strokeWidth / 2.0f + pLeft, mCircleSize - strokeWidth / 2.0f); + private class BatteryMeterDrawableException extends RuntimeException { + public BatteryMeterDrawableException(String detailMessage) { + super(detailMessage); + } - // calculate Y position for text - Rect bounds = new Rect(); - mTextPaint.getTextBounds("99", 0, "99".length(), bounds); - mTextX = mCircleSize / 2.0f + getPaddingLeft(); - // the +1dp at end of formula balances out rounding issues.works out on all resolutions - mTextY = mCircleSize / 2.0f + (bounds.bottom - bounds.top) / 2.0f - - strokeWidth / 2.0f + getResources().getDisplayMetrics().density; - - // draw the bolt - final float bl = (int) (mRectLeft.left + mRectLeft.width() / 3.2f); - final float bt = (int) (mRectLeft.top + mRectLeft.height() / 4f); - final float br = (int) (mRectLeft.right - mRectLeft.width() / 5.2f); - final float bb = (int) (mRectLeft.bottom - mRectLeft.height() / 8f); - if (mBoltFrame.left != bl || mBoltFrame.top != bt - || mBoltFrame.right != br || mBoltFrame.bottom != bb) { - mBoltFrame.set(bl, bt, br, bb); - mBoltPath.reset(); - mBoltPath.moveTo( - mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(), - mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height()); - for (int i = 2; i < mBoltPoints.length; i += 2) { - mBoltPath.lineTo( - mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(), - mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height()); - } - mBoltPath.lineTo( - mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(), - mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height()); + public BatteryMeterDrawableException(String detailMessage, Throwable throwable) { + super(detailMessage, throwable); } } } diff --git a/packages/SystemUI/src/com/android/systemui/DockBatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/DockBatteryMeterView.java index b80e6d0..a29b16c 100755 --- a/packages/SystemUI/src/com/android/systemui/DockBatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/DockBatteryMeterView.java @@ -18,7 +18,6 @@ package com.android.systemui; import android.content.Context; import android.content.Intent; -import android.content.res.Resources; import android.os.BatteryManager; import android.util.AttributeSet; import android.view.View; @@ -148,45 +147,4 @@ public class DockBatteryMeterView extends BatteryMeterView { setVisibility(View.GONE); } } - - @Override - protected BatteryMeterDrawable createBatteryMeterDrawable(BatteryMeterMode mode) { - Resources res = mContext.getResources(); - switch (mode) { - case BATTERY_METER_CIRCLE: - return new DockCircleBatteryMeterDrawable(res); - case BATTERY_METER_ICON_LANDSCAPE: - return new DockNormalBatteryMeterDrawable(res, true); - case BATTERY_METER_TEXT: - case BATTERY_METER_GONE: - return null; - default: - return new DockNormalBatteryMeterDrawable(res, false); - } - } - - protected class DockNormalBatteryMeterDrawable extends NormalBatteryMeterDrawable { - - public DockNormalBatteryMeterDrawable(Resources res, boolean horizontal) { - super(res, horizontal); - } - - @Override - protected int getBoltPointsArrayResource() { - return mHorizontal - ? R.array.dockbatterymeter_inverted_bolt_points - : R.array.dockbatterymeter_bolt_points; - } - } - - protected class DockCircleBatteryMeterDrawable extends CircleBatteryMeterDrawable { - public DockCircleBatteryMeterDrawable(Resources res) { - super(res); - } - - @Override - protected int getBoltPointsArrayResource() { - return R.array.dockbatterymeter_bolt_points; - } - } } |