2017-01-11 05:51:45 -07:00
|
|
|
package com.brentvatne.exoplayer;
|
|
|
|
|
|
|
|
import android.content.Context;
|
|
|
|
import android.net.Uri;
|
|
|
|
import android.text.TextUtils;
|
|
|
|
|
2018-06-02 03:24:13 -06:00
|
|
|
import com.facebook.react.bridge.Dynamic;
|
2018-06-11 22:25:58 -06:00
|
|
|
import com.facebook.react.bridge.ReadableArray;
|
2017-01-11 05:51:45 -07:00
|
|
|
import com.facebook.react.bridge.ReadableMap;
|
|
|
|
import com.facebook.react.common.MapBuilder;
|
|
|
|
import com.facebook.react.uimanager.ThemedReactContext;
|
|
|
|
import com.facebook.react.uimanager.ViewGroupManager;
|
|
|
|
import com.facebook.react.uimanager.annotations.ReactProp;
|
2018-08-01 07:58:02 -06:00
|
|
|
import com.google.android.exoplayer2.DefaultLoadControl;
|
2017-01-11 05:51:45 -07:00
|
|
|
import com.google.android.exoplayer2.upstream.RawResourceDataSource;
|
|
|
|
|
2017-10-02 12:11:41 -06:00
|
|
|
import java.util.HashMap;
|
2017-01-11 05:51:45 -07:00
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
import javax.annotation.Nullable;
|
|
|
|
|
|
|
|
public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerView> {
|
|
|
|
|
|
|
|
private static final String REACT_CLASS = "RCTVideo";
|
|
|
|
|
|
|
|
private static final String PROP_SRC = "src";
|
|
|
|
private static final String PROP_SRC_URI = "uri";
|
|
|
|
private static final String PROP_SRC_TYPE = "type";
|
2018-01-13 13:29:53 -07:00
|
|
|
private static final String PROP_SRC_HEADERS = "requestHeaders";
|
2017-01-11 05:51:45 -07:00
|
|
|
private static final String PROP_RESIZE_MODE = "resizeMode";
|
|
|
|
private static final String PROP_REPEAT = "repeat";
|
2018-07-17 15:14:21 -06:00
|
|
|
private static final String PROP_SELECTED_AUDIO_TRACK = "selectedAudioTrack";
|
|
|
|
private static final String PROP_SELECTED_AUDIO_TRACK_TYPE = "type";
|
|
|
|
private static final String PROP_SELECTED_AUDIO_TRACK_VALUE = "value";
|
2018-06-02 03:24:13 -06:00
|
|
|
private static final String PROP_SELECTED_TEXT_TRACK = "selectedTextTrack";
|
|
|
|
private static final String PROP_SELECTED_TEXT_TRACK_TYPE = "type";
|
|
|
|
private static final String PROP_SELECTED_TEXT_TRACK_VALUE = "value";
|
2018-06-11 22:25:58 -06:00
|
|
|
private static final String PROP_TEXT_TRACKS = "textTracks";
|
2017-01-11 05:51:45 -07:00
|
|
|
private static final String PROP_PAUSED = "paused";
|
|
|
|
private static final String PROP_MUTED = "muted";
|
|
|
|
private static final String PROP_VOLUME = "volume";
|
2018-08-02 01:20:08 -06:00
|
|
|
private static final String PROP_BUFFER_CONFIG = "bufferConfig";
|
|
|
|
private static final String PROP_BUFFER_CONFIG_MIN_BUFFER_MS = "minBufferMs";
|
|
|
|
private static final String PROP_BUFFER_CONFIG_MAX_BUFFER_MS = "maxBufferMs";
|
|
|
|
private static final String PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS = "bufferForPlaybackMs";
|
|
|
|
private static final String PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = "bufferForPlaybackAfterRebufferMs";
|
2017-03-31 10:15:39 -06:00
|
|
|
private static final String PROP_PROGRESS_UPDATE_INTERVAL = "progressUpdateInterval";
|
2018-08-25 10:23:11 -06:00
|
|
|
private static final String PROP_REPORT_BANDWIDTH = "reportBandwidth";
|
2017-01-11 05:51:45 -07:00
|
|
|
private static final String PROP_SEEK = "seek";
|
|
|
|
private static final String PROP_RATE = "rate";
|
2019-02-10 20:47:07 -07:00
|
|
|
private static final String PROP_MIN_LOAD_RETRY_COUNT = "minLoadRetryCount";
|
2018-11-26 15:50:31 -07:00
|
|
|
private static final String PROP_MAXIMUM_BIT_RATE = "maxBitRate";
|
2017-01-11 05:51:45 -07:00
|
|
|
private static final String PROP_PLAY_IN_BACKGROUND = "playInBackground";
|
|
|
|
private static final String PROP_DISABLE_FOCUS = "disableFocus";
|
2018-05-18 14:30:01 -06:00
|
|
|
private static final String PROP_FULLSCREEN = "fullscreen";
|
2018-06-08 01:01:13 -06:00
|
|
|
private static final String PROP_USE_TEXTURE_VIEW = "useTextureView";
|
2018-08-24 04:03:46 -06:00
|
|
|
private static final String PROP_SELECTED_VIDEO_TRACK = "selectedVideoTrack";
|
|
|
|
private static final String PROP_SELECTED_VIDEO_TRACK_TYPE = "type";
|
|
|
|
private static final String PROP_SELECTED_VIDEO_TRACK_VALUE = "value";
|
2018-11-28 05:56:58 -07:00
|
|
|
private static final String PROP_HIDE_SHUTTER_VIEW = "hideShutterView";
|
2019-01-04 02:28:32 -07:00
|
|
|
private static final String PROP_CONTROLS = "controls";
|
2017-01-11 05:51:45 -07:00
|
|
|
|
2019-09-16 14:29:31 -06:00
|
|
|
private ReactExoplayerConfig config;
|
|
|
|
|
|
|
|
public ReactExoplayerViewManager(ReactExoplayerConfig config) {
|
|
|
|
this.config = config;
|
|
|
|
}
|
|
|
|
|
2017-01-11 05:51:45 -07:00
|
|
|
@Override
|
|
|
|
public String getName() {
|
|
|
|
return REACT_CLASS;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected ReactExoplayerView createViewInstance(ThemedReactContext themedReactContext) {
|
2019-09-16 14:29:31 -06:00
|
|
|
return new ReactExoplayerView(themedReactContext, config);
|
2017-01-11 05:51:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onDropViewInstance(ReactExoplayerView view) {
|
|
|
|
view.cleanUpResources();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public @Nullable Map<String, Object> getExportedCustomDirectEventTypeConstants() {
|
|
|
|
MapBuilder.Builder<String, Object> builder = MapBuilder.builder();
|
|
|
|
for (String event : VideoEventEmitter.Events) {
|
|
|
|
builder.put(event, MapBuilder.of("registrationName", event));
|
|
|
|
}
|
|
|
|
return builder.build();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public @Nullable Map<String, Object> getExportedViewConstants() {
|
|
|
|
return MapBuilder.<String, Object>of(
|
|
|
|
"ScaleNone", Integer.toString(ResizeMode.RESIZE_MODE_FIT),
|
|
|
|
"ScaleAspectFit", Integer.toString(ResizeMode.RESIZE_MODE_FIT),
|
|
|
|
"ScaleToFill", Integer.toString(ResizeMode.RESIZE_MODE_FILL),
|
|
|
|
"ScaleAspectFill", Integer.toString(ResizeMode.RESIZE_MODE_CENTER_CROP)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
@ReactProp(name = PROP_SRC)
|
|
|
|
public void setSrc(final ReactExoplayerView videoView, @Nullable ReadableMap src) {
|
|
|
|
Context context = videoView.getContext().getApplicationContext();
|
|
|
|
String uriString = src.hasKey(PROP_SRC_URI) ? src.getString(PROP_SRC_URI) : null;
|
|
|
|
String extension = src.hasKey(PROP_SRC_TYPE) ? src.getString(PROP_SRC_TYPE) : null;
|
2017-10-02 12:11:41 -06:00
|
|
|
Map<String, String> headers = src.hasKey(PROP_SRC_HEADERS) ? toStringMap(src.getMap(PROP_SRC_HEADERS)) : null;
|
|
|
|
|
2017-01-11 05:51:45 -07:00
|
|
|
|
|
|
|
if (TextUtils.isEmpty(uriString)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (startsWithValidScheme(uriString)) {
|
|
|
|
Uri srcUri = Uri.parse(uriString);
|
|
|
|
|
|
|
|
if (srcUri != null) {
|
2017-10-02 12:11:41 -06:00
|
|
|
videoView.setSrc(srcUri, extension, headers);
|
2017-01-11 05:51:45 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int identifier = context.getResources().getIdentifier(
|
2017-01-31 05:49:23 -07:00
|
|
|
uriString,
|
|
|
|
"drawable",
|
|
|
|
context.getPackageName()
|
|
|
|
);
|
|
|
|
if (identifier == 0) {
|
|
|
|
identifier = context.getResources().getIdentifier(
|
2017-01-11 05:51:45 -07:00
|
|
|
uriString,
|
|
|
|
"raw",
|
|
|
|
context.getPackageName()
|
2017-01-31 05:49:23 -07:00
|
|
|
);
|
|
|
|
}
|
2017-01-11 05:51:45 -07:00
|
|
|
if (identifier > 0) {
|
|
|
|
Uri srcUri = RawResourceDataSource.buildRawResourceUri(identifier);
|
|
|
|
if (srcUri != null) {
|
|
|
|
videoView.setRawSrc(srcUri, extension);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@ReactProp(name = PROP_RESIZE_MODE)
|
|
|
|
public void setResizeMode(final ReactExoplayerView videoView, final String resizeModeOrdinalString) {
|
|
|
|
videoView.setResizeModeModifier(convertToIntDef(resizeModeOrdinalString));
|
|
|
|
}
|
|
|
|
|
|
|
|
@ReactProp(name = PROP_REPEAT, defaultBoolean = false)
|
|
|
|
public void setRepeat(final ReactExoplayerView videoView, final boolean repeat) {
|
|
|
|
videoView.setRepeatModifier(repeat);
|
|
|
|
}
|
|
|
|
|
2018-08-24 04:03:46 -06:00
|
|
|
@ReactProp(name = PROP_SELECTED_VIDEO_TRACK)
|
|
|
|
public void setSelectedVideoTrack(final ReactExoplayerView videoView,
|
|
|
|
@Nullable ReadableMap selectedVideoTrack) {
|
|
|
|
String typeString = null;
|
|
|
|
Dynamic value = null;
|
|
|
|
if (selectedVideoTrack != null) {
|
|
|
|
typeString = selectedVideoTrack.hasKey(PROP_SELECTED_VIDEO_TRACK_TYPE)
|
|
|
|
? selectedVideoTrack.getString(PROP_SELECTED_VIDEO_TRACK_TYPE) : null;
|
|
|
|
value = selectedVideoTrack.hasKey(PROP_SELECTED_VIDEO_TRACK_VALUE)
|
|
|
|
? selectedVideoTrack.getDynamic(PROP_SELECTED_VIDEO_TRACK_VALUE) : null;
|
|
|
|
}
|
|
|
|
videoView.setSelectedVideoTrack(typeString, value);
|
|
|
|
}
|
|
|
|
|
2018-07-17 15:14:21 -06:00
|
|
|
@ReactProp(name = PROP_SELECTED_AUDIO_TRACK)
|
|
|
|
public void setSelectedAudioTrack(final ReactExoplayerView videoView,
|
|
|
|
@Nullable ReadableMap selectedAudioTrack) {
|
|
|
|
String typeString = null;
|
|
|
|
Dynamic value = null;
|
|
|
|
if (selectedAudioTrack != null) {
|
|
|
|
typeString = selectedAudioTrack.hasKey(PROP_SELECTED_AUDIO_TRACK_TYPE)
|
|
|
|
? selectedAudioTrack.getString(PROP_SELECTED_AUDIO_TRACK_TYPE) : null;
|
|
|
|
value = selectedAudioTrack.hasKey(PROP_SELECTED_AUDIO_TRACK_VALUE)
|
|
|
|
? selectedAudioTrack.getDynamic(PROP_SELECTED_AUDIO_TRACK_VALUE) : null;
|
|
|
|
}
|
|
|
|
videoView.setSelectedAudioTrack(typeString, value);
|
|
|
|
}
|
|
|
|
|
2018-06-02 03:24:13 -06:00
|
|
|
@ReactProp(name = PROP_SELECTED_TEXT_TRACK)
|
|
|
|
public void setSelectedTextTrack(final ReactExoplayerView videoView,
|
|
|
|
@Nullable ReadableMap selectedTextTrack) {
|
2018-06-20 16:34:36 -06:00
|
|
|
String typeString = null;
|
|
|
|
Dynamic value = null;
|
|
|
|
if (selectedTextTrack != null) {
|
|
|
|
typeString = selectedTextTrack.hasKey(PROP_SELECTED_TEXT_TRACK_TYPE)
|
|
|
|
? selectedTextTrack.getString(PROP_SELECTED_TEXT_TRACK_TYPE) : null;
|
|
|
|
value = selectedTextTrack.hasKey(PROP_SELECTED_TEXT_TRACK_VALUE)
|
|
|
|
? selectedTextTrack.getDynamic(PROP_SELECTED_TEXT_TRACK_VALUE) : null;
|
|
|
|
}
|
2018-06-02 03:24:13 -06:00
|
|
|
videoView.setSelectedTextTrack(typeString, value);
|
|
|
|
}
|
|
|
|
|
2018-06-11 22:25:58 -06:00
|
|
|
@ReactProp(name = PROP_TEXT_TRACKS)
|
|
|
|
public void setPropTextTracks(final ReactExoplayerView videoView,
|
|
|
|
@Nullable ReadableArray textTracks) {
|
|
|
|
videoView.setTextTracks(textTracks);
|
|
|
|
}
|
|
|
|
|
2017-01-11 05:51:45 -07:00
|
|
|
@ReactProp(name = PROP_PAUSED, defaultBoolean = false)
|
|
|
|
public void setPaused(final ReactExoplayerView videoView, final boolean paused) {
|
|
|
|
videoView.setPausedModifier(paused);
|
|
|
|
}
|
|
|
|
|
|
|
|
@ReactProp(name = PROP_MUTED, defaultBoolean = false)
|
|
|
|
public void setMuted(final ReactExoplayerView videoView, final boolean muted) {
|
|
|
|
videoView.setMutedModifier(muted);
|
|
|
|
}
|
|
|
|
|
|
|
|
@ReactProp(name = PROP_VOLUME, defaultFloat = 1.0f)
|
|
|
|
public void setVolume(final ReactExoplayerView videoView, final float volume) {
|
|
|
|
videoView.setVolumeModifier(volume);
|
|
|
|
}
|
|
|
|
|
2017-03-31 10:15:39 -06:00
|
|
|
@ReactProp(name = PROP_PROGRESS_UPDATE_INTERVAL, defaultFloat = 250.0f)
|
|
|
|
public void setProgressUpdateInterval(final ReactExoplayerView videoView, final float progressUpdateInterval) {
|
|
|
|
videoView.setProgressUpdateInterval(progressUpdateInterval);
|
|
|
|
}
|
|
|
|
|
2018-08-25 10:23:11 -06:00
|
|
|
@ReactProp(name = PROP_REPORT_BANDWIDTH, defaultBoolean = false)
|
|
|
|
public void setReportBandwidth(final ReactExoplayerView videoView, final boolean reportBandwidth) {
|
2018-11-01 10:11:57 -06:00
|
|
|
videoView.setReportBandwidth(reportBandwidth);
|
2018-08-25 10:23:11 -06:00
|
|
|
}
|
|
|
|
|
2017-01-11 05:51:45 -07:00
|
|
|
@ReactProp(name = PROP_SEEK)
|
|
|
|
public void setSeek(final ReactExoplayerView videoView, final float seek) {
|
|
|
|
videoView.seekTo(Math.round(seek * 1000f));
|
|
|
|
}
|
|
|
|
|
|
|
|
@ReactProp(name = PROP_RATE)
|
|
|
|
public void setRate(final ReactExoplayerView videoView, final float rate) {
|
|
|
|
videoView.setRateModifier(rate);
|
|
|
|
}
|
|
|
|
|
2018-10-29 10:53:52 -06:00
|
|
|
@ReactProp(name = PROP_MAXIMUM_BIT_RATE)
|
2018-11-26 15:50:31 -07:00
|
|
|
public void setMaxBitRate(final ReactExoplayerView videoView, final int maxBitRate) {
|
|
|
|
videoView.setMaxBitRateModifier(maxBitRate);
|
2018-10-29 10:53:52 -06:00
|
|
|
}
|
|
|
|
|
2019-02-10 20:47:07 -07:00
|
|
|
@ReactProp(name = PROP_MIN_LOAD_RETRY_COUNT)
|
|
|
|
public void minLoadRetryCount(final ReactExoplayerView videoView, final int minLoadRetryCount) {
|
|
|
|
videoView.setMinLoadRetryCountModifier(minLoadRetryCount);
|
2019-01-24 06:19:37 -07:00
|
|
|
}
|
|
|
|
|
2017-01-11 05:51:45 -07:00
|
|
|
@ReactProp(name = PROP_PLAY_IN_BACKGROUND, defaultBoolean = false)
|
|
|
|
public void setPlayInBackground(final ReactExoplayerView videoView, final boolean playInBackground) {
|
|
|
|
videoView.setPlayInBackground(playInBackground);
|
|
|
|
}
|
|
|
|
|
|
|
|
@ReactProp(name = PROP_DISABLE_FOCUS, defaultBoolean = false)
|
|
|
|
public void setDisableFocus(final ReactExoplayerView videoView, final boolean disableFocus) {
|
|
|
|
videoView.setDisableFocus(disableFocus);
|
|
|
|
}
|
|
|
|
|
2018-05-18 14:30:01 -06:00
|
|
|
@ReactProp(name = PROP_FULLSCREEN, defaultBoolean = false)
|
|
|
|
public void setFullscreen(final ReactExoplayerView videoView, final boolean fullscreen) {
|
|
|
|
videoView.setFullscreen(fullscreen);
|
|
|
|
}
|
|
|
|
|
2018-10-13 21:23:30 -06:00
|
|
|
@ReactProp(name = PROP_USE_TEXTURE_VIEW, defaultBoolean = true)
|
2018-06-08 01:01:13 -06:00
|
|
|
public void setUseTextureView(final ReactExoplayerView videoView, final boolean useTextureView) {
|
|
|
|
videoView.setUseTextureView(useTextureView);
|
|
|
|
}
|
|
|
|
|
2018-11-28 05:56:58 -07:00
|
|
|
@ReactProp(name = PROP_HIDE_SHUTTER_VIEW, defaultBoolean = false)
|
|
|
|
public void setHideShutterView(final ReactExoplayerView videoView, final boolean hideShutterView) {
|
|
|
|
videoView.setHideShutterView(hideShutterView);
|
|
|
|
}
|
|
|
|
|
2019-01-04 02:28:32 -07:00
|
|
|
@ReactProp(name = PROP_CONTROLS, defaultBoolean = false)
|
|
|
|
public void setControls(final ReactExoplayerView videoView, final boolean controls) {
|
|
|
|
videoView.setControls(controls);
|
|
|
|
}
|
|
|
|
|
2018-08-03 16:54:55 -06:00
|
|
|
@ReactProp(name = PROP_BUFFER_CONFIG)
|
2018-08-02 01:20:08 -06:00
|
|
|
public void setBufferConfig(final ReactExoplayerView videoView, @Nullable ReadableMap bufferConfig) {
|
2018-08-01 07:58:02 -06:00
|
|
|
int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS;
|
|
|
|
int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS;
|
|
|
|
int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS;
|
|
|
|
int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS;
|
2018-08-02 01:20:08 -06:00
|
|
|
if (bufferConfig != null) {
|
|
|
|
minBufferMs = bufferConfig.hasKey(PROP_BUFFER_CONFIG_MIN_BUFFER_MS)
|
|
|
|
? bufferConfig.getInt(PROP_BUFFER_CONFIG_MIN_BUFFER_MS) : minBufferMs;
|
|
|
|
maxBufferMs = bufferConfig.hasKey(PROP_BUFFER_CONFIG_MAX_BUFFER_MS)
|
|
|
|
? bufferConfig.getInt(PROP_BUFFER_CONFIG_MAX_BUFFER_MS) : maxBufferMs;
|
|
|
|
bufferForPlaybackMs = bufferConfig.hasKey(PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS)
|
|
|
|
? bufferConfig.getInt(PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS) : bufferForPlaybackMs;
|
|
|
|
bufferForPlaybackAfterRebufferMs = bufferConfig.hasKey(PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS)
|
|
|
|
? bufferConfig.getInt(PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS) : bufferForPlaybackAfterRebufferMs;
|
|
|
|
videoView.setBufferConfig(minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs);
|
2018-08-01 07:58:02 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-11 05:51:45 -07:00
|
|
|
private boolean startsWithValidScheme(String uriString) {
|
|
|
|
return uriString.startsWith("http://")
|
|
|
|
|| uriString.startsWith("https://")
|
|
|
|
|| uriString.startsWith("content://")
|
|
|
|
|| uriString.startsWith("file://")
|
|
|
|
|| uriString.startsWith("asset://");
|
|
|
|
}
|
|
|
|
|
|
|
|
private @ResizeMode.Mode int convertToIntDef(String resizeModeOrdinalString) {
|
|
|
|
if (!TextUtils.isEmpty(resizeModeOrdinalString)) {
|
|
|
|
int resizeModeOrdinal = Integer.parseInt(resizeModeOrdinalString);
|
|
|
|
return ResizeMode.toResizeMode(resizeModeOrdinal);
|
|
|
|
}
|
|
|
|
return ResizeMode.RESIZE_MODE_FIT;
|
|
|
|
}
|
2017-10-02 12:11:41 -06:00
|
|
|
|
|
|
|
/**
|
|
|
|
* toStringMap converts a {@link ReadableMap} into a HashMap.
|
|
|
|
*
|
|
|
|
* @param readableMap The ReadableMap to be conveted.
|
|
|
|
* @return A HashMap containing the data that was in the ReadableMap.
|
|
|
|
* @see 'Adapted from https://github.com/artemyarulin/react-native-eval/blob/master/android/src/main/java/com/evaluator/react/ConversionUtil.java'
|
|
|
|
*/
|
|
|
|
public static Map<String, String> toStringMap(@Nullable ReadableMap readableMap) {
|
|
|
|
if (readableMap == null)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
com.facebook.react.bridge.ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
|
|
|
|
if (!iterator.hasNextKey())
|
|
|
|
return null;
|
|
|
|
|
|
|
|
Map<String, String> result = new HashMap<>();
|
|
|
|
while (iterator.hasNextKey()) {
|
|
|
|
String key = iterator.nextKey();
|
|
|
|
result.put(key, readableMap.getString(key));
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2017-01-11 05:51:45 -07:00
|
|
|
}
|