Skip to content

Commit 1c3bfb2

Browse files
committed
2 parents aac8fdd + 7f0303e commit 1c3bfb2

File tree

4 files changed

+326
-0
lines changed

4 files changed

+326
-0
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "react_native_target", "rn_android_library")
2+
3+
rn_android_library(
4+
name = "progressbar",
5+
srcs = glob(["*.java"]),
6+
visibility = [
7+
"PUBLIC",
8+
],
9+
deps = [
10+
YOGA_TARGET,
11+
react_native_dep("third-party/java/infer-annotations:infer-annotations"),
12+
react_native_dep("third-party/java/jsr-305:jsr-305"),
13+
react_native_target("java/com/facebook/react/bridge:bridge"),
14+
react_native_target("java/com/facebook/react/common:common"),
15+
react_native_target("java/com/facebook/react/module/annotations:annotations"),
16+
react_native_target("java/com/facebook/react/uimanager:uimanager"),
17+
react_native_target("java/com/facebook/react/uimanager/annotations:annotations"),
18+
],
19+
)
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright (c) Facebook, Inc. and its affiliates.
2+
3+
// This source code is licensed under the MIT license found in the
4+
// LICENSE file in the root directory of this source tree.
5+
6+
package com.facebook.react.views.progressbar;
7+
8+
import javax.annotation.Nullable;
9+
10+
import android.content.Context;
11+
import android.graphics.PorterDuff;
12+
import android.graphics.drawable.Drawable;
13+
import android.view.View;
14+
import android.view.ViewGroup;
15+
import android.widget.FrameLayout;
16+
import android.widget.ProgressBar;
17+
18+
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
19+
20+
/**
21+
* Controls an enclosing ProgressBar. Exists so that the ProgressBar can be recreated if
22+
* the style would change.
23+
*/
24+
/* package */ class ProgressBarContainerView extends FrameLayout {
25+
private static final int MAX_PROGRESS = 1000;
26+
27+
private @Nullable Integer mColor;
28+
private boolean mIndeterminate = true;
29+
private boolean mAnimating = true;
30+
private double mProgress;
31+
private @Nullable ProgressBar mProgressBar;
32+
33+
public ProgressBarContainerView(Context context) {
34+
super(context);
35+
}
36+
37+
public void setStyle(@Nullable String styleName) {
38+
int style = ReactProgressBarViewManager.getStyleFromString(styleName);
39+
mProgressBar = ReactProgressBarViewManager.createProgressBar(getContext(), style);
40+
mProgressBar.setMax(MAX_PROGRESS);
41+
removeAllViews();
42+
addView(
43+
mProgressBar,
44+
new ViewGroup.LayoutParams(
45+
ViewGroup.LayoutParams.MATCH_PARENT,
46+
ViewGroup.LayoutParams.MATCH_PARENT));
47+
}
48+
49+
public void setColor(@Nullable Integer color) {
50+
this.mColor = color;
51+
}
52+
53+
public void setIndeterminate(boolean indeterminate) {
54+
mIndeterminate = indeterminate;
55+
}
56+
57+
public void setProgress(double progress) {
58+
mProgress = progress;
59+
}
60+
61+
public void setAnimating(boolean animating) {
62+
mAnimating = animating;
63+
}
64+
65+
public void apply() {
66+
if (mProgressBar == null) {
67+
throw new JSApplicationIllegalArgumentException("setStyle() not called");
68+
}
69+
70+
mProgressBar.setIndeterminate(mIndeterminate);
71+
setColor(mProgressBar);
72+
mProgressBar.setProgress((int) (mProgress * MAX_PROGRESS));
73+
if (mAnimating) {
74+
mProgressBar.setVisibility(View.VISIBLE);
75+
} else {
76+
mProgressBar.setVisibility(View.GONE);
77+
}
78+
}
79+
80+
private void setColor(ProgressBar progressBar) {
81+
Drawable drawable;
82+
if (progressBar.isIndeterminate()) {
83+
drawable = progressBar.getIndeterminateDrawable();
84+
} else {
85+
drawable = progressBar.getProgressDrawable();
86+
}
87+
88+
if (drawable == null) {
89+
return;
90+
}
91+
92+
if (mColor != null) {
93+
drawable.setColorFilter(mColor, PorterDuff.Mode.SRC_IN);
94+
} else {
95+
drawable.clearColorFilter();
96+
}
97+
}
98+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.views.progressbar;
9+
10+
import android.util.SparseIntArray;
11+
import android.view.View;
12+
import android.view.ViewGroup;
13+
import android.widget.ProgressBar;
14+
import com.facebook.react.uimanager.LayoutShadowNode;
15+
import com.facebook.react.uimanager.annotations.ReactProp;
16+
import com.facebook.yoga.YogaMeasureFunction;
17+
import com.facebook.yoga.YogaMeasureMode;
18+
import com.facebook.yoga.YogaMeasureOutput;
19+
import com.facebook.yoga.YogaNode;
20+
import java.util.HashSet;
21+
import java.util.Set;
22+
import javax.annotation.Nullable;
23+
24+
/**
25+
* Node responsible for holding the style of the ProgressBar, see under
26+
* {@link android.R.attr.progressBarStyle} for possible styles. ReactProgressBarViewManager
27+
* manages how this style is applied to the ProgressBar.
28+
*/
29+
public class ProgressBarShadowNode extends LayoutShadowNode implements YogaMeasureFunction {
30+
31+
private String mStyle = ReactProgressBarViewManager.DEFAULT_STYLE;
32+
33+
private final SparseIntArray mHeight;
34+
private final SparseIntArray mWidth;
35+
private final Set<Integer> mMeasured;
36+
37+
public ProgressBarShadowNode() {
38+
mHeight = new SparseIntArray();
39+
mWidth = new SparseIntArray();
40+
mMeasured = new HashSet<>();
41+
initMeasureFunction();
42+
}
43+
44+
private void initMeasureFunction() {
45+
setMeasureFunction(this);
46+
}
47+
48+
public @Nullable String getStyle() {
49+
return mStyle;
50+
}
51+
52+
@ReactProp(name = ReactProgressBarViewManager.PROP_STYLE)
53+
public void setStyle(@Nullable String style) {
54+
mStyle = style == null ? ReactProgressBarViewManager.DEFAULT_STYLE : style;
55+
}
56+
57+
@Override
58+
public long measure(
59+
YogaNode node,
60+
float width,
61+
YogaMeasureMode widthMode,
62+
float height,
63+
YogaMeasureMode heightMode) {
64+
final int style = ReactProgressBarViewManager.getStyleFromString(getStyle());
65+
if (!mMeasured.contains(style)) {
66+
ProgressBar progressBar = ReactProgressBarViewManager.createProgressBar(getThemedContext(), style);
67+
final int spec = View.MeasureSpec.makeMeasureSpec(
68+
ViewGroup.LayoutParams.WRAP_CONTENT,
69+
View.MeasureSpec.UNSPECIFIED);
70+
progressBar.measure(spec, spec);
71+
mHeight.put(style, progressBar.getMeasuredHeight());
72+
mWidth.put(style, progressBar.getMeasuredWidth());
73+
mMeasured.add(style);
74+
}
75+
76+
return YogaMeasureOutput.make(mWidth.get(style), mHeight.get(style));
77+
}
78+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.views.progressbar;
9+
10+
import javax.annotation.Nullable;
11+
12+
import android.content.Context;
13+
import android.widget.ProgressBar;
14+
15+
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
16+
import com.facebook.react.module.annotations.ReactModule;
17+
import com.facebook.react.uimanager.BaseViewManager;
18+
import com.facebook.react.uimanager.annotations.ReactProp;
19+
import com.facebook.react.uimanager.ThemedReactContext;
20+
import com.facebook.react.uimanager.ViewProps;
21+
22+
/**
23+
* Manages instances of ProgressBar. ProgressBar is wrapped in a ProgressBarContainerView because
24+
* the style of the ProgressBar can only be set in the constructor; whenever the style of a
25+
* ProgressBar changes, we have to drop the existing ProgressBar (if there is one) and create a new
26+
* one with the style given.
27+
*/
28+
@ReactModule(name = ReactProgressBarViewManager.REACT_CLASS)
29+
public class ReactProgressBarViewManager extends
30+
BaseViewManager<ProgressBarContainerView, ProgressBarShadowNode> {
31+
32+
public static final String REACT_CLASS = "AndroidProgressBar";
33+
34+
/* package */ static final String PROP_STYLE = "styleAttr";
35+
/* package */ static final String PROP_INDETERMINATE = "indeterminate";
36+
/* package */ static final String PROP_PROGRESS = "progress";
37+
/* package */ static final String PROP_ANIMATING = "animating";
38+
39+
/* package */ static final String DEFAULT_STYLE = "Normal";
40+
41+
private static Object sProgressBarCtorLock = new Object();
42+
43+
/**
44+
* We create ProgressBars on both the UI and shadow threads. There is a race condition in the
45+
* ProgressBar constructor that may cause crashes when two ProgressBars are constructed at the
46+
* same time on two different threads. This static ctor wrapper protects against that.
47+
*/
48+
public static ProgressBar createProgressBar(Context context, int style) {
49+
synchronized (sProgressBarCtorLock) {
50+
return new ProgressBar(context, null, style);
51+
}
52+
}
53+
54+
@Override
55+
public String getName() {
56+
return REACT_CLASS;
57+
}
58+
59+
@Override
60+
protected ProgressBarContainerView createViewInstance(ThemedReactContext context) {
61+
return new ProgressBarContainerView(context);
62+
}
63+
64+
@ReactProp(name = PROP_STYLE)
65+
public void setStyle(ProgressBarContainerView view, @Nullable String styleName) {
66+
view.setStyle(styleName);
67+
}
68+
69+
@ReactProp(name = ViewProps.COLOR, customType = "Color")
70+
public void setColor(ProgressBarContainerView view, @Nullable Integer color) {
71+
view.setColor(color);
72+
}
73+
74+
@ReactProp(name = PROP_INDETERMINATE)
75+
public void setIndeterminate(ProgressBarContainerView view, boolean indeterminate) {
76+
view.setIndeterminate(indeterminate);
77+
}
78+
79+
@ReactProp(name = PROP_PROGRESS)
80+
public void setProgress(ProgressBarContainerView view, double progress) {
81+
view.setProgress(progress);
82+
}
83+
84+
@ReactProp(name = PROP_ANIMATING)
85+
public void setAnimating(ProgressBarContainerView view, boolean animating) {
86+
view.setAnimating(animating);
87+
}
88+
89+
@Override
90+
public ProgressBarShadowNode createShadowNodeInstance() {
91+
return new ProgressBarShadowNode();
92+
}
93+
94+
@Override
95+
public Class<ProgressBarShadowNode> getShadowNodeClass() {
96+
return ProgressBarShadowNode.class;
97+
}
98+
99+
@Override
100+
public void updateExtraData(ProgressBarContainerView root, Object extraData) {
101+
// do nothing
102+
}
103+
104+
@Override
105+
protected void onAfterUpdateTransaction(ProgressBarContainerView view) {
106+
view.apply();
107+
}
108+
109+
/* package */ static int getStyleFromString(@Nullable String styleStr) {
110+
if (styleStr == null) {
111+
throw new JSApplicationIllegalArgumentException(
112+
"ProgressBar needs to have a style, null received");
113+
} else if (styleStr.equals("Horizontal")) {
114+
return android.R.attr.progressBarStyleHorizontal;
115+
} else if (styleStr.equals("Small")) {
116+
return android.R.attr.progressBarStyleSmall;
117+
} else if (styleStr.equals("Large")) {
118+
return android.R.attr.progressBarStyleLarge;
119+
} else if (styleStr.equals("Inverse")) {
120+
return android.R.attr.progressBarStyleInverse;
121+
} else if (styleStr.equals("SmallInverse")) {
122+
return android.R.attr.progressBarStyleSmallInverse;
123+
} else if (styleStr.equals("LargeInverse")) {
124+
return android.R.attr.progressBarStyleLargeInverse;
125+
} else if (styleStr.equals("Normal")) {
126+
return android.R.attr.progressBarStyle;
127+
} else {
128+
throw new JSApplicationIllegalArgumentException("Unknown ProgressBar style: " + styleStr);
129+
}
130+
}
131+
}

0 commit comments

Comments
 (0)