/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.react.modules.timepicker; import javax.annotation.Nullable; import android.app.Activity; import android.app.DialogFragment; import android.app.FragmentManager; import android.app.TimePickerDialog.OnTimeSetListener; import android.content.DialogInterface; import android.content.DialogInterface.OnDismissListener; import android.os.Bundle; import android.widget.TimePicker; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.module.annotations.ReactModule; /** * {@link NativeModule} that allows JS to show a native time picker dialog and get called back when * the user selects a time. */ @ReactModule(name = "TimePickerAndroid") public class TimePickerDialogModule extends ReactContextBaseJavaModule { @VisibleForTesting public static final String FRAGMENT_TAG = "TimePickerAndroid"; private static final String ERROR_NO_ACTIVITY = "E_NO_ACTIVITY"; /* package */ static final String ARG_HOUR = "hour"; /* package */ static final String ARG_MINUTE = "minute"; /* package */ static final String ARG_IS24HOUR = "is24Hour"; /* package */ static final String ACTION_TIME_SET = "timeSetAction"; /* package */ static final String ACTION_DISMISSED = "dismissedAction"; public TimePickerDialogModule(ReactApplicationContext reactContext) { super(reactContext); } @Override public String getName() { return "TimePickerAndroid"; } private class TimePickerDialogListener implements OnTimeSetListener, OnDismissListener { private final Promise mPromise; private boolean mPromiseResolved = false; public TimePickerDialogListener(Promise promise) { mPromise = promise; } @Override public void onTimeSet(TimePicker view, int hour, int minute) { if (!mPromiseResolved && getReactApplicationContext().hasActiveCatalystInstance()) { WritableMap result = new WritableNativeMap(); result.putString("action", ACTION_TIME_SET); result.putInt("hour", hour); result.putInt("minute", minute); mPromise.resolve(result); mPromiseResolved = true; } } @Override public void onDismiss(DialogInterface dialog) { if (!mPromiseResolved && getReactApplicationContext().hasActiveCatalystInstance()) { WritableMap result = new WritableNativeMap(); result.putString("action", ACTION_DISMISSED); mPromise.resolve(result); mPromiseResolved = true; } } } @ReactMethod public void open(@Nullable final ReadableMap options, Promise promise) { Activity activity = getCurrentActivity(); if (activity == null) { promise.reject( ERROR_NO_ACTIVITY, "Tried to open a TimePicker dialog while not attached to an Activity"); return; } // We want to support both android.app.Activity and the pre-Honeycomb FragmentActivity // (for apps that use it for legacy reasons). This unfortunately leads to some code duplication. if (activity instanceof android.support.v4.app.FragmentActivity) { android.support.v4.app.FragmentManager fragmentManager = ((android.support.v4.app.FragmentActivity) activity).getSupportFragmentManager(); android.support.v4.app.DialogFragment oldFragment = (android.support.v4.app.DialogFragment) fragmentManager.findFragmentByTag(FRAGMENT_TAG); if (oldFragment != null) { oldFragment.dismiss(); } SupportTimePickerDialogFragment fragment = new SupportTimePickerDialogFragment(); if (options != null) { Bundle args = createFragmentArguments(options); fragment.setArguments(args); } TimePickerDialogListener listener = new TimePickerDialogListener(promise); fragment.setOnDismissListener(listener); fragment.setOnTimeSetListener(listener); fragment.show(fragmentManager, FRAGMENT_TAG); } else { FragmentManager fragmentManager = activity.getFragmentManager(); DialogFragment oldFragment = (DialogFragment) fragmentManager.findFragmentByTag(FRAGMENT_TAG); if (oldFragment != null) { oldFragment.dismiss(); } TimePickerDialogFragment fragment = new TimePickerDialogFragment(); if (options != null) { final Bundle args = createFragmentArguments(options); fragment.setArguments(args); } TimePickerDialogListener listener = new TimePickerDialogListener(promise); fragment.setOnDismissListener(listener); fragment.setOnTimeSetListener(listener); fragment.show(fragmentManager, FRAGMENT_TAG); } } private Bundle createFragmentArguments(ReadableMap options) { final Bundle args = new Bundle(); if (options.hasKey(ARG_HOUR) && !options.isNull(ARG_HOUR)) { args.putInt(ARG_HOUR, options.getInt(ARG_HOUR)); } if (options.hasKey(ARG_MINUTE) && !options.isNull(ARG_MINUTE)) { args.putInt(ARG_MINUTE, options.getInt(ARG_MINUTE)); } if (options.hasKey(ARG_IS24HOUR) && !options.isNull(ARG_IS24HOUR)) { args.putBoolean(ARG_IS24HOUR, options.getBoolean(ARG_IS24HOUR)); } return args; } }