1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.systemui.statusbar.phone; 16 17 import android.annotation.Nullable; 18 import android.content.Context; 19 import android.util.AttributeSet; 20 import android.view.Gravity; 21 import android.view.View; 22 import android.view.ViewGroup; 23 import android.widget.LinearLayout; 24 import android.widget.RelativeLayout; 25 26 import java.util.ArrayList; 27 28 /** 29 * Automatically reverses the order of children as they are added. 30 * Also reverse the width and height values of layout params 31 */ 32 public class ReverseLinearLayout extends LinearLayout { 33 34 /** If true, the layout is reversed vs. a regular linear layout */ 35 private boolean mIsLayoutReverse; 36 37 /** If true, the layout is opposite to it's natural reversity from the layout direction */ 38 private boolean mIsAlternativeOrder; 39 ReverseLinearLayout(Context context, @Nullable AttributeSet attrs)40 public ReverseLinearLayout(Context context, @Nullable AttributeSet attrs) { 41 super(context, attrs); 42 } 43 44 @Override onFinishInflate()45 protected void onFinishInflate() { 46 super.onFinishInflate(); 47 updateOrder(); 48 } 49 50 @Override addView(View child)51 public void addView(View child) { 52 reverseParams(child.getLayoutParams(), child, mIsLayoutReverse); 53 if (mIsLayoutReverse) { 54 super.addView(child, 0); 55 } else { 56 super.addView(child); 57 } 58 } 59 60 @Override addView(View child, ViewGroup.LayoutParams params)61 public void addView(View child, ViewGroup.LayoutParams params) { 62 reverseParams(params, child, mIsLayoutReverse); 63 if (mIsLayoutReverse) { 64 super.addView(child, 0, params); 65 } else { 66 super.addView(child, params); 67 } 68 } 69 70 @Override onRtlPropertiesChanged(int layoutDirection)71 public void onRtlPropertiesChanged(int layoutDirection) { 72 super.onRtlPropertiesChanged(layoutDirection); 73 updateOrder(); 74 } 75 setAlternativeOrder(boolean alternative)76 public void setAlternativeOrder(boolean alternative) { 77 mIsAlternativeOrder = alternative; 78 updateOrder(); 79 } 80 81 /** 82 * In landscape, the LinearLayout is not auto mirrored since it is vertical. Therefore we 83 * have to do it manually 84 */ updateOrder()85 private void updateOrder() { 86 boolean isLayoutRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; 87 boolean isLayoutReverse = isLayoutRtl ^ mIsAlternativeOrder; 88 89 if (mIsLayoutReverse != isLayoutReverse) { 90 // reversity changed, swap the order of all views. 91 int childCount = getChildCount(); 92 ArrayList<View> childList = new ArrayList<>(childCount); 93 for (int i = 0; i < childCount; i++) { 94 childList.add(getChildAt(i)); 95 } 96 removeAllViews(); 97 for (int i = childCount - 1; i >= 0; i--) { 98 final View child = childList.get(i); 99 super.addView(child); 100 } 101 mIsLayoutReverse = isLayoutReverse; 102 } 103 } 104 reverseParams(ViewGroup.LayoutParams params, View child, boolean isLayoutReverse)105 private static void reverseParams(ViewGroup.LayoutParams params, View child, 106 boolean isLayoutReverse) { 107 if (child instanceof Reversable) { 108 ((Reversable) child).reverse(isLayoutReverse); 109 } 110 if (child.getPaddingLeft() == child.getPaddingRight() 111 && child.getPaddingTop() == child.getPaddingBottom()) { 112 child.setPadding(child.getPaddingTop(), child.getPaddingLeft(), 113 child.getPaddingTop(), child.getPaddingLeft()); 114 } 115 if (params == null) { 116 return; 117 } 118 int width = params.width; 119 params.width = params.height; 120 params.height = width; 121 } 122 123 public interface Reversable { reverse(boolean isLayoutReverse)124 void reverse(boolean isLayoutReverse); 125 } 126 127 public static class ReverseRelativeLayout extends RelativeLayout implements Reversable { 128 ReverseRelativeLayout(Context context)129 public ReverseRelativeLayout(Context context) { 130 super(context); 131 } 132 133 @Override reverse(boolean isLayoutReverse)134 public void reverse(boolean isLayoutReverse) { 135 updateGravity(isLayoutReverse); 136 reverseGroup(this, isLayoutReverse); 137 } 138 139 private int mDefaultGravity = Gravity.NO_GRAVITY; setDefaultGravity(int gravity)140 public void setDefaultGravity(int gravity) { 141 mDefaultGravity = gravity; 142 } 143 updateGravity(boolean isLayoutReverse)144 public void updateGravity(boolean isLayoutReverse) { 145 // Flip gravity if top of bottom is used 146 if (mDefaultGravity != Gravity.TOP && mDefaultGravity != Gravity.BOTTOM) return; 147 148 // Use the default (intended for 270 LTR and 90 RTL) unless layout is otherwise 149 int gravityToApply = mDefaultGravity; 150 if (isLayoutReverse) { 151 gravityToApply = mDefaultGravity == Gravity.TOP ? Gravity.BOTTOM : Gravity.TOP; 152 } 153 154 if (getGravity() != gravityToApply) setGravity(gravityToApply); 155 } 156 } 157 reverseGroup(ViewGroup group, boolean isLayoutReverse)158 private static void reverseGroup(ViewGroup group, boolean isLayoutReverse) { 159 for (int i = 0; i < group.getChildCount(); i++) { 160 final View child = group.getChildAt(i); 161 reverseParams(child.getLayoutParams(), child, isLayoutReverse); 162 163 // Recursively reverse all children 164 if (child instanceof ViewGroup) { 165 reverseGroup((ViewGroup) child, isLayoutReverse); 166 } 167 } 168 } 169 170 } 171