-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: secure entry react component for android
- Loading branch information
Showing
10 changed files
with
247 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
android/src/main/java/com/ticketmasterignite/tickets/SecureEntryFragment.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package com.ticketmasterignite.tickets | ||
|
||
import android.os.Bundle | ||
import android.view.LayoutInflater | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import androidx.fragment.app.Fragment | ||
import com.ticketmaster.presence.SecureEntryView | ||
import com.ticketmasterignite.R | ||
|
||
class SecureEntryFragment : Fragment() { | ||
private var secureEntryView: SecureEntryView? = null | ||
override fun onCreateView( | ||
inflater: LayoutInflater, container: ViewGroup?, | ||
savedInstanceState: Bundle? | ||
): View? { | ||
val view = inflater.inflate(R.layout.fragment_secure_entry_view, container, false) | ||
secureEntryView = view.findViewById(R.id.secure_entry_view) | ||
return view | ||
} | ||
|
||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
super.onViewCreated(view, savedInstanceState) | ||
val token = arguments?.getString("token") ?: "" | ||
secureEntryView?.setToken(token) | ||
} | ||
} |
126 changes: 126 additions & 0 deletions
126
android/src/main/java/com/ticketmasterignite/tickets/SecureEntryViewManager.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package com.ticketmasterignite.tickets | ||
|
||
import android.os.Bundle | ||
import android.view.Choreographer | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import android.widget.FrameLayout | ||
import android.widget.TextView | ||
import androidx.fragment.app.Fragment | ||
import androidx.fragment.app.FragmentActivity | ||
import androidx.lifecycle.DefaultLifecycleObserver | ||
import androidx.lifecycle.Lifecycle | ||
import androidx.lifecycle.LifecycleObserver | ||
import androidx.lifecycle.LifecycleOwner | ||
import androidx.lifecycle.OnLifecycleEvent | ||
import com.facebook.react.bridge.ReactApplicationContext | ||
import com.facebook.react.bridge.ReadableArray | ||
import com.facebook.react.bridge.ReadableMap | ||
import com.facebook.react.uimanager.ThemedReactContext | ||
import com.facebook.react.uimanager.ViewGroupManager | ||
import com.facebook.react.uimanager.annotations.ReactProp | ||
import com.facebook.react.uimanager.annotations.ReactPropGroup | ||
|
||
class SecureEntryViewFragmentManager ( | ||
private val reactContext: ReactApplicationContext | ||
) : ViewGroupManager<FrameLayout>() { | ||
private var propWidth: Int? = null | ||
private var propHeight: Int? = null | ||
private var secureEntryToken: String? = null | ||
private var customFragment: SecureEntryFragment? = null | ||
|
||
override fun getName() = REACT_CLASS | ||
|
||
/** | ||
* Return a FrameLayout which will later hold the Fragment | ||
*/ | ||
override fun createViewInstance(reactContext: ThemedReactContext) = | ||
FrameLayout(reactContext) | ||
|
||
/** | ||
* Map the "create" command to an integer | ||
*/ | ||
override fun getCommandsMap() = mapOf("create" to COMMAND_CREATE) | ||
|
||
/** | ||
* Handle "create" command (called from JS) and call createFragment method | ||
*/ | ||
override fun receiveCommand( | ||
root: FrameLayout, | ||
commandId: String, | ||
args: ReadableArray? | ||
) { | ||
super.receiveCommand(root, commandId, args) | ||
val reactNativeViewId = requireNotNull(args).getInt(0) | ||
|
||
when (commandId.toInt()) { | ||
COMMAND_CREATE -> createFragment(root, reactNativeViewId) | ||
} | ||
} | ||
|
||
@ReactPropGroup(names = ["width", "height"], customType = "Style") | ||
fun setStyle(view: FrameLayout, index: Int, value: Int) { | ||
if (index == 0) propWidth = value | ||
if (index == 1) propHeight = value | ||
} | ||
|
||
@ReactProp(name = "token") | ||
fun setToken(view: View, token: ReadableMap) { | ||
token.getString("token")?.let { secureEntryToken = it } | ||
} | ||
|
||
|
||
/** | ||
* Replace your React Native view with a custom fragment | ||
*/ | ||
private fun createFragment(root: FrameLayout, reactNativeViewId: Int) { | ||
val parentView = root.findViewById<ViewGroup>(reactNativeViewId) | ||
setupLayout(parentView) | ||
|
||
customFragment = SecureEntryFragment() | ||
if (!secureEntryToken.isNullOrEmpty()) { | ||
val args = Bundle().apply { | ||
putString("token", secureEntryToken) | ||
} | ||
customFragment?.apply { | ||
arguments = args | ||
} | ||
} | ||
|
||
val activity = reactContext.currentActivity as FragmentActivity | ||
activity.supportFragmentManager | ||
.beginTransaction() | ||
.replace(reactNativeViewId, customFragment!!, reactNativeViewId.toString()) | ||
.commit() | ||
} | ||
|
||
fun setupLayout(view: View) { | ||
Choreographer.getInstance().postFrameCallback(object: Choreographer.FrameCallback { | ||
override fun doFrame(frameTimeNanos: Long) { | ||
manuallyLayoutChildren(view) | ||
view.viewTreeObserver.dispatchOnGlobalLayout() | ||
Choreographer.getInstance().postFrameCallback(this) | ||
} | ||
}) | ||
} | ||
|
||
/** | ||
* Layout all children properly | ||
*/ | ||
private fun manuallyLayoutChildren(view: View) { | ||
// propWidth and propHeight coming from react-native props | ||
val width = requireNotNull(propWidth) | ||
val height = requireNotNull(propHeight) | ||
|
||
view.measure( | ||
View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), | ||
View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)) | ||
|
||
view.layout(0, 80, width, height) | ||
} | ||
|
||
companion object { | ||
private const val REACT_CLASS = "SecureEntryViewManager" | ||
private const val COMMAND_CREATE = 1 | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
android/src/main/res/layout/fragment_secure_entry_view.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<!-- res/layout/fragment_my_custom_view.xml --> | ||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
android:layout_width="match_parent" | ||
android:layout_height="match_parent"> | ||
|
||
<com.ticketmaster.presence.SecureEntryView | ||
android:id="@+id/secure_entry_view" | ||
android:layout_width="match_parent" | ||
android:layout_height="match_parent" /> | ||
</FrameLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import React from 'react'; | ||
import { SecureEntryAndroid } from 'react-native-ticketmaster-ignite'; | ||
|
||
const SecureEntryView = () => <SecureEntryAndroid token="token_here" />; | ||
|
||
export default SecureEntryView; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { requireNativeComponent } from 'react-native'; | ||
|
||
export const SecureEntry = requireNativeComponent('SecureEntryViewManager'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { requireNativeComponent, useWindowDimensions } from 'react-native'; | ||
import React, { useEffect, useRef } from 'react'; | ||
import { PixelRatio, UIManager, findNodeHandle } from 'react-native'; | ||
|
||
interface SecureEntryProps { | ||
token: string; | ||
} | ||
|
||
const createFragment = (viewId: number | null) => | ||
UIManager.dispatchViewManagerCommand( | ||
viewId, | ||
// we are calling the 'create' command | ||
(UIManager as any).SecureEntryViewManager.Commands.create.toString(), | ||
[viewId] | ||
); | ||
|
||
const SecureEntryViewManager = requireNativeComponent<SecureEntryProps>( | ||
'SecureEntryViewManager' | ||
); | ||
|
||
export const SecureEntryAndroid: React.FC<SecureEntryProps> = (props) => { | ||
const ref = useRef(null); | ||
const height = useWindowDimensions().height; | ||
const width = useWindowDimensions().width; | ||
const token = props.token.toString(); | ||
|
||
useEffect(() => { | ||
const viewId = findNodeHandle(ref.current); | ||
createFragment(viewId); | ||
}, []); | ||
|
||
return ( | ||
<SecureEntryViewManager | ||
token={{ token }} | ||
style={{ | ||
// converts dpi to px, provide desired height | ||
height: PixelRatio.getPixelSizeForLayoutSize(height), | ||
// converts dpi to px, provide desired width | ||
width: PixelRatio.getPixelSizeForLayoutSize(width), | ||
}} | ||
ref={ref} | ||
/> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters