diff --git a/.expo/packager-info.json b/.expo/packager-info.json
index 36cff4cb..ac02bb60 100644
--- a/.expo/packager-info.json
+++ b/.expo/packager-info.json
@@ -1,9 +1,10 @@
{
- "expoServerPort": null,
- "packagerPort": null,
+ "expoServerPort": 8081,
+ "packagerPort": 8081,
"packagerPid": null,
"expoServerNgrokUrl": null,
"packagerNgrokUrl": null,
"ngrokPid": null,
- "webpackServerPort": null
+ "webpackServerPort": null,
+ "devToolsPort": 19002
}
diff --git a/.expo/settings.json b/.expo/settings.json
index 37f384fc..4377edd6 100644
--- a/.expo/settings.json
+++ b/.expo/settings.json
@@ -1,10 +1,10 @@
{
- "scheme": "com.livelike.demo",
"hostType": "lan",
"lanType": "ip",
"devClient": true,
"dev": true,
"minify": false,
"urlRandomness": null,
- "https": false
+ "https": false,
+ "scheme": "com.livelike.demo"
}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..df1aca84
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,106 @@
+# Built application files
+*.apk
+*.ap_
+*.aab
+
+# Files for the ART/Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+out/
+.idea/
+# Uncomment the following line in case you need and you don't have the release build type files in your app
+# release/
+
+# Gradle files
+.gradle/
+build/
+
+# expo files
+.expo/packager-info.json
+.expo/settings.json
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+*.log
+
+# Android Studio Navigation editor temp files
+.navigation/
+
+# Android Studio captures folder
+captures/
+
+# IntelliJ
+*.iml
+.idea/workspace.xml
+.idea/tasks.xml
+.idea/gradle.xml
+.idea/assetWizardSettings.xml
+.idea/dictionaries
+.idea/libraries
+# Android Studio 3 in .gitignore file.
+.idea/caches
+.idea/modules.xml
+# Comment next line if keeping position of elements in Navigation Editor is relevant for you
+.idea/navEditor.xml
+.idea/
+android/.idea/
+
+# Keystore files
+# Uncomment the following lines if you do not want to check your keystore files in.
+#*.jks
+#*.keystore
+
+# External native build folder generated in Android Studio 2.2 and later
+.externalNativeBuild
+.cxx/
+
+# Google Services (e.g. APIs or Firebase)
+# google-services.json
+
+# Freeline
+freeline.py
+freeline/
+freeline_project_description.json
+
+# fastlane
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots
+fastlane/test_output
+fastlane/readme.md
+
+# Version control
+vcs.xml
+
+# lint
+lint/intermediates/
+lint/generated/
+lint/outputs/
+lint/tmp/
+# lint/reports/
+
+# Android Profiling
+*.hprof
+
+#node_modules of commitizen library
+node_modules/
+package.json
+yarn.lock
+android/.idea/gradle.xml
+node_modules
+ios/Pods/
+ios/Pods/boost-for-react-native/README.md
+ios/Podfile.lock
+android/.idea/gradle.xml
+ios/Podfile.lock
diff --git a/LiveLikeAndroidView.jsx b/LiveLikeAndroidView.jsx
index 1a846812..6572291c 100644
--- a/LiveLikeAndroidView.jsx
+++ b/LiveLikeAndroidView.jsx
@@ -1,33 +1,287 @@
-import React, { useEffect, useRef } from 'react';
-import { UIManager, findNodeHandle } from 'react-native';
-import { PixelRatio} from "react-native";
-import { LiveLikeAndroidViewManager } from './android-view-manager';
-
-const createFragment = (viewId) =>
- UIManager.dispatchViewManagerCommand(
- viewId,
- // we are calling the 'create' command
- UIManager.LiveLikeAndroidViewManager.Commands.create.toString(),
- [viewId]
- );
+import React, {useEffect, useRef, useState} from 'react';
+import {findNodeHandle, NativeModules, requireNativeComponent, UIManager} from 'react-native';
+
+export const LiveLikeChatWidgetView = requireNativeComponent('LiveLikeChatWidgetView');
+export const LiveLikeWidgetView = requireNativeComponent('LiveLikeWidgetView');
+
+const clientId = "mOBYul18quffrBDuq2IACKtVuLbUzXIPye5S3bq5"
+
+
+
+// ClientID = BJSFlQAxraN9F99EcVOzpva7G8ohtJdGKpRdx3Ml
+// let programId = "5337f725-f580-49b5-9697-822f69e6d16e"
+// let chatRoomId = "65735146-5f90-4b75-bbcc-e1b75eff6014"
+
+// const programId = "08c5c27e-952d-4392-bd2a-c042db036ac5"
+// const chatRoomId = "bda23d2a-da84-4fc1-bd39-7e9ddba73d71" // TODO: Pinned Message
+// const chatRoomId = "bda23d2a-da84-4fc1-bd39-7e9ddba73d71" // TODO: Video Pinned New
+
+
+// Messages + Pinned Video
+const programId = "71add52f-dd99-42ac-8e96-743aaad41c3b"
+const chatRoomId = "3b097ea8-2b74-42b7-a810-5853b9796e00"
+// // https://cf-blast.livelikecdn.com/producer/applications/BJSFlQAxraN9F99EcVOzpva7G8ohtJdGKpRdx3Ml/chat-rooms/1ad3b3ae-c25f-4f3b-8873-727b1bf7ebbb/pinned-messages
+
+
+// Sequential pinned messages
+// const programId = "5337f725-f580-49b5-9697-822f69e6d16e"
+// const chatRoomId = "65735146-5f90-4b75-bbcc-e1b75eff6014"
+// const data = "{programId: '5337f725-f580-49b5-9697-822f69e6d16e', chatRoomId: '65735146-5f90-4b75-bbcc-e1b75eff6014', userAvatarUrl: 'https://websdk.livelikecdn.com/demo/assets/images/redrobot.png'}"
+// https://cf-blast.livelikecdn.com/producer/applications/OPba08mrr8gLZ2UMQ3uWMBOLiGhfovgIeQAEfqgI/chat-rooms/65735146-5f90-4b75-bbcc-e1b75eff6014
+
+
+const {LiveLikeModule} = NativeModules
+
+
+const sendMessage = (viewId, message, timeOut) => {
+ setTimeout(() => {
+ console.log('DEBUG: MESSAGE SENT: ', message)
+ UIManager.dispatchViewManagerCommand(
+ viewId,
+ UIManager.LiveLikeChatWidgetView.Commands.sendMessage.toString(),
+ [viewId, message]
+ );
+ }, timeOut)
+}
+
+
+const updateNickName = (viewId, nickName) => {
+ UIManager.dispatchViewManagerCommand(
+ viewId,
+ UIManager.LiveLikeChatWidgetView.Commands.updateNickName.toString(),
+ [viewId, nickName]
+ );
+}
+
+const updateUserAvatar = (viewId, userAvatar) => {
+ UIManager.dispatchViewManagerCommand(
+ viewId,
+ UIManager.LiveLikeChatWidgetView.Commands.updateUserAvatar.toString(),
+ [viewId, userAvatar]
+ );
+}
+
+let redAvatar = "https://websdk.livelikecdn.com/demo/assets/images/redrobot.png"
+let yellowAvatar = "https://websdk.livelikecdn.com/demo/assets/images/yellowrobot.png"
+
+
+const TIME_DELAY = 13000
export const LiveLikeAndroidView = () => {
- const ref = useRef(null);
-
- useEffect(() => {
- const viewId = findNodeHandle(ref.current);
- createFragment(viewId);
- }, []);
-
- return (
-
- );
-};
\ No newline at end of file
+
+ const [show, setShow] = useState(true)
+ const [showAskWidget, setShowAskWidget] = useState(false)
+ const ref = useRef(null);
+
+ useEffect(() => {
+ LiveLikeModule.initializeSDK(clientId)
+ }, [])
+
+
+ useEffect(() => {
+ console.log('SHOW CHAT: ', show)
+ }, [show])
+
+ useEffect(() => {
+ console.log('SHOW ASK: ', showAskWidget)
+ }, [showAskWidget])
+
+
+ // useEffect(() => {
+ // let viewId = findNodeHandle(ref.current)
+ // setTimeout(() => {
+ // updateUserAvatar(viewId, yellowAvatar)
+ // }, 5000)
+ // }, [])
+
+ // useEffect(() => {
+ // setTimeout(() => {
+ // setShowAskWidget(true)
+ // }, 5000)
+ // }, [])
+ //
+ //
+ // useEffect(() => {
+ // setTimeout(() => {
+ // setShow(!show)
+ // }, TIME_DELAY)
+ // }, [show])
+ //
+ // useEffect(() => {
+ // let viewId = findNodeHandle(ref.current)
+ // if (show && viewId) {
+ // let message1 = 'Send ' + Math.floor(Math.random() * 100)
+ // let message2 = 'Send ' + Math.floor(Math.random() * 100)
+ // sendMessage(viewId, message1, 5000)
+ // sendMessage(viewId, message2, 8000)
+ // }
+ // }, [show])
+
+
+ // useEffect(() => {
+ // setTimeout(() => {
+ // programId = "08c5c27e-952d-4392-bd2a-c042db036ac5"
+ // chatRoomId = "bda23d2a-da84-4fc1-bd39-7e9ddba73d71"
+ // }, TIME_DELAY * 1.5)
+ // }, [])
+
+ return (
+ {
+ LayoutAnimation.configureNext(LayoutAnimation.Presets.linear)
+ this.setState({widgetHeight: event.nativeEvent.height})
+ }}
+ onWidgetHidden={(event) => {
+ LayoutAnimation.configureNext(LayoutAnimation.Presets.linear)
+ this.setState({widgetHeight: 0})
+ }}
+ onEvent={event => {
+ }}
+ onChatMessageSent={(event) => {
+ console.log('DEBUG: ON CHAT MESSAGE SUCCESS', event.nativeEvent.message)
+ }}
+ onVideoPlayed={(event) => {
+ console.log('DEBUG: ON Video message Clicked', event.nativeEvent)
+ }}
+ onAskInfluencer={(event) => {
+ console.log('DEBUG: ON ASK INFLUENCER', event.nativeEvent)
+ }}
+ onRemoveAllPinMessages={(event) => {
+ console.log('DEBUG: ON REMOVE ALL PIN MESSAGES', event.nativeEvent)
+ }}
+ />
+ )
+};
+
+
+/*
+
+ {
+ LayoutAnimation.configureNext(LayoutAnimation.Presets.linear)
+ this.setState({widgetHeight: event.nativeEvent.height})
+ }}
+ onWidgetHidden={(event) => {
+ LayoutAnimation.configureNext(LayoutAnimation.Presets.linear)
+ this.setState({widgetHeight: 0})
+ }}
+ onEvent={event => {
+ }}
+ onChatMessageSent={(event) => {
+ console.log('DEBUG: ON CHAT MESSAGE SUCCESS', event.nativeEvent.message)
+ }}
+ onVideoPlayed={(event) => {
+ console.log('DEBUG: ON Video message Clicked', event.nativeEvent.videoUrl)
+ }}
+ onAskInfluencer={(event) => {
+ console.log('DEBUG: ON ASK INFLUENCER', event.nativeEvent)
+ }}
+ />
+
+ */
+
+/*
+
+
+ {
+ setShow(!show)
+ }}
+ />
+
+
+ {
+ LayoutAnimation.configureNext(LayoutAnimation.Presets.linear)
+ this.setState({widgetHeight: event.nativeEvent.height})
+ }}
+ onWidgetHidden={(event) => {
+ LayoutAnimation.configureNext(LayoutAnimation.Presets.linear)
+ this.setState({widgetHeight: 0})
+ }}
+ onEvent={event => {
+ }}
+ onChatMessageSent={(event) => {
+ console.log('DEBUG: ON CHAT MESSAGE SUCCESS', event.nativeEvent.message)
+ }}
+ onVideoPlayed={(event) => {
+ console.log('DEBUG: ON Video message Clicked', event.nativeEvent.videoUrl)
+ }}
+ onAskInfluencer={(event) => {
+ console.log('DEBUG: ON ASK INFLUENCER', event.nativeEvent)
+ }}
+ />
+
+
+
+
+ {
+ console.log('DEBUG1:', 'widget shown')
+ }}
+ onWidgetHidden={(event) => {
+ console.log('DEBUG2:', 'widget hidden')
+ }}
+ />
+
+
+
+ useEffect(() => {
+ setTimeout(() => {
+ LiveLikeModule.subscribeUserStream("nickName").then(nickName => {
+ console.log('NICKNAME', nickName)
+ })
+ }, 2000)
+ }, [])
+
+
+ useEffect(() => {
+ const newMessage = 'Hey, New Message' + Math.floor(Math.random() * 100)
+ const viewId = findNodeHandle(ref.current);
+ setTimeout(() => {
+ sendMessage(viewId, newMessage)
+ }, 5000)
+ }, [])
+
+
+ */
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..ebcfd362
--- /dev/null
+++ b/README.md
@@ -0,0 +1,32 @@
+
+## Run Application
+
+Clone the project
+
+```bash
+ git clone https://github.com/LiveLike/ReactNativeWrapperExample
+```
+
+Go to the project directory
+
+```bash
+ cd ReactNativeWrapperExample
+```
+
+Install dependencies
+
+```bash
+ npm install
+```
+
+Run on iOS
+
+```bash
+ npm run iOS
+```
+
+Run on Android
+
+```bash
+ npm run Android
+```
diff --git a/android-view-manager.jsx b/android-view-manager.jsx
deleted file mode 100644
index 32a270b5..00000000
--- a/android-view-manager.jsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import { requireNativeComponent } from 'react-native';
-
-export const LiveLikeAndroidViewManager = requireNativeComponent(
- 'LiveLikeAndroidViewManager'
-);
\ No newline at end of file
diff --git a/android/.expo/settings.json b/android/.expo/settings.json
new file mode 100644
index 00000000..92bc513b
--- /dev/null
+++ b/android/.expo/settings.json
@@ -0,0 +1,8 @@
+{
+ "hostType": "lan",
+ "lanType": "ip",
+ "dev": true,
+ "minify": false,
+ "urlRandomness": null,
+ "https": false
+}
diff --git a/android/.idea/gradle.xml b/android/.idea/gradle.xml
index f9c779e9..09d2578b 100644
--- a/android/.idea/gradle.xml
+++ b/android/.idea/gradle.xml
@@ -33,6 +33,5 @@
-
\ No newline at end of file
diff --git a/android/.idea/jarRepositories.xml b/android/.idea/jarRepositories.xml
index 6f2acf95..15cc4525 100644
--- a/android/.idea/jarRepositories.xml
+++ b/android/.idea/jarRepositories.xml
@@ -46,5 +46,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/.idea/misc.xml b/android/.idea/misc.xml
index 6b15099a..b0220e0c 100644
--- a/android/.idea/misc.xml
+++ b/android/.idea/misc.xml
@@ -7,6 +7,7 @@
+
diff --git a/android/.idea/modules.xml b/android/.idea/modules.xml
deleted file mode 100644
index 9dddca50..00000000
--- a/android/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/android/app/build.gradle b/android/app/build.gradle
index d68106cf..92d3a2a2 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -79,10 +79,10 @@ import com.android.build.OutputFile
*/
project.ext.react = [
- enableHermes: (findProperty('expo.jsEngine') ?: "jsc") == "hermes",
- cliPath: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/cli.js",
- hermesCommand: new File(["node", "--print", "require.resolve('hermes-engine/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/%OS-BIN%/hermesc",
- composeSourceMapsPath: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/scripts/compose-source-maps.js",
+ enableHermes: false,
+ // cliPath: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/cli.js",
+ // hermesCommand: new File(["node", "--print", "require.resolve('hermes-engine/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/%OS-BIN%/hermesc",
+ // composeSourceMapsPath: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/scripts/compose-source-maps.js",
]
apply from: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../react.gradle")
@@ -176,6 +176,7 @@ android {
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
+ namespace 'com.livelike.demo'
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
@@ -196,7 +197,8 @@ android {
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
//noinspection GradleDynamicVersion
- implementation "com.facebook.react:react-native:+" // From node_modules
+ implementation "com.facebook.react:react-native:+"
+ implementation 'androidx.media2:media2-widget:1.1.2'// From node_modules
def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true";
def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true";
@@ -222,7 +224,7 @@ dependencies {
implementation 'com.facebook.fresco:animated-webp:2.0.0'
}
}
-
+
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
@@ -248,10 +250,10 @@ dependencies {
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
- implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
+ implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
- implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.activity:activity-ktx:1.2.2"
implementation "androidx.fragment:fragment-ktx:1.3.3"
@@ -262,7 +264,7 @@ dependencies {
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.2.0'
- implementation 'com.livelike:android-engagement-sdk:2.29'
+ implementation 'com.livelike:android-engagement-sdk:2.43.5'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
@@ -277,7 +279,7 @@ dependencies {
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
- from configurations.compile
+ from configurations.implementation
into 'libs'
}
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
index 99e38fc5..35a24c4a 100644
--- a/android/app/src/debug/AndroidManifest.xml
+++ b/android/app/src/debug/AndroidManifest.xml
@@ -1,6 +1,5 @@
-
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 3dfcd809..f1d2e4c8 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -1,4 +1,5 @@
-
+
@@ -17,7 +18,8 @@
-
+
diff --git a/android/app/src/main/java/com/livelike/demo/LiveLikeAndroidViewManager.kt b/android/app/src/main/java/com/livelike/demo/LiveLikeAndroidViewManager.kt
deleted file mode 100644
index 5ca8afeb..00000000
--- a/android/app/src/main/java/com/livelike/demo/LiveLikeAndroidViewManager.kt
+++ /dev/null
@@ -1,112 +0,0 @@
-package com.livelike.demo
-import android.view.Choreographer
-import android.view.View
-import android.view.ViewGroup
-import android.widget.FrameLayout
-import androidx.fragment.app.FragmentActivity
-import com.facebook.react.bridge.ReactApplicationContext
-import com.facebook.react.bridge.ReadableArray
-import com.facebook.react.common.MapBuilder
-import com.facebook.react.uimanager.ThemedReactContext
-import com.facebook.react.uimanager.ViewGroupManager
-import com.facebook.react.uimanager.annotations.ReactPropGroup
-import com.livelike.demo.ui.main.ChatFragment
-import com.livelike.demo.ui.main.WidgetTimeLineFragment
-
-
-// replace with your package
-
-
-class LiveLikeAndroidViewManager(var reactContext: ReactApplicationContext) :
- ViewGroupManager() {
- val COMMAND_CREATE = 1
- private var propWidth = 0
- private var propHeight = 0
- override fun getName(): String {
- return REACT_CLASS
- }
-
- /**
- * Return a FrameLayout which will later hold the Fragment
- */
- public override fun createViewInstance(reactContext: ThemedReactContext): FrameLayout {
- return FrameLayout(reactContext)
- }
-
- /**
- * Map the "create" command to an integer
- */
- override fun getCommandsMap(): Map? {
- return MapBuilder.of("create", 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 = args!!.getInt(0)
- val commandIdInt = commandId.toInt()
- when (commandIdInt) {
- COMMAND_CREATE -> createFragment(root, reactNativeViewId)
- else -> {}
- }
- }
-
- @ReactPropGroup(names = ["width", "height"], customType = "Style")
- fun setStyle(view: FrameLayout?, index: Int, value: Int) {
- if (index == 0) {
- propWidth = value
- }
- if (index == 1) {
- propHeight = value
- }
- }
-
- /**
- * Replace your React Native view with a custom fragment
- */
- fun createFragment(root: FrameLayout, reactNativeViewId: Int) {
- val parentView = root.findViewById(reactNativeViewId) as ViewGroup
- setupLayout(parentView)
- val myFragment = WidgetTimeLineFragment()
- //val myFragment = ChatFragment()
- val activity = reactContext.currentActivity as FragmentActivity?
- activity!!.supportFragmentManager
- .beginTransaction()
- .replace(reactNativeViewId, myFragment, 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
- */
- fun manuallyLayoutChildren(view: View) {
- // propWidth and propHeight coming from react-native props
- val width = propWidth
- val height = propHeight
- view.measure(
- View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)
- )
- view.layout(0, 0, width, height)
- }
-
- companion object {
- const val REACT_CLASS = "LiveLikeAndroidViewManager"
- }
-}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/livelike/demo/LiveLikeManager.kt b/android/app/src/main/java/com/livelike/demo/LiveLikeManager.kt
new file mode 100644
index 00000000..f5c1efd1
--- /dev/null
+++ b/android/app/src/main/java/com/livelike/demo/LiveLikeManager.kt
@@ -0,0 +1,118 @@
+package com.livelike.demo
+
+import android.content.Context
+import android.util.Log
+import com.facebook.react.bridge.Promise
+import com.facebook.react.bridge.ReactMethod
+import com.livelike.demo.ui.main.PageViewModel.Companion.PREF_USER_ACCESS_TOKEN
+import com.livelike.engagementsdk.EngagementSDK
+import com.livelike.engagementsdk.LiveLikeContentSession
+import com.livelike.engagementsdk.chat.LiveLikeChatSession
+import com.livelike.engagementsdk.core.AccessTokenDelegate
+import com.livelike.engagementsdk.core.utils.LogLevel
+import com.livelike.engagementsdk.core.utils.minimumLogLevel
+import com.livelike.engagementsdk.publicapis.ErrorDelegate
+
+object LiveLikeManager {
+
+
+ lateinit var engagementSDK: EngagementSDK;
+ var userAccessToken: String? = null;
+ var contentSession: LiveLikeContentSession? = null
+ const val PREF_USER_ACCESS_TOKEN = "user_access_token"
+
+
+ @ReactMethod
+ fun subscribeUserStream(key: String, promise: Promise) {
+ engagementSDK.userStream.subscribe(key) {
+ promise.resolve(it?.nickname)
+ unSubscribeUserStream()
+ }
+ }
+
+ @ReactMethod
+ fun unSubscribeUserStream() {
+ engagementSDK.userStream.subscribe("invalid-key") {}
+ }
+
+
+ @Synchronized private fun createContentSession(programId: String): LiveLikeContentSession {
+ this.contentSession = engagementSDK.createContentSession(programId, null, false)
+ return this.contentSession!!
+
+ }
+
+ @Synchronized fun getChatSession(programId: String): LiveLikeChatSession? {
+ if(isValidContentSession(programId)) {
+ return this.contentSession?.chatSession
+ }
+
+ destroyContentSession()
+ return createContentSession(programId)?.chatSession
+
+ }
+
+ @Synchronized fun getContentSession(programId: String): LiveLikeContentSession? {
+
+ if(isValidContentSession(programId)) {
+ return this.contentSession
+ }
+
+ destroyContentSession()
+ return createContentSession(programId)
+
+ }
+
+ @ReactMethod
+ @Synchronized fun destroyContentSession() {
+ this.contentSession?.chatSession?.close()
+ this.contentSession?.close()
+ this.contentSession = null
+ }
+
+ @Synchronized private fun isValidContentSession(programId: String): Boolean {
+ return this.contentSession?.contentSessionId() == programId
+ }
+
+
+ @ReactMethod
+ fun initializeSDK(applicationContext: Context, clientId: String, promise: Promise) {
+
+ minimumLogLevel = LogLevel.Verbose
+
+ engagementSDK = clientId?.let {
+ EngagementSDK(
+ it,
+ applicationContext,
+ object : ErrorDelegate() {
+ override fun onError(error: String) {
+ promise.reject(Throwable(error))
+ }
+ }, accessTokenDelegate = object : AccessTokenDelegate {
+ override fun getAccessToken(): String? {
+ return applicationContext.getSharedPreferences(
+ applicationContext.packageName,
+ Context.MODE_PRIVATE
+ ).getString(
+ PREF_USER_ACCESS_TOKEN,
+ null
+ ).apply {}
+ }
+
+ override fun storeAccessToken(accessToken: String?) {
+
+ promise.resolve(accessToken)
+ userAccessToken = accessToken
+
+ applicationContext.getSharedPreferences(
+ applicationContext.packageName,
+ Context.MODE_PRIVATE
+ ).edit().putString(
+ PREF_USER_ACCESS_TOKEN,
+ accessToken
+ ).apply()
+ }
+ })
+ }!!
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/livelike/demo/MainActivity.kt b/android/app/src/main/java/com/livelike/demo/MainActivity.kt
index 3a1175c4..bfe8ae7c 100644
--- a/android/app/src/main/java/com/livelike/demo/MainActivity.kt
+++ b/android/app/src/main/java/com/livelike/demo/MainActivity.kt
@@ -19,7 +19,7 @@ class MainActivity : ReactActivity() {
// coloring the background, status bar, and navigation bar.
// This is required for expo-splash-screen.
setTheme(R.style.AppTheme)
- pageViewModel.initEnagementSDK(applicationContext = applicationContext)
+ pageViewModel.initEngagementSDK(applicationContext = applicationContext)
super.onCreate(null)
}
diff --git a/android/app/src/main/java/com/livelike/demo/MainApplication.java b/android/app/src/main/java/com/livelike/demo/MainApplication.java
index 4eb17166..3c28947d 100644
--- a/android/app/src/main/java/com/livelike/demo/MainApplication.java
+++ b/android/app/src/main/java/com/livelike/demo/MainApplication.java
@@ -34,7 +34,7 @@ public boolean getUseDeveloperSupport() {
protected List getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List packages = new PackageList(this).getPackages();
- packages.add(new MyPackage());
+ packages.add(new RNLiveLikePackage(getApplication()));
return packages;
}
diff --git a/android/app/src/main/java/com/livelike/demo/MyFragment.kt b/android/app/src/main/java/com/livelike/demo/MyFragment.kt
deleted file mode 100644
index be45ac32..00000000
--- a/android/app/src/main/java/com/livelike/demo/MyFragment.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.livelike.demo
-
-// replace with your package
-// replace with your view's import
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.fragment.app.Fragment
-
-
-class MyFragment : Fragment() {
- var customView: CustomView? = null
- override fun onCreateView(
- inflater: LayoutInflater,
- parent: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- super.onCreateView(inflater, parent, savedInstanceState)
- customView = CustomView(this.requireContext())
- return customView // this CustomView could be any view that you want to render
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- // do any logic that should happen in an `onCreate` method, e.g:
- // customView.onCreate(savedInstanceState);
- }
-
- override fun onPause() {
- super.onPause()
- // do any logic that should happen in an `onPause` method
- // e.g.: customView.onPause();
- }
-
- override fun onResume() {
- super.onResume()
- // do any logic that should happen in an `onResume` method
- // e.g.: customView.onResume();
- }
-
- override fun onDestroy() {
- super.onDestroy()
- // do any logic that should happen in an `onDestroy` method
- // e.g.: customView.onDestroy();
- }
-}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/livelike/demo/MyPackage.kt b/android/app/src/main/java/com/livelike/demo/MyPackage.kt
index 66eb9165..c77cd520 100644
--- a/android/app/src/main/java/com/livelike/demo/MyPackage.kt
+++ b/android/app/src/main/java/com/livelike/demo/MyPackage.kt
@@ -16,7 +16,7 @@ class MyPackage : ReactPackage {
override fun createViewManagers(reactContext: ReactApplicationContext): List> {
return Arrays.asList>(
- LiveLikeAndroidViewManager(reactContext)
+ //LiveLikeAndroidViewManager(reactContext)
)
}
}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/livelike/demo/RNLiveLikeModule.kt b/android/app/src/main/java/com/livelike/demo/RNLiveLikeModule.kt
new file mode 100644
index 00000000..dca8a683
--- /dev/null
+++ b/android/app/src/main/java/com/livelike/demo/RNLiveLikeModule.kt
@@ -0,0 +1,66 @@
+package com.livelike.demo
+
+import android.app.Application
+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.livelike.engagementsdk.chat.ChatRoomInfo
+import com.livelike.engagementsdk.publicapis.LiveLikeCallback
+import com.livelike.engagementsdk.publicapis.LiveLikeUserApi
+
+class RNLiveLikeModule(
+ val application: Application,
+ private val applicationContext: ReactApplicationContext
+) : ReactContextBaseJavaModule(applicationContext) {
+
+ override fun getName(): String {
+ return "LiveLikeModule"
+ }
+
+ @ReactMethod
+ fun initializeSDK(clientId: String, promise: Promise) {
+ LiveLikeManager.initializeSDK(application, clientId, promise)
+ }
+
+ @ReactMethod
+ fun subscribeUserStream(key: String, promise: Promise) {
+ LiveLikeManager.subscribeUserStream(key, promise)
+ }
+
+ @ReactMethod
+ fun getChatRoomName(chatRoomId: String, promise: Promise) {
+ LiveLikeManager.engagementSDK.chat()
+ .getChatRoom(chatRoomId, object : LiveLikeCallback() {
+ override fun onResponse(chatRoomDetails: ChatRoomInfo?, error: String?) {
+ chatRoomDetails?.let {
+ return promise.resolve(chatRoomDetails?.title)
+ }
+ error?.let {
+ return promise.reject(error)
+ }
+ }
+ })
+ }
+
+ @ReactMethod
+ fun getCurrentUserProfileId(promise: Promise) {
+ LiveLikeManager.engagementSDK.getCurrentUserDetails(object :
+ com.livelike.engagementsdk.publicapis.LiveLikeCallback() {
+ override fun onResponse(result: LiveLikeUserApi?, error: String?) {
+ result?.let {
+ promise.resolve(result.userId)
+ }
+ error?.let {
+ promise.reject(Throwable(error))
+ }
+ }
+ })
+ }
+
+ @ReactMethod
+ fun destroyContentSession(promise: Promise) {
+ LiveLikeManager.destroyContentSession()
+ promise.resolve(true)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/livelike/demo/RNLiveLikePackage.kt b/android/app/src/main/java/com/livelike/demo/RNLiveLikePackage.kt
new file mode 100644
index 00000000..cf41ea3e
--- /dev/null
+++ b/android/app/src/main/java/com/livelike/demo/RNLiveLikePackage.kt
@@ -0,0 +1,22 @@
+package com.livelike.demo
+
+import android.app.Application
+import com.facebook.react.ReactPackage
+import com.facebook.react.bridge.NativeModule
+import com.facebook.react.bridge.ReactApplicationContext
+import com.facebook.react.uimanager.ViewManager
+import com.livelike.demo.widget.LiveLikeChatViewManager
+import com.livelike.demo.widget.LiveLikeWidgetViewManager
+import java.util.*
+
+// replace with your package
+
+class RNLiveLikePackage(val application: Application) : ReactPackage {
+ override fun createNativeModules(reactContext: ReactApplicationContext): MutableList {
+ return Arrays.asList(RNLiveLikeModule(application, reactContext))
+ }
+
+ override fun createViewManagers(reactContext: ReactApplicationContext): List> {
+ return Arrays.asList>(LiveLikeChatViewManager(reactContext), LiveLikeWidgetViewManager(reactContext))
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/livelike/demo/adapters/PinMessageAdapter.kt b/android/app/src/main/java/com/livelike/demo/adapters/PinMessageAdapter.kt
new file mode 100644
index 00000000..5aa0517d
--- /dev/null
+++ b/android/app/src/main/java/com/livelike/demo/adapters/PinMessageAdapter.kt
@@ -0,0 +1,229 @@
+package com.livelike.demo.adapters
+
+import android.content.Context
+import android.os.Handler
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.RelativeLayout
+import androidx.core.view.children
+import androidx.core.view.forEach
+import com.bumptech.glide.Glide
+import com.bumptech.glide.load.resource.bitmap.CenterCrop
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners
+import com.bumptech.glide.request.BaseRequestOptions
+import com.bumptech.glide.request.RequestOptions
+import com.facebook.react.uimanager.ThemedReactContext
+import com.livelike.demo.R
+import com.livelike.demo.databinding.PinHybridMessageBinding
+import com.livelike.engagementsdk.chat.data.remote.PinMessageInfo
+import com.livelike.engagementsdk.publicapis.LiveLikeChatMessage
+import org.json.JSONObject
+
+
+class PinMessageAdapter() {
+
+ private val messageList: ArrayList = arrayListOf()
+ var parentView: RelativeLayout? = null
+ lateinit var pinMessageHandler: PinMessageActionHandler
+
+ interface PinMessageActionHandler {
+ fun onVideoPlayed(videoUrl: String)
+ fun onRemoveAllPinMessages()
+ }
+
+
+ private fun bindHybridMessage(
+ bindingObject: PinHybridMessageBinding,
+ pinMessage: PinMessageInfo
+ ) {
+
+ val messagePayload = pinMessage.messagePayload
+ val isChatMessage: Boolean = isVideoMessage(messagePayload).not()
+ val pinView = bindingObject.root
+ bindingObject.container.clipToOutline = true
+
+ // Avatar + Video Thumbnail
+ bindingObject.imgChatAvatar.visibility = when (isChatMessage) {
+ true -> View.VISIBLE
+ else -> View.GONE
+ }
+ bindingObject.imgVideoThumbnailContainer.clipToOutline = true
+ bindingObject.imgVideoThumbnail.clipToOutline = true
+ bindingObject.imgVideoThumbnailContainer.visibility = when (isChatMessage) {
+ true -> View.GONE
+ else -> View.VISIBLE
+ }
+
+ // Text message
+ var message: String? = ""
+ try {
+ message = if (isChatMessage) messagePayload?.message else getCustomDataProp(
+ "title",
+ messagePayload,
+ )
+ } catch (e: java.lang.Exception) {
+ }
+ bindingObject.chatMessage.text = message
+
+
+ val appContext: Context = pinView.context.applicationContext
+ if (isChatMessage) {
+ updateImageResource(
+ appContext,
+ bindingObject.imgChatAvatar,
+ messagePayload?.userPic,
+ R.drawable.default_avatar,
+ getAvatarOptions()
+ )
+ } else {
+ updateImageResource(
+ appContext,
+ bindingObject.imgVideoThumbnail,
+ getCustomDataProp("videoThumbnail", messagePayload),
+ R.drawable.default_video_thumbnail,
+ RequestOptions()
+ )
+ }
+
+ pinMessage.id?.let {
+ registerListener(bindingObject, it)
+ }
+ }
+
+ private fun getAvatarOptions(): BaseRequestOptions {
+ val options = RequestOptions()
+ options.optionalCircleCrop()
+ options.transform(
+ CenterCrop(),
+ RoundedCorners(10)
+ )
+ return options
+
+ }
+
+ private fun registerListener(bindingObject: PinHybridMessageBinding, messageId: String) {
+
+ val messageDetails = getDetailedMessageBy(messageId)
+ val isVideoMessage = isVideoMessage(messageDetails.messagePayload)
+
+ bindingObject.closeBtnContainer.setOnClickListener {
+ removeAllPinMessages()
+ pinMessageHandler.onRemoveAllPinMessages()
+ }
+
+ bindingObject.container.setOnClickListener {
+ if (isVideoMessage) {
+ getCustomDataProp("url", messageDetails.messagePayload)?.let {
+ pinMessageHandler.onVideoPlayed(it)
+ }
+ }
+
+ Handler().postDelayed({
+ removePinMessage(messageId)
+ }, 1000)
+
+ }
+
+ }
+
+ private fun getCustomDataProp(key: String, messagePayload: LiveLikeChatMessage?): String? {
+
+ try {
+ messagePayload?.custom_data?.let {
+ val jsonObject = JSONObject(it)
+ return jsonObject.get(key).toString()
+ }
+ } catch (e: java.lang.Exception) {
+
+ }
+ return null
+ }
+
+ private fun updateImageResource(
+ appContext: Context,
+ targetNode: ImageView,
+ targetSource: String?,
+ defaultSource: Int,
+ options: BaseRequestOptions
+ ) {
+ targetSource?.let {
+ Glide.with(appContext)
+ .load(it)
+ .apply(options)
+ .placeholder(defaultSource)
+ .error(defaultSource)
+ .into(targetNode)
+ }
+ }
+
+
+ private fun isVideoMessage(messagePayload: LiveLikeChatMessage?): Boolean {
+
+ if (messagePayload?.custom_data.isNullOrEmpty()) {
+ return false
+ }
+
+ messagePayload?.custom_data?.let {
+ val jsonObject = JSONObject(it)
+ val messageType = jsonObject.get("messageType").toString()
+ return messageType.toLowerCase() == "video"
+ }
+
+ return false
+
+ }
+
+ fun setup(view: RelativeLayout) {
+ this.parentView = view
+ }
+
+ fun clear() {
+ this.messageList.clear()
+ this.parentView = null
+
+ }
+
+ fun addPinMessage(
+ pinMessage: PinMessageInfo,
+ parentContext: ThemedReactContext
+ ) {
+
+ val pinnedBindingObject = PinHybridMessageBinding.bind(
+ LayoutInflater.from(parentContext)
+ .inflate(R.layout.pin_hybrid_message, parentView, false)
+ )
+ val pinnedView = pinnedBindingObject.root
+ pinnedView.setTag(pinMessage.id)
+ messageList.add(pinMessage)
+ parentView?.addView(pinnedView)
+ bindHybridMessage(pinnedBindingObject, pinMessage)
+
+ }
+
+
+ fun removePinMessage(messageId: String?) {
+ val index = messageList.indexOfFirst { it.id == messageId }
+ if (index != -1) {
+ messageList.removeAt(index)
+ }
+ parentView?.children?.forEach {
+ val thisMessageId = it.tag as String
+ if(thisMessageId == messageId) {
+ parentView?.removeView(it)
+ }
+ }
+ }
+
+ private fun getDetailedMessageBy(messageId: String): PinMessageInfo {
+ val index = messageList.indexOfFirst { it.id == messageId }
+ return messageList[index]
+ }
+
+ private fun removeAllPinMessages() {
+ this.messageList.clear()
+ this.parentView?.removeAllViews()
+ }
+
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/livelike/demo/ui/CustomChatAdapter.kt b/android/app/src/main/java/com/livelike/demo/ui/CustomChatAdapter.kt
deleted file mode 100644
index c5450d4a..00000000
--- a/android/app/src/main/java/com/livelike/demo/ui/CustomChatAdapter.kt
+++ /dev/null
@@ -1,4 +0,0 @@
-package com.livelike.demo.ui
-
-class CustomChatAdapter(
-)
diff --git a/android/app/src/main/java/com/livelike/demo/ui/main/Application.kt b/android/app/src/main/java/com/livelike/demo/ui/main/Application.kt
deleted file mode 100644
index f2b25628..00000000
--- a/android/app/src/main/java/com/livelike/demo/ui/main/Application.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.livelike.demo.ui.main
-
-import android.app.Application
-import android.content.Context
-
-class Application : Application() {
-
-
-
-}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/livelike/demo/ui/main/ChatFragment.kt b/android/app/src/main/java/com/livelike/demo/ui/main/ChatFragment.kt
deleted file mode 100644
index 320eab15..00000000
--- a/android/app/src/main/java/com/livelike/demo/ui/main/ChatFragment.kt
+++ /dev/null
@@ -1,101 +0,0 @@
-package com.livelike.demo.ui.main
-
-import android.os.Bundle
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.lifecycle.ViewModelProvider
-import com.livelike.demo.R
-import com.livelike.engagementsdk.EngagementSDK
-import com.livelike.engagementsdk.EpochTime
-import com.livelike.engagementsdk.chat.ChatView
-import com.livelike.engagementsdk.publicapis.ErrorDelegate
-import com.livelike.engagementsdk.publicapis.LiveLikeCallback
-
-/**
- * A placeholder fragment containing a simple view.
- */
-class ChatFragment : BaseFragment() {
-
- private lateinit var pageViewModel: PageViewModel
- private var programId = ""
- private var isChatInputVisible = true
- var chat_view : ChatView? = null
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- pageViewModel = ViewModelProvider(requireActivity()).get(PageViewModel::class.java)
- setProgramId(arguments?.getString(ARG_SECTION_NUMBER) ?: "3ebd6f09-2f16-4171-b94a-c9335154d672")
- //setProgramId("3ebd6f09-2f16-4171-b94a-c9335154d672")
- isChatInputVisible = true//arguments?.getBoolean(ARG_CHAT_INPUT_VISIBILITY) ?: false
- }
-
- private fun setProgramId(programId: String) {
- this.programId = programId
-
- }
-
- override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- val root = inflater.inflate(R.layout.chat_fragment_layout, container, false)
- val chatView: ChatView = root.findViewById(R.id.chat_view)
- initChatSession(chatView)
- return root
- }
-
- private fun initChatSession(chat_view: ChatView) {
- pageViewModel.chatFrag= this
- val chatSession = pageViewModel.engagementSDK.createChatSession(object : EngagementSDK.TimecodeGetter {
- override fun getTimecode(): EpochTime {
- return EpochTime(0)
- }
-
- }, errorDelegate = object : ErrorDelegate() {
- override fun onError(error: String) {
- Log.e("TEST", error)
- }
- })
-
- if (chatSession != null) {
- chatSession.connectToChatRoom(this.programId, callback = object : LiveLikeCallback() {
- override fun onResponse(result: Unit?, error: String?) {
- if (error != null) {
- Log.e("TEST", error)
- }
- }
- })
- chat_view.allowMediaFromKeyboard = true
- chat_view.isChatInputVisible = true
- chat_view.setSession(chatSession)
- //chat_view.clearSession()
- this.chat_view = chat_view
- //chatSession.close()
- }
- }
-
-
- companion object {
- /**
- * The fragment argument representing the section number for this
- * fragment.
- */
- private const val ARG_SECTION_NUMBER = "section_number"
- private const val ARG_CHAT_INPUT_VISIBILITY = "chat_input_visibility"
-
- /**
- * Returns a new instance of this fragment for the given section
- * number.
- */
- @JvmStatic
- fun newInstance(sectionNumber: String, isChatInputVisible: Boolean): ChatFragment {
- return ChatFragment().apply {
- arguments = Bundle().apply {
- putString(ARG_SECTION_NUMBER, sectionNumber)
- putBoolean(ARG_CHAT_INPUT_VISIBILITY,isChatInputVisible)
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/livelike/demo/ui/main/CustomAskWidgetView.kt b/android/app/src/main/java/com/livelike/demo/ui/main/CustomAskWidgetView.kt
new file mode 100644
index 00000000..8c725f45
--- /dev/null
+++ b/android/app/src/main/java/com/livelike/demo/ui/main/CustomAskWidgetView.kt
@@ -0,0 +1,125 @@
+package com.livelike.demo.ui.main
+
+import android.content.Context
+import android.os.Handler
+import android.text.Editable
+import android.text.TextWatcher
+import android.util.AttributeSet
+import android.view.View
+import android.widget.LinearLayout
+import com.livelike.demo.R
+import com.livelike.demo.databinding.FcCustomAskAWidgetBinding
+import com.livelike.engagementsdk.widget.widgetModel.TextAskWidgetModel
+
+
+class CustomAskWidgetView : LinearLayout {
+
+ var askWidgetModel: TextAskWidgetModel? = null
+ private lateinit var binding: FcCustomAskAWidgetBinding
+ lateinit var userEventsListener: UserEventsListener
+ var influencerName: String? = null
+
+ interface UserEventsListener {
+ fun closeDialog()
+ fun onMessageSent(message: String)
+ }
+
+ constructor(context: Context) : super(context) {
+ init(null, 0)
+ }
+
+ constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+ init(attrs, 0)
+ }
+
+ constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(
+ context,
+ attrs,
+ defStyle
+ ) {
+ init(attrs, defStyle)
+ }
+
+ private fun init(attrs: AttributeSet?, defStyle: Int) {
+ binding = FcCustomAskAWidgetBinding.bind(
+ inflate(
+ context,
+ R.layout.fc_custom_ask_a_widget,
+ this
+ )
+ )
+ }
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ registerListeners()
+ askWidgetModel?.widgetData?.let { liveLikeWidget -> }
+ binding.closeIconBtn.visibility = View.VISIBLE
+ this.influencerName?.let {
+ binding.headerTitle.text = "Ask ${it}"
+ }
+ }
+
+ override fun onDetachedFromWindow() {
+ super.onDetachedFromWindow()
+ closeDialog()
+ }
+
+
+ private fun registerListeners(): Unit {
+
+
+ binding.influencerQuestionInput.addTextChangedListener(object : TextWatcher {
+
+ override fun afterTextChanged(arg0: Editable) {
+
+ // Updating Send Button
+ val isEnabled = binding.influencerQuestionInput.text?.length!! > 0
+
+ // Updating Send Button
+ binding.sendBtn.isEnabled = isEnabled
+
+ // Updating Word Count
+ val maxWordCount = resources.getInteger(R.integer.ask_influencer_input_word_limit)
+ binding.wordCount.text = "${binding.influencerQuestionInput.text?.length}/${maxWordCount}"
+ }
+
+ override fun beforeTextChanged(
+ s: CharSequence,
+ start: Int,
+ count: Int,
+ after: Int
+ ) {
+ }
+
+ override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
+ })
+
+
+ // On Send Button
+ binding.sendBtn.setOnClickListener {
+ if (binding.influencerQuestionInput.text.toString().trim().isNotEmpty()) {
+ askWidgetModel?.submitReply(binding.influencerQuestionInput.text.toString().trim())
+ binding.askInfluencerContentWrapper.visibility = View.GONE
+ binding.askInfluencerConfirmationWrapper.visibility = View.VISIBLE
+ binding.closeIconBtn.visibility = View.GONE
+ val influencerName = if(this.influencerName.isNullOrEmpty()) "our Expert" else this.influencerName
+ binding.confirmationMessage.text = "We've shared your message with ${influencerName}"
+ userEventsListener.onMessageSent(binding.influencerQuestionInput.text.toString())
+ Handler().postDelayed({
+ closeDialog()
+ }, 2500)
+ }
+ }
+
+
+ binding.closeIconBtn.setOnClickListener {
+ closeDialog()
+ }
+ }
+
+ private fun closeDialog() {
+ binding.influencerQuestionInput.clearFocus()
+ userEventsListener.closeDialog()
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/livelike/demo/ui/main/CustomChatView.kt b/android/app/src/main/java/com/livelike/demo/ui/main/CustomChatView.kt
deleted file mode 100644
index 3e2ccd76..00000000
--- a/android/app/src/main/java/com/livelike/demo/ui/main/CustomChatView.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.livelike.demo.ui.main
-
-import android.content.Context
-import android.util.AttributeSet
-import com.livelike.engagementsdk.chat.ChatView
-
-class CustomChatView(context: Context, attrs: AttributeSet?) : ChatView(context, attrs) {
-
-}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/livelike/demo/ui/main/FCVideoView.kt b/android/app/src/main/java/com/livelike/demo/ui/main/FCVideoView.kt
new file mode 100644
index 00000000..d30f319c
--- /dev/null
+++ b/android/app/src/main/java/com/livelike/demo/ui/main/FCVideoView.kt
@@ -0,0 +1,336 @@
+package com.livelike.demo.ui.main
+
+import android.content.Context
+import android.graphics.Outline
+import android.media.MediaPlayer
+import android.net.Uri
+import android.os.Build
+import android.util.AttributeSet
+import android.view.View
+import android.view.ViewOutlineProvider
+import android.widget.LinearLayout
+import androidx.annotation.RequiresApi
+import com.bumptech.glide.Glide
+import com.bumptech.glide.load.engine.DiskCacheStrategy
+import com.bumptech.glide.request.RequestOptions
+import com.livelike.demo.R
+import com.livelike.demo.databinding.VideoViewBinding
+import com.livelike.engagementsdk.DismissAction
+
+class FCVideoView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr) {
+
+
+ private var inflated = false
+ private var mediaPlayer: MediaPlayer? = null
+ private var isMuted: Boolean = false
+ private var playedAtLeastOnce: Boolean = false
+ private var stopPosition: Int = 0
+ var _binding: VideoViewBinding? = null
+ var thumbnailUrl: String? = null
+ lateinit var videoEventHandler: IVideoEventHandler
+ var influencerName: String? = null
+
+ interface IVideoEventHandler {
+ fun onAskInfluencer()
+ fun onVideoPlayed(videoUrl: String)
+ }
+
+ init {
+ inflate(context)
+ }
+
+ fun setVideoThumbnail(url: String?) {
+ url?.let {
+ this.thumbnailUrl = url
+ }
+ }
+
+ fun setInfluencerNickName(nickName: String?) {
+ nickName?.let {
+ this.influencerName = it
+ }
+ }
+
+
+ var videoUrl: String? = null
+ set(value) {
+ field = value
+ if (value != null) {
+ setFrameThumbnail(value)
+ }
+ }
+
+ var dismissFunc: ((action: DismissAction) -> Unit)? =
+ {
+ removeAllViews()
+ }
+
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ }
+
+ override fun onDetachedFromWindow() {
+ super.onDetachedFromWindow()
+ release()
+ }
+
+
+ private fun inflate(context: Context) {
+ if (!inflated) {
+ inflated = true
+ _binding = VideoViewBinding.bind(
+ inflate(
+ context,
+ R.layout.video_view,
+ this@FCVideoView
+ )
+ )
+ }
+ _binding?.playbackErrorView?.visibility = View.GONE
+ _binding?.thumbnailView?.clipToOutline = true
+ if (!videoUrl.isNullOrEmpty()) {
+ videoUrl?.let {
+ setFrameThumbnail(videoUrl!!)
+ }
+ }
+ setOnClickListeners()
+ }
+
+
+ private fun setOnClickListeners() {
+ _binding?.let {
+
+
+ it.widgetContainer.setOnClickListener { view ->
+ videoUrl?.let {
+ videoEventHandler.onVideoPlayed(it)
+ }
+ }
+
+ it.askInfluencer.setOnClickListener {
+ videoEventHandler.onAskInfluencer()
+ }
+ }
+ }
+
+
+ /** sets the video view */
+ private fun initializePlayer(videoUrl: String) {
+ _binding?.let {
+ try {
+ val uri = Uri.parse(videoUrl)
+ it.playerView.clipToOutline = true
+ it.playerView.setVideoURI(uri)
+ it.playerView.requestFocus()
+ it.playerView.start()
+ unMute()
+
+ // perform set on prepared listener event on video view
+ try {
+ it.playerView.setOnPreparedListener { mp ->
+ this.mediaPlayer = mp
+ playedAtLeastOnce = true
+ it.progressBar.visibility = View.GONE
+ it.playbackErrorView.visibility = View.GONE
+ }
+
+ it.playerView.setOnCompletionListener { mediaPlayer ->
+ it.playerView?.stopPlayback()
+ setFrameThumbnail(videoUrl)
+ it.soundView.visibility = GONE
+ }
+
+ it.playerView.setOnErrorListener { _, what, extra ->
+ hideVideoControls()
+ it.progressBar.visibility = GONE
+ it.playerView.visibility = INVISIBLE
+ it.playbackErrorView.visibility = VISIBLE
+ true
+ }
+ } catch (e: Exception) {
+ it.progressBar.visibility = GONE
+ it.playbackErrorView.visibility = VISIBLE
+ e.printStackTrace()
+ }
+ } catch (e: Exception) {
+ it.progressBar.visibility = GONE
+ it.playbackErrorView.visibility = VISIBLE
+ e.printStackTrace()
+ }
+ }
+ }
+
+
+ private fun showVideoControls() {
+ _binding?.let {
+ it.icPlay.setImageResource(R.drawable.ic_play_button)
+ it.icPlay.visibility = VISIBLE
+ it.playbackErrorView.visibility = GONE
+ it.soundView.visibility = VISIBLE
+ }
+ }
+
+ private fun hideVideoControls() {
+ _binding?.let {
+ it.icPlay.setImageResource(R.drawable.ic_play_button)
+ it.icPlay.visibility = GONE
+ it.playbackErrorView.visibility = GONE
+ it.soundView.visibility = GONE
+ }
+ }
+
+ /** responsible for playing the video */
+ private fun play() {
+ _binding?.let {
+ it.progressBar.visibility = View.VISIBLE
+ hideVideoControls()
+ it.playbackErrorView.visibility = View.GONE
+ it.thumbnailView.visibility = View.GONE
+ it.playerView.visibility = View.VISIBLE
+ videoUrl?.let { url -> initializePlayer(url) }
+ }
+ }
+
+ /** responsible for resuming the video from where it was stopped */
+ private fun resume() {
+ _binding?.let {
+ it.playbackErrorView.visibility = GONE
+ it.progressBar.visibility = GONE
+ it.playerView.seekTo(stopPosition)
+ hideVideoControls()
+ if (it.playerView.currentPosition == 0) {
+ play()
+ } else {
+ it.playerView.start()
+ }
+ }
+ }
+
+
+ /** responsible for stopping the video */
+ private fun pause() {
+ _binding?.let {
+ stopPosition = it.playerView.currentPosition
+ it.playerView.pause()
+ showVideoControls()
+ }
+ }
+
+ /** responsible for stopping the player and releasing it */
+ private fun release() {
+ try {
+ playedAtLeastOnce = false
+ _binding?.let {
+ if (it.playerView != null && it.playerView.isPlaying) {
+ it.playerView.stopPlayback()
+ it.playerView.seekTo(0)
+ stopPosition = 0
+ mediaPlayer?.stop()
+ mediaPlayer?.release()
+ mediaPlayer = null
+ }
+ }
+ } catch (e: IllegalStateException) {
+ e.printStackTrace()
+ }
+ }
+
+ /** checks if the player is paused */
+ fun isPaused(): Boolean {
+ return !mediaPlayer!!.isPlaying && playedAtLeastOnce
+ }
+
+ private fun mute() {
+ try {
+ isMuted = true
+ mediaPlayer?.setVolume(0f, 0f)
+ _binding?.let {
+ it.icSound.setImageResource(R.drawable.ic_volume_on)
+ it.muteTv.text = context.resources.getString(R.string.livelike_unmute_label)
+ }
+ } catch (e: IllegalStateException) {
+ e.printStackTrace()
+ }
+ }
+
+ private fun unMute() {
+ try {
+ isMuted = false
+ mediaPlayer?.setVolume(1f, 1f)
+ _binding?.let {
+ it.icSound.setImageResource(R.drawable.ic_volume_off)
+ it.muteTv.text = context.resources.getString(R.string.livelike_mute_label)
+ }
+
+ } catch (e: IllegalStateException) {
+ e.printStackTrace()
+ }
+ }
+
+ /** extract thumbnail from the video url */
+ private fun setFrameThumbnail(videoUrl: String) {
+
+ influencerName?.let {
+ _binding?.askInfluencerCTA?.text = "ASK ${it}"
+ }
+
+ _binding?.let {
+ it.thumbnailView.visibility = VISIBLE
+ showVideoControls()
+ it.progressBar.visibility = GONE
+ it.playbackErrorView.visibility = GONE
+ it.playerView.visibility = INVISIBLE
+ var requestOptions = RequestOptions()
+
+
+ if (videoUrl.isNotEmpty()) {
+ Glide.with(context.applicationContext)
+ .asBitmap()
+ .load(videoUrl)
+ .apply(requestOptions)
+ .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
+ .thumbnail(0.1f)
+ .into(it.thumbnailView)
+ }
+
+ if (thumbnailUrl != null) {
+ Glide.with(context.applicationContext)
+ .load(thumbnailUrl)
+ .apply(requestOptions)
+ .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
+ .into(it.thumbnailView)
+ }
+ }
+ }
+
+
+ @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+ private fun setPlayerViewCornersRound(isOnlyBottomCornersToBeRounded: Boolean) {
+ _binding?.let {
+ it.playerView.outlineProvider = object : ViewOutlineProvider() {
+ override fun getOutline(view: View, outline: Outline) {
+ val corner = 20f
+ if (isOnlyBottomCornersToBeRounded) {
+ outline.setRoundRect(0, -corner.toInt(), view.width, view.height, corner)
+ } else {
+ outline.setRoundRect(
+ 0,
+ 0,
+ view.width,
+ view.height,
+ corner
+ ) // for making all corners rounded
+ }
+ }
+ }
+
+ it.playerView.clipToOutline = true
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/livelike/demo/ui/main/MyItemRecyclerViewAdapter.kt b/android/app/src/main/java/com/livelike/demo/ui/main/MyItemRecyclerViewAdapter.kt
deleted file mode 100644
index b74d7d06..00000000
--- a/android/app/src/main/java/com/livelike/demo/ui/main/MyItemRecyclerViewAdapter.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.livelike.demo.ui.main
-
-import android.view.LayoutInflater
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.recyclerview.widget.RecyclerView
-import com.livelike.demo.databinding.FragmentItemBinding
-import com.livelike.demo.ui.main.placeholder.PlaceholderContent.PlaceholderItem
-
-/**
- * [RecyclerView.Adapter] that can display a [PlaceholderItem].
- * TODO: Replace the implementation with code for your data type.
- */
-class MyItemRecyclerViewAdapter(
- private val values: List
-) : RecyclerView.Adapter() {
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
-
- return ViewHolder(
- FragmentItemBinding.inflate(
- LayoutInflater.from(parent.context),
- parent,
- false
- )
- )
-
- }
-
- override fun onBindViewHolder(holder: ViewHolder, position: Int) {
- val item = values[position]
- holder.idView.text = item.id
- holder.contentView.text = item.content
- }
-
- override fun getItemCount(): Int = values.size
-
- inner class ViewHolder(binding: FragmentItemBinding) : RecyclerView.ViewHolder(binding.root) {
- val idView: TextView = binding.itemNumber
- val contentView: TextView = binding.content
-
- override fun toString(): String {
- return super.toString() + " '" + contentView.text + "'"
- }
- }
-
-}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/livelike/demo/ui/main/PageViewModel.kt b/android/app/src/main/java/com/livelike/demo/ui/main/PageViewModel.kt
index 88f23fbd..c23748b2 100644
--- a/android/app/src/main/java/com/livelike/demo/ui/main/PageViewModel.kt
+++ b/android/app/src/main/java/com/livelike/demo/ui/main/PageViewModel.kt
@@ -1,13 +1,13 @@
package com.livelike.demo.ui.main
import android.content.Context
+import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.google.gson.GsonBuilder
import com.livelike.demo.MainActivity
import com.livelike.engagementsdk.EngagementSDK
import com.livelike.engagementsdk.LiveLikeContentSession
-import com.livelike.engagementsdk.LiveLikeUser
import com.livelike.engagementsdk.chat.data.remote.LiveLikePagination
import com.livelike.engagementsdk.core.AccessTokenDelegate
import com.livelike.engagementsdk.core.data.models.LeaderBoard
@@ -15,17 +15,18 @@ import com.livelike.engagementsdk.core.data.models.LeaderBoardEntry
import com.livelike.engagementsdk.core.data.models.LeaderBoardEntryPaginationResult
import com.livelike.engagementsdk.publicapis.ErrorDelegate
import com.livelike.engagementsdk.publicapis.LiveLikeCallback
-import com.livelike.engagementsdk.widget.domain.Reward
-import com.livelike.engagementsdk.widget.domain.RewardSource
-import com.livelike.engagementsdk.widget.domain.UserProfileDelegate
+import com.livelike.engagementsdk.publicapis.LiveLikeUserApi
+
class PageViewModel : ViewModel() {
- var chatFrag: ChatFragment? = null
+// var chatFrag: ChatFragment? = null
lateinit var engagementSDK: EngagementSDK
lateinit var contentSession: LiveLikeContentSession
val widgetJsonData: MutableLiveData = MutableLiveData()
- val currentUserLeaderBoard : MutableLiveData = MutableLiveData()
+ val currentUserLeaderBoard: MutableLiveData = MutableLiveData()
+ //val clientId = "OPba08mrr8gLZ2UMQ3uWMBOLiGhfovgIeQAEfqgI" // Fancode
+ val clientId = "mOBYul18quffrBDuq2IACKtVuLbUzXIPye5S3bq5"
fun createContentSession(programId: String?) {
contentSession = programId?.let { engagementSDK.createContentSession(it) }!!
@@ -37,9 +38,11 @@ class PageViewModel : ViewModel() {
}
}
- fun initEnagementSDK(applicationContext: Context) {
- val sharedPreferences = applicationContext.getSharedPreferences(MainActivity.ID_SHARED_PREFS, Context.MODE_PRIVATE)
- val clientId = "mOBYul18quffrBDuq2IACKtVuLbUzXIPye5S3bq5"
+ fun initEngagementSDK(applicationContext: Context) {
+ val sharedPreferences = applicationContext.getSharedPreferences(
+ MainActivity.ID_SHARED_PREFS,
+ Context.MODE_PRIVATE
+ )
engagementSDK = clientId?.let {
EngagementSDK(
it,
@@ -66,23 +69,45 @@ class PageViewModel : ViewModel() {
applicationContext.packageName,
Context.MODE_PRIVATE
).edit().putString(
- PREF_USER_ACCESS_TOKEN, accessToken
+ PREF_USER_ACCESS_TOKEN,
+ accessToken
).apply()
}
})
}!!
- //Considering this as end of init calling getCurrentProfileData
- engagementSDK.getLeaderBoardEntryForCurrentUserProfile(LEADERBOARD_ID, object : LiveLikeCallback() {
- override fun onResponse(result: LeaderBoardEntry?, error: String?) {
- if(error?.isNotEmpty() != true) {
- currentUserLeaderBoard.value = result
- }
- }
+
+ getUserProfileDetails()
+ getUserLeaderBoardDetails()
+
+
+ }
+
+ private fun getUserProfileDetails() {
+
+ engagementSDK.getCurrentUserDetails(object : LiveLikeCallback() {
+ override fun onResponse(result: LiveLikeUserApi?, error: String?) {
+ if (result !== null)
+ Log.i("USER DETAILS", result.toString())
+ }
})
+ }
+
+ private fun getUserLeaderBoardDetails() {
+
+ //Considering this as end of init calling getCurrentProfileData
+ engagementSDK.getLeaderBoardEntryForCurrentUserProfile(
+ LEADERBOARD_ID,
+ object : LiveLikeCallback() {
+ override fun onResponse(result: LeaderBoardEntry?, error: String?) {
+ if (error?.isNotEmpty() != true) {
+ currentUserLeaderBoard.value = result
+ }
+ }
+ })
}
fun initiateLeaderBoard(liveLikeCallback: LiveLikeCallback>) {
@@ -105,11 +130,11 @@ class PageViewModel : ViewModel() {
const val CONTENT_PROGRAM_ID: String = "9baf1962-f7db-43b9-ae8e-b84f1bf31988"*/
- const val LIVELIKE_CLIENT_ID: String = "mOBYul18quffrBDuq2IACKtVuLbUzXIPye5S3bq5"
- const val CONTENT_PROGRAM_ID: String = "086a57ea-e082-4cd6-a52c-48ab2bbd4ca4"
+ const val LIVELIKE_CLIENT_ID: String = "mOBYul18quffrBDuq2IACKtVuLbUzXIPye5S3bq5"
+ const val CONTENT_PROGRAM_ID: String = "086a57ea-e082-4cd6-a52c-48ab2bbd4ca4"
- /* const val LIVELIKE_CLIENT_ID: String = "mOBYul18quffrBDuq2IACKtVuLbUzXIPye5S3bq5"
- const val CONTENT_PROGRAM_ID: String = "df48a928-90af-43fa-bda0-270f65d597d6"*/
+ /* const val LIVELIKE_CLIENT_ID: String = "mOBYul18quffrBDuq2IACKtVuLbUzXIPye5S3bq5"
+ const val CONTENT_PROGRAM_ID: String = "df48a928-90af-43fa-bda0-270f65d597d6"*/
/*const val LIVELIKE_CLIENT_ID: String = "gD21f6o5DzyXl1rOvitdrvAA6F1XyBlQdU1hyexx"
const val CONTENT_PROGRAM_ID: String = "9baf1962-f7db-43b9-ae8e-b84f1bf31988"*/
@@ -118,7 +143,7 @@ class PageViewModel : ViewModel() {
/*const val LIVELIKE_CLIENT_ID: String = "ufMEjCqZXZDijB3wzOqurOSWKxazZLPDkdnJVdyb"
const val CONTENT_PROGRAM_ID: String = "4682af13-6793-45f3-bf55-dac529e0af57"*/
- const val LEADERBOARD_ID: String = "86ef1ca9-5ebf-4f8f-8a4b-a4a34697bc47"
+ const val LEADERBOARD_ID: String = "8f52892d-3530-490d-b838-1594d296e7c9" // OLD: "86ef1ca9-5ebf-4f8f-8a4b-a4a34697bc47"
/*const val LEADERBOARD_ID: String = "dd00dda1-8493-40c8-9f6f-108c58796fe0"*/
const val PREF_USER_ACCESS_TOKEN = "user_access_token"
diff --git a/android/app/src/main/java/com/livelike/demo/ui/main/VideoPlayerViewModel.kt b/android/app/src/main/java/com/livelike/demo/ui/main/VideoPlayerViewModel.kt
deleted file mode 100644
index 9c293ca2..00000000
--- a/android/app/src/main/java/com/livelike/demo/ui/main/VideoPlayerViewModel.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.livelike.demo.ui.main
-
-import androidx.lifecycle.ViewModel
-
-class VideoPlayerViewModel : ViewModel() {
- // TODO: Implement the ViewModel
-}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/livelike/demo/ui/main/WidgetTimeLineFragment.kt b/android/app/src/main/java/com/livelike/demo/ui/main/WidgetTimeLineFragment.kt
index 4532606a..326234f0 100644
--- a/android/app/src/main/java/com/livelike/demo/ui/main/WidgetTimeLineFragment.kt
+++ b/android/app/src/main/java/com/livelike/demo/ui/main/WidgetTimeLineFragment.kt
@@ -25,13 +25,14 @@ import java.io.InputStream
*/
class WidgetTimeLineFragment : BaseFragment() {
+
private lateinit var pageViewModel: PageViewModel
private var programId = ""
private lateinit var containerGrp: ViewGroup
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
pageViewModel = ViewModelProvider(requireActivity()).get(PageViewModel::class.java)
- pageViewModel.initEnagementSDK(applicationContext = requireContext())
+ pageViewModel.initEngagementSDK(applicationContext = requireContext())
val sharedPreferences = context?.getSharedPreferences(MainActivity.ID_SHARED_PREFS, Context.MODE_PRIVATE)
pageViewModel.createContentSession(sharedPreferences?.getString(MainActivity.PROGRAM_ID_KEY,"2a69e084-721a-4ba4-b692-e42846ab3a7a"))
}
diff --git a/android/app/src/main/java/com/livelike/demo/widget/FCReactData.kt b/android/app/src/main/java/com/livelike/demo/widget/FCReactData.kt
new file mode 100644
index 00000000..26422db3
--- /dev/null
+++ b/android/app/src/main/java/com/livelike/demo/widget/FCReactData.kt
@@ -0,0 +1,4 @@
+package com.livelike.demo.widget
+
+
+data class FCReactData(val programId:String, val chatRoomId: String) {}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/livelike/demo/widget/LiveLikeChatViewManager.kt b/android/app/src/main/java/com/livelike/demo/widget/LiveLikeChatViewManager.kt
new file mode 100644
index 00000000..d8545d2f
--- /dev/null
+++ b/android/app/src/main/java/com/livelike/demo/widget/LiveLikeChatViewManager.kt
@@ -0,0 +1,195 @@
+package com.livelike.demo.widget
+
+import com.facebook.react.bridge.ReactApplicationContext
+import com.facebook.react.bridge.ReadableArray
+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
+import com.google.gson.Gson
+import com.livelike.demo.LiveLikeManager
+import com.livelike.engagementsdk.chat.LiveLikeChatSession
+import com.livelike.engagementsdk.chat.data.remote.LiveLikeOrdering
+import com.livelike.engagementsdk.chat.data.remote.LiveLikePagination
+import com.livelike.engagementsdk.chat.data.remote.PinMessageInfo
+import com.livelike.engagementsdk.publicapis.LiveLikeCallback
+import java.util.*
+
+
+class LiveLikeChatViewManager(val applicationContext: ReactApplicationContext) : ViewGroupManager() {
+
+ val REACT_CLASS = "LiveLikeChatWidgetView"
+
+ companion object {
+ const val EVENT_WIDGET_SHOWN = "widgetShown"
+ const val EVENT_WIDGET_HIDDEN = "widgetHidden"
+ const val EVENT_ANALYTICS = "analytics"
+ const val COMMAND_SEND_MESSAGE = 0
+ const val COMMAND_UPDATE_NICK_NAME = 1
+ const val COMMAND_UPDATE_USER_AVATAR = 2
+ }
+
+ override fun getName(): String {
+ return REACT_CLASS
+ }
+
+
+ override fun getCommandsMap(): Map? {
+ val hashMap = hashMapOf()
+ hashMap.put("sendMessage", COMMAND_SEND_MESSAGE)
+ hashMap.put("updateNickName", COMMAND_UPDATE_NICK_NAME)
+ hashMap.put("updateUserAvatar", COMMAND_UPDATE_USER_AVATAR)
+ return hashMap
+ }
+
+ override fun receiveCommand(
+ root: LiveLikeChatWidgetView,
+ commandId: String,
+ args: ReadableArray?
+ ) {
+ super.receiveCommand(root, commandId.toInt(), args)
+ val commandIdInt = commandId.toInt()
+ when (commandIdInt) {
+ COMMAND_SEND_MESSAGE -> sendMessage(root, args)
+ COMMAND_UPDATE_NICK_NAME -> updateNickName(root, args)
+ COMMAND_UPDATE_USER_AVATAR -> updateUserAvatar(root, args)
+ else -> {}
+ }
+ }
+
+ private fun updateNickName(
+ view: LiveLikeChatWidgetView,
+ args: ReadableArray?
+ ) {
+ val nickName = args?.getString(1)
+ setNickName(nickName)
+ }
+
+ private fun updateUserAvatar(
+ view: LiveLikeChatWidgetView,
+ args: ReadableArray?
+ ) {
+ val userAvatar = args?.getString(1)
+ userAvatar?.let {
+ view.setAvatar(it)
+ }
+ }
+
+ private fun sendMessage(
+ view: LiveLikeChatWidgetView,
+ args: ReadableArray?
+ ) {
+ val message = args?.getString(1)
+ message?.let {
+ view.sendChatMessage(it)
+ view.scrollToBottom()
+ }
+ }
+
+ override fun createViewInstance(reactContext: ThemedReactContext): LiveLikeChatWidgetView {
+ return LiveLikeChatWidgetView(reactContext, applicationContext);
+ }
+
+
+ @ReactProp(name = "data")
+ fun setdata(view: LiveLikeChatWidgetView, data: String) {
+ val gson = Gson()
+ var fcReactData: FCReactData? = gson.fromJson(data, FCReactData::class.java)
+ fcReactData?.let {
+ val contentSession = LiveLikeManager.getContentSession(it.programId)
+ view.updateContentSession(contentSession)
+ onConfiguration(view, it.chatRoomId)
+ }
+ }
+
+
+ @ReactProp(name = "userNickName")
+ fun setUserNickName(view: LiveLikeChatWidgetView, nickName: String?) {
+ setNickName(nickName)
+ }
+
+ @ReactProp(name = "userAvatarUrl")
+ fun setUserAvatar(view: LiveLikeChatWidgetView, userAvatar: String?) {
+ view.setAvatar(userAvatar)
+ }
+
+ @ReactProp(name = "influencerName")
+ fun setInfluencerName(view: LiveLikeChatWidgetView, influencerName: String) {
+ view.setInfluencerName(influencerName)
+ }
+
+ private fun onConfiguration(chatView: LiveLikeChatWidgetView, chatRoomId: String) {
+ if (isChatConfigurable(chatView)) {
+ chatView.setUpWidgetView()
+ chatView.setupChat(chatRoomId)
+ //chatView.setUpTimelineView()
+ this.registerPinnedMessageHandler(chatView, chatRoomId)
+ }
+ }
+
+ override fun onDropViewInstance(view: LiveLikeChatWidgetView) {
+ super.onDropViewInstance(view)
+ view.destroyChatSession()
+ }
+
+ override fun getExportedCustomDirectEventTypeConstants(): Map {
+ var map = HashMap()
+ map.put(EVENT_WIDGET_SHOWN, MapBuilder.of("registrationName", "onWidgetShown"));
+ map.put(EVENT_WIDGET_HIDDEN, MapBuilder.of("registrationName", "onWidgetHidden"));
+ map.put(EVENT_ANALYTICS, MapBuilder.of("registrationName", "onEvent"));
+ map.put(
+ LiveLikeChatWidgetView.CHAT_MESSAGE_SENT,
+ MapBuilder.of("registrationName", LiveLikeChatWidgetView.CHAT_MESSAGE_SENT)
+ );
+ map.put(
+ LiveLikeChatWidgetView.EVENT_VIDEO_PLAYED,
+ MapBuilder.of("registrationName", LiveLikeChatWidgetView.EVENT_VIDEO_PLAYED)
+ );
+ map.put(
+ LiveLikeChatWidgetView.EVENT_ASK_INFLUENCER,
+ MapBuilder.of("registrationName", LiveLikeChatWidgetView.EVENT_ASK_INFLUENCER)
+ );
+ map.put(
+ LiveLikeChatWidgetView.EVENT_REMOVE_ALL_PIN_MESSAGES,
+ MapBuilder.of("registrationName", LiveLikeChatWidgetView.EVENT_REMOVE_ALL_PIN_MESSAGES)
+ );
+ return map;
+ }
+
+
+ // Helper Functions
+ private fun setNickName(nickName: String?) {
+
+ if (nickName == "" || nickName == null) {
+ return
+ }
+
+ LiveLikeManager.engagementSDK.updateChatNickname(nickName)
+ }
+
+
+ private fun registerPinnedMessageHandler(chatView: LiveLikeChatWidgetView, chatRoomId: String) {
+
+
+ LiveLikeManager.engagementSDK.chat()?.getPinMessageInfoList(
+ chatRoomId!!,
+ LiveLikeOrdering.ASC,
+ LiveLikePagination.FIRST,
+ object : LiveLikeCallback>() {
+ override fun onResponse(
+ result: List?, error:
+ String?
+ ) {
+ result?.let {
+ chatView.handleHistoricalPinMessages(result)
+ }
+ }
+ })
+
+ }
+
+
+ private fun isChatConfigurable(chatView: LiveLikeChatWidgetView): Boolean {
+ return chatView.chatSession != null
+ }
+}
diff --git a/android/app/src/main/java/com/livelike/demo/widget/LiveLikeChatWidgetView.kt b/android/app/src/main/java/com/livelike/demo/widget/LiveLikeChatWidgetView.kt
new file mode 100644
index 00000000..a8b190fa
--- /dev/null
+++ b/android/app/src/main/java/com/livelike/demo/widget/LiveLikeChatWidgetView.kt
@@ -0,0 +1,444 @@
+package com.livelike.demo.widget
+
+import android.content.res.Resources
+import android.os.Handler
+import android.os.Looper
+import android.util.Log
+import android.util.TypedValue
+import android.view.Choreographer
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.content.ContentProviderCompat.requireContext
+import androidx.core.content.ContextCompat
+import androidx.recyclerview.widget.RecyclerView
+import com.bumptech.glide.Glide
+import com.bumptech.glide.load.resource.bitmap.CenterCrop
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners
+import com.bumptech.glide.request.RequestOptions
+import com.facebook.react.bridge.*
+import com.facebook.react.uimanager.ThemedReactContext
+import com.facebook.react.uimanager.events.RCTEventEmitter
+import com.livelike.demo.LiveLikeManager
+import com.livelike.demo.R
+import com.livelike.demo.adapters.PinMessageAdapter
+import com.livelike.demo.databinding.FcChatViewBinding
+import com.livelike.demo.ui.main.FCVideoView
+import com.livelike.engagementsdk.LiveLikeContentSession
+import com.livelike.engagementsdk.MessageListener
+import com.livelike.engagementsdk.chat.ChatView
+import com.livelike.engagementsdk.chat.ChatViewDelegate
+import com.livelike.engagementsdk.chat.ChatViewThemeAttributes
+import com.livelike.engagementsdk.chat.LiveLikeChatSession
+import com.livelike.engagementsdk.chat.data.remote.PinMessageInfo
+import com.livelike.engagementsdk.publicapis.ChatMessageType
+import com.livelike.engagementsdk.publicapis.LiveLikeCallback
+import com.livelike.engagementsdk.publicapis.LiveLikeChatMessage
+import com.livelike.engagementsdk.widget.timeline.WidgetTimeLineViewModel
+import com.livelike.engagementsdk.widget.timeline.WidgetsTimeLineView
+import org.json.JSONObject
+
+
+class LiveLikeChatWidgetView(
+ val context: ThemedReactContext,
+ val applicationContext: ReactApplicationContext
+) : ConstraintLayout(context), LifecycleEventListener {
+
+ private var renderWidget = true
+ lateinit var chatView: ChatView;
+ private var chatViewBinding: FcChatViewBinding? = null
+ var fallback: Choreographer.FrameCallback;
+ var chatSession: LiveLikeChatSession? = null
+ var contentSession: LiveLikeContentSession? = null
+ var userAvatarUrl = ""
+ private var pinMessageAdapter = PinMessageAdapter()
+ var influencerNickName: String? = null
+
+
+ init {
+
+ this.applicationContext.addLifecycleEventListener(this)
+ this.fallback = Choreographer.FrameCallback() {
+ manuallyLayoutChildren();
+ viewTreeObserver.dispatchOnGlobalLayout();
+ if (renderWidget) {
+ Choreographer.getInstance().postFrameCallback(this!!.fallback)
+ }
+ }
+ Choreographer.getInstance().postFrameCallback(fallback)
+ val inflater: LayoutInflater = LayoutInflater.from(context)
+ chatViewBinding = FcChatViewBinding.bind(inflater.inflate(R.layout.fc_chat_view, null))
+ registerPinMessagesHandler()
+ chatViewBinding?.let {
+ chatView = it.chatView
+ addView(it.root)
+ configureChatView()
+ }
+
+ }
+
+ private fun registerPinMessagesHandler() {
+ pinMessageAdapter.pinMessageHandler = object : PinMessageAdapter.PinMessageActionHandler {
+ override fun onVideoPlayed(videoUrl: String) {
+ val params = Arguments.createMap()
+ params.putString("videoUrl", videoUrl)
+ params.putString("source", "pinned")
+ sendEvent(EVENT_VIDEO_PLAYED, params)
+ }
+
+ override fun onRemoveAllPinMessages() {
+ val params = Arguments.createMap()
+ sendEvent(EVENT_REMOVE_ALL_PIN_MESSAGES, params)
+ }
+ }
+ }
+
+ override fun onHostResume() {}
+
+ override fun onHostPause() {}
+
+ override fun onHostDestroy() {}
+
+ private fun updateChatSession(chatSession: LiveLikeChatSession?) {
+ this.chatSession = chatSession
+ this.chatSession?.allowDiscardOwnPublishedMessageInSubscription = false
+ }
+
+ private fun configureChatView() {
+ chatView.isChatInputVisible = false
+ chatView.allowMediaFromKeyboard = false
+ }
+
+ fun scrollToBottom() {
+ chatView.scrollChatToBottom()
+ }
+
+ private fun manuallyLayoutChildren() {
+ for (i in 0 until getChildCount()) {
+ var child = getChildAt(i);
+ child.measure(
+ MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)
+ );
+ child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
+ }
+ }
+
+
+ fun setupChat(chatRoomId: String) {
+ setSessionToChatView()
+ connectToChatRoom(chatRoomId)
+ setUserAvatar()
+ registerMessageListener()
+ registerVideoMessageHandler()
+ }
+
+
+ fun setAvatar(avatarUrl: String?) {
+ avatarUrl?.let {
+ this.userAvatarUrl = it
+ chatSession?.shouldDisplayAvatar = true
+ chatSession?.avatarUrl = it
+ }
+ }
+
+ private fun setUserAvatar() {
+ if (this.userAvatarUrl == "") {
+ return
+ }
+
+ chatSession?.shouldDisplayAvatar = true
+ chatSession?.avatarUrl = this.userAvatarUrl
+ }
+
+
+ private fun connectToChatRoom(chatRoomId: String) {
+ var connectedChatRoomId: String? = null
+ chatSession?.getCurrentChatRoom?.let {
+ connectedChatRoomId = it()
+ }
+
+ if(connectedChatRoomId.isNullOrEmpty() || connectedChatRoomId != chatRoomId){
+ chatSession?.connectToChatRoom(
+ chatRoomId,
+ callback = object : LiveLikeCallback() {
+ override fun onResponse(result: Unit?, error: String?) {
+ }
+ })
+ }
+
+ }
+
+ private fun setSessionToChatView() {
+ if (chatSession != null) {
+ chatView.setSession(chatSession!!)
+ }
+ }
+
+
+ private fun registerVideoMessageHandler() {
+ chatView.chatViewDelegate = object : ChatViewDelegate {
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ viewType: ChatMessageType
+ ): RecyclerView.ViewHolder {
+ return FCVideoViewHolder(FCVideoView(parent.context))
+ }
+
+ override fun onBindViewHolder(
+ holder: RecyclerView.ViewHolder,
+ liveLikeChatMessage: LiveLikeChatMessage,
+ chatViewThemeAttributes: ChatViewThemeAttributes,
+ showChatAvatar: Boolean
+ ) {
+
+
+ registerVideoEvents(holder.itemView as FCVideoView)
+
+ chatViewThemeAttributes.apply {
+
+ (holder.itemView as FCVideoView)._binding?.let {
+
+ // Setting nickName
+ it.chatNickname.text = liveLikeChatMessage.nickname
+ it.chatNickname.setTextSize(
+ TypedValue.COMPLEX_UNIT_PX,
+ chatUserNameTextSize
+ )
+ it.chatNickname.isAllCaps = chatUserNameTextAllCaps
+
+
+ // Setting Video Title
+ val videoTitle = getCustomDataProp("title", liveLikeChatMessage)
+ it.videoTitle.text = videoTitle
+
+ // User Avatar
+ it.imgChatAvatar.visibility = when (showChatAvatar) {
+ true -> View.VISIBLE
+ else -> View.GONE
+ }
+ val avatarLayoutParams = LinearLayout.LayoutParams(
+ chatAvatarWidth,
+ chatAvatarHeight
+ )
+ avatarLayoutParams.gravity = chatAvatarGravity
+ it.imgChatAvatar.layoutParams = avatarLayoutParams
+ avatarLayoutParams.setMargins(
+ avatarLayoutParams.leftMargin,
+ avatarLayoutParams.topMargin + dpToPx(8),
+ avatarLayoutParams.rightMargin,
+ avatarLayoutParams.bottomMargin
+ )
+ val options = RequestOptions()
+ if (chatAvatarCircle) {
+ options.optionalCircleCrop()
+ }
+ if (chatAvatarRadius > 0) {
+ options.transform(
+ CenterCrop(),
+ RoundedCorners(chatAvatarRadius)
+ )
+ }
+ val userPic: String? = getCustomDataProp("userPic", liveLikeChatMessage)
+ Glide.with(holder.itemView.context.applicationContext)
+ .load(userPic)
+ .placeholder(chatUserPicDrawable)
+ .error(chatUserPicDrawable)
+ .into(it.imgChatAvatar)
+
+
+ // Handle Chat background
+ val chatBackgroundLayoutParams = it.chatBackground.layoutParams as ConstraintLayout.LayoutParams
+ chatBackgroundLayoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
+ it.chatBackground.layoutParams = chatBackgroundLayoutParams
+
+ // Chat Bubble background
+ val chatBubbleLayoutParams: LinearLayout.LayoutParams =
+ it.chatBubbleBackground.layoutParams as LinearLayout.LayoutParams
+ it.chatBubbleBackground.setPadding(
+ chatBubblePaddingLeft,
+ chatBubblePaddingTop,
+ chatBubblePaddingRight,
+ chatBubblePaddingBottom + dpToPx(6)
+ )
+ it.chatBubbleBackground.layoutParams = chatBubbleLayoutParams
+ it.chatBubbleBackground.clipToOutline = true
+
+
+ // Video Thumbnail
+ val url: String? = getCustomDataProp("url", liveLikeChatMessage)
+ url?.let {
+ val videoViewHolder = (holder as FCVideoViewHolder)
+ val fcVideoView = videoViewHolder.itemView as FCVideoView
+ try {
+ fcVideoView.setVideoThumbnail(
+ getCustomDataProp(
+ "videoThumbnail",
+ liveLikeChatMessage
+ )
+ )
+ fcVideoView.setInfluencerNickName(influencerNickName)
+ } catch (e: Exception) {
+ }
+ fcVideoView.videoUrl = it
+ }
+
+ }
+ }
+ }
+ }
+
+ }
+
+ private fun registerVideoEvents(videoView: FCVideoView) {
+
+ videoView.videoEventHandler = object : FCVideoView.IVideoEventHandler {
+ override fun onAskInfluencer() {
+ sendEvent(EVENT_ASK_INFLUENCER, Arguments.createMap())
+ }
+
+ override fun onVideoPlayed(videoUrl: String) {
+ val map = Arguments.createMap()
+ map.putString("videoUrl", videoUrl)
+ map.putString("source", "chat")
+ sendEvent(EVENT_VIDEO_PLAYED, map)
+ }
+ }
+ }
+
+ private fun getCustomDataProp(key: String, messagePayload: LiveLikeChatMessage?): String? {
+
+ try {
+ messagePayload?.custom_data?.let {
+ val jsonObject = JSONObject(it)
+ return jsonObject.get(key).toString()
+ }
+ } catch (e: java.lang.Exception) {
+
+ }
+ return null
+ }
+
+ class FCVideoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {}
+
+
+ private fun registerMessageListener() {
+
+ chatSession?.setMessageListener(object : MessageListener {
+ override fun onDeleteMessage(messageId: String) {}
+
+ override fun onHistoryMessage(messages: List) {}
+
+ override fun onNewMessage(message: LiveLikeChatMessage) {}
+
+ override fun onPinMessage(message: PinMessageInfo) {
+ Handler(Looper.getMainLooper()).post(Runnable {
+ pinMessageAdapter.addPinMessage(message, context)
+ })
+ }
+
+ override fun onUnPinMessage(pinMessageId: String) {
+ Handler(Looper.getMainLooper()).post(Runnable {
+ pinMessageAdapter.removePinMessage(pinMessageId)
+ })
+ }
+ })
+ }
+
+
+ fun sendChatMessage(message: String) {
+ chatSession?.sendChatMessage(
+ message,
+ null,
+ null,
+ null,
+ object : LiveLikeCallback() {
+ override fun onResponse(result: LiveLikeChatMessage?, error: String?) {
+ val params = Arguments.createMap()
+ params.putString("message", message)
+ params.putBoolean("isSuccess", error.isNullOrEmpty())
+ sendEvent(CHAT_MESSAGE_SENT, params)
+ }
+ })
+
+ }
+
+
+ fun dpToPx(dp: Int): Int {
+ val scale = Resources.getSystem().displayMetrics.density
+ return (dp * scale + 0.5f).toInt()
+ }
+
+
+ fun handleHistoricalPinMessages(pinnedMessages: List) {
+ chatViewBinding?.pinnedMessageList?.let {
+ pinMessageAdapter.setup(it)
+ }
+ pinnedMessages.forEach {
+ pinMessageAdapter.addPinMessage(it, context)
+ }
+ }
+
+ fun setInfluencerName(influencerName: String) {
+ this.influencerNickName = influencerName
+ }
+
+ fun sendEvent(
+ eventName: String,
+ params: WritableMap?
+ ) {
+ val reactContext = this.getContext() as ReactContext;
+ reactContext.getJSModule(RCTEventEmitter::class.java)
+ .receiveEvent(this.getId(), eventName, params)
+ }
+
+ fun destroyChatSession() {
+ if(chatSession != null) {
+ chatView.clearSession()
+ chatSession = null
+ pinMessageAdapter.clear()
+ }
+ }
+
+ fun updateContentSession(contentSession: LiveLikeContentSession?) {
+ this.contentSession = contentSession
+ updateChatSession(contentSession?.chatSession)
+ }
+
+ fun setUpWidgetView() {
+ this.contentSession?.let { this.chatViewBinding?.widgetView?.setSession(it) }
+ }
+
+ fun setUpTimelineView() {
+ var timeLineViewModel: WidgetTimeLineViewModel
+ this.contentSession.let {
+ timeLineViewModel = WidgetTimeLineViewModel(it!!)
+
+ val timeLineView = WidgetsTimeLineView(
+ context,
+ timeLineViewModel,
+ sdk = LiveLikeManager.engagementSDK
+ )
+
+ timeLineView.setSeparator(
+ ContextCompat.getDrawable(
+ context,
+ R.drawable.white_separator
+ )
+ )
+ chatViewBinding?.timelineViewContainer?.addView(timeLineView)
+
+ }
+
+ }
+
+
+ companion object {
+ const val CHAT_MESSAGE_SENT = "onChatMessageSent"
+ const val EVENT_VIDEO_PLAYED = "onVideoPlayed"
+ const val EVENT_ASK_INFLUENCER = "onAskInfluencer"
+ const val EVENT_REMOVE_ALL_PIN_MESSAGES = "onRemoveAllPinMessages"
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/livelike/demo/widget/LiveLikeWidgetView.kt b/android/app/src/main/java/com/livelike/demo/widget/LiveLikeWidgetView.kt
new file mode 100644
index 00000000..99c4900f
--- /dev/null
+++ b/android/app/src/main/java/com/livelike/demo/widget/LiveLikeWidgetView.kt
@@ -0,0 +1,260 @@
+package com.livelike.demo.widget
+
+import android.view.Choreographer
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.LinearLayout
+import com.facebook.react.bridge.*
+import com.facebook.react.uimanager.ThemedReactContext
+import com.facebook.react.uimanager.events.RCTEventEmitter
+import com.livelike.demo.LiveLikeManager
+import com.livelike.demo.R
+import com.livelike.demo.ui.main.CustomAskWidgetView
+import com.livelike.engagementsdk.LiveLikeContentSession
+import com.livelike.engagementsdk.LiveLikeWidget
+import com.livelike.engagementsdk.chat.data.remote.LiveLikePagination
+import com.livelike.engagementsdk.core.services.messaging.proxies.LiveLikeWidgetEntity
+import com.livelike.engagementsdk.core.services.messaging.proxies.WidgetInterceptor
+import com.livelike.engagementsdk.publicapis.LiveLikeCallback
+import com.livelike.engagementsdk.widget.LiveLikeWidgetViewFactory
+import com.livelike.engagementsdk.widget.view.WidgetView
+import com.livelike.engagementsdk.widget.widgetModel.*
+
+
+/*
+ TODO:
+ 1. Register Dismiss on sendButton Click.
+ 2. Correct the UI for the confirmation.
+ 3. Register dismiss on the cross button
+ 4. Change the colour of the userName for video posting
+
+ */
+
+class LiveLikeWidgetView(
+ val context: ThemedReactContext,
+ val applicationContext: ReactApplicationContext
+) : LinearLayout(context), LifecycleEventListener {
+
+
+ var contentSession: LiveLikeContentSession? = null
+ lateinit var widgetView: WidgetView;
+ lateinit var customAskWidgetView: CustomAskWidgetView
+ var widgetDetails: LiveLikeWidget? = null
+ var fallback: Choreographer.FrameCallback;
+ private var renderWidget = false
+ private var influencerNickName: String? = null
+
+
+ init {
+ this.applicationContext.addLifecycleEventListener(this)
+ this.fallback = Choreographer.FrameCallback() {
+ manuallyLayoutChildren();
+ viewTreeObserver.dispatchOnGlobalLayout();
+ if(renderWidget) {
+ Choreographer.getInstance().postFrameCallback(this!!.fallback)
+ }
+ }
+ Choreographer.getInstance().postFrameCallback(fallback)
+ createView()
+ registerCustomViewModel()
+ }
+
+ private fun fetchPublishedWidgets() {
+ contentSession?.getPublishedWidgets(
+ LiveLikePagination.FIRST,
+ object : LiveLikeCallback>() {
+ override fun onResponse(result: List?, error: String?) {
+
+ if(result?.size!! > 0) {
+ result?.first()?.let {
+ widgetDetails = it
+ }
+ }
+
+ }
+ })
+ }
+
+ private fun registerWidgetInterceptor() {
+ contentSession?.widgetInterceptor = object : WidgetInterceptor() {
+ override fun widgetWantsToShow(widgetData: LiveLikeWidgetEntity) {}
+ }
+ }
+
+ private fun subscribeWidgetStream() {
+ contentSession?.widgetStream?.subscribe(this) {
+ it?.let {
+ widgetDetails = it
+ }
+ }
+ }
+
+ fun hideWidget() {
+ this.renderWidget = false
+ contentSession?.widgetInterceptor?.dismissWidget()
+ widgetView.clearWidget()
+ sendEvent(LiveLikeWidgetViewManager.EVENT_WIDGET_HIDDEN, null)
+ }
+
+
+ fun displayAskWidget() {
+ widgetDetails?.let {
+ renderWidget = true
+ this.widgetView.displayWidget(LiveLikeManager.engagementSDK, it)
+ Choreographer.getInstance().postFrameCallback(this.fallback)
+ sendEvent(LiveLikeWidgetViewManager.EVENT_WIDGET_SHOWN, null)
+ }
+ }
+
+ private fun createView() {
+ val parentView = LayoutInflater.from(context).inflate(R.layout.fc_widget_view, null) as LinearLayout;
+ addView(parentView)
+ widgetView = parentView.findViewById(R.id.widget_view);
+ }
+
+ override fun onHostResume() {
+// contentSession?.resume()
+ }
+
+ override fun onHostPause() {
+// contentSession?.pause()
+ }
+
+ override fun onHostDestroy() {
+// contentSession?.close()
+ this.contentSession = null
+ }
+
+ fun updateContentSession(contentSession: LiveLikeContentSession) {
+ this.contentSession = contentSession;
+ fetchPublishedWidgets()
+ registerWidgetInterceptor()
+ subscribeWidgetStream()
+ }
+
+
+ private fun registerCustomViewListeners() {
+ customAskWidgetView.userEventsListener =
+ object : CustomAskWidgetView.UserEventsListener {
+ override fun closeDialog() {
+ hideWidget()
+ }
+
+ override fun onMessageSent(message: String) {
+ val map = Arguments.createMap()
+ map.putString("message", message)
+ sendEvent(EVENT_INFLUENCER_MESSAGE_SENT, map)
+ }
+ }
+ }
+
+
+ fun manuallyLayoutChildren() {
+ for (i in 0 until getChildCount()) {
+ var child = getChildAt(i);
+ child.measure(
+ MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)
+ );
+ child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
+ }
+ }
+
+ fun setInfluencerName(influencerName: String) {
+ this.influencerNickName = influencerName
+ }
+
+ fun sendEvent(
+ eventName: String,
+ params: WritableMap?
+ ) {
+ val reactContext = this.getContext() as ReactContext;
+ reactContext.getJSModule(RCTEventEmitter::class.java)
+ .receiveEvent(this.getId(), eventName, params)
+ }
+
+
+ private fun registerCustomViewModel() {
+ widgetView.widgetViewFactory = object : LiveLikeWidgetViewFactory {
+
+ override fun createAlertWidgetView(alertWidgetModel: AlertWidgetModel): View? {
+ return null
+ }
+
+ override fun createCheerMeterView(cheerMeterWidgetModel: CheerMeterWidgetmodel): View? {
+ return null
+ }
+
+ override fun createImageSliderWidgetView(imageSliderWidgetModel: ImageSliderWidgetModel): View? {
+ return null
+ }
+
+ override fun createNumberPredictionFollowupWidgetView(
+ followUpWidgetViewModel: NumberPredictionFollowUpWidgetModel,
+ isImage: Boolean
+ ): View? {
+ return null
+ }
+
+ override fun createNumberPredictionWidgetView(
+ numberPredictionWidgetModel: NumberPredictionWidgetModel,
+ isImage: Boolean
+ ): View? {
+ return null
+ }
+
+ override fun createPollWidgetView(
+ pollWidgetModel: PollWidgetModel,
+ isImage: Boolean
+ ): View? {
+ return null
+ }
+
+ override fun createPredictionFollowupWidgetView(
+ followUpWidgetViewModel: FollowUpWidgetViewModel,
+ isImage: Boolean
+ ): View? {
+ return null
+ }
+
+ override fun createPredictionWidgetView(
+ predictionViewModel: PredictionWidgetViewModel,
+ isImage: Boolean
+ ): View? {
+ return null
+ }
+
+ override fun createQuizWidgetView(
+ quizWidgetModel: QuizWidgetModel,
+ isImage: Boolean
+ ): View? {
+ return null
+ }
+
+ override fun createSocialEmbedWidgetView(socialEmbedWidgetModel: SocialEmbedWidgetModel): View? {
+ return null
+ }
+
+ // Returns a view for customising Ask a Widget
+ override fun createTextAskWidgetView(imageSliderWidgetModel: TextAskWidgetModel): View? {
+ customAskWidgetView = CustomAskWidgetView(context).apply {
+ askWidgetModel = imageSliderWidgetModel
+ influencerNickName?.let {
+ influencerName = influencerNickName as String
+ }
+ }
+ registerCustomViewListeners()
+ return customAskWidgetView
+ }
+
+ override fun createVideoAlertWidgetView(videoAlertWidgetModel: VideoAlertWidgetModel): View? {
+ return null
+ }
+ }
+ }
+
+
+ companion object {
+ const val EVENT_INFLUENCER_MESSAGE_SENT = "onInfluencerMessageSent"
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/livelike/demo/widget/LiveLikeWidgetViewManager.kt b/android/app/src/main/java/com/livelike/demo/widget/LiveLikeWidgetViewManager.kt
new file mode 100644
index 00000000..9ab47e64
--- /dev/null
+++ b/android/app/src/main/java/com/livelike/demo/widget/LiveLikeWidgetViewManager.kt
@@ -0,0 +1,65 @@
+package com.livelike.demo.widget
+
+import com.facebook.react.bridge.ReactApplicationContext
+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
+import com.livelike.demo.LiveLikeManager
+import java.util.*
+
+
+class LiveLikeWidgetViewManager(val applicationContext: ReactApplicationContext) :
+ ViewGroupManager() {
+
+ val REACT_CLASS = "LiveLikeWidgetView"
+
+ companion object {
+ const val EVENT_WIDGET_SHOWN = "widgetShown"
+ const val EVENT_WIDGET_HIDDEN = "widgetHidden"
+ const val EVENT_ANALYTICS = "analytics"
+ }
+
+ override fun getName(): String {
+ return REACT_CLASS
+ }
+
+ override fun createViewInstance(reactContext: ThemedReactContext): LiveLikeWidgetView {
+ return LiveLikeWidgetView(reactContext, applicationContext);
+ }
+
+
+ @ReactProp(name = "programId")
+ fun setProgramId(view: LiveLikeWidgetView, programId: String) {
+ val session = LiveLikeManager.getContentSession(programId)
+ session?.let {
+ view.updateContentSession(it)
+ }
+
+ }
+
+ @ReactProp(name = "influencerName")
+ fun setInfluencerName(view: LiveLikeWidgetView, influencerName: String) {
+ view.setInfluencerName(influencerName)
+ }
+
+ @ReactProp(name = "showAskWidget")
+ fun showAskWidget(view: LiveLikeWidgetView, showWidget: Boolean) {
+ if (showWidget) {
+ view.displayAskWidget()
+ }
+ }
+
+ override fun onDropViewInstance(view: LiveLikeWidgetView) {
+ super.onDropViewInstance(view)
+ }
+
+ override fun getExportedCustomDirectEventTypeConstants(): Map {
+ var map = HashMap()
+ map.put(EVENT_WIDGET_SHOWN, MapBuilder.of("registrationName", "onWidgetShown"));
+ map.put(EVENT_WIDGET_HIDDEN, MapBuilder.of("registrationName", "onWidgetHidden"));
+ map.put(LiveLikeWidgetView.EVENT_INFLUENCER_MESSAGE_SENT, MapBuilder.of("registrationName", LiveLikeWidgetView.EVENT_INFLUENCER_MESSAGE_SENT));
+ map.put(EVENT_ANALYTICS, MapBuilder.of("registrationName", "onEvent"));
+ return map;
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/res/.DS_Store b/android/app/src/main/res/.DS_Store
new file mode 100644
index 00000000..633f6955
Binary files /dev/null and b/android/app/src/main/res/.DS_Store differ
diff --git a/android/app/src/main/res/drawable/ask_a_influencer_background.xml b/android/app/src/main/res/drawable/ask_a_influencer_background.xml
new file mode 100644
index 00000000..c5ec9f4a
--- /dev/null
+++ b/android/app/src/main/res/drawable/ask_a_influencer_background.xml
@@ -0,0 +1,10 @@
+
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/ask_a_widget_background.xml b/android/app/src/main/res/drawable/ask_a_widget_background.xml
new file mode 100644
index 00000000..708de250
--- /dev/null
+++ b/android/app/src/main/res/drawable/ask_a_widget_background.xml
@@ -0,0 +1,9 @@
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/ask_a_widget_send_btn_selector.xml b/android/app/src/main/res/drawable/ask_a_widget_send_btn_selector.xml
new file mode 100644
index 00000000..dddb4414
--- /dev/null
+++ b/android/app/src/main/res/drawable/ask_a_widget_send_btn_selector.xml
@@ -0,0 +1,15 @@
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/ask_influencer_chat_input_box.xml b/android/app/src/main/res/drawable/ask_influencer_chat_input_box.xml
new file mode 100644
index 00000000..48595e14
--- /dev/null
+++ b/android/app/src/main/res/drawable/ask_influencer_chat_input_box.xml
@@ -0,0 +1,15 @@
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/ask_influencer_check_icon.xml b/android/app/src/main/res/drawable/ask_influencer_check_icon.xml
new file mode 100644
index 00000000..0333739a
--- /dev/null
+++ b/android/app/src/main/res/drawable/ask_influencer_check_icon.xml
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/android/app/src/main/res/drawable/chat_bubble_background.xml b/android/app/src/main/res/drawable/chat_bubble_background.xml
index 521d112d..84a7f8e8 100644
--- a/android/app/src/main/res/drawable/chat_bubble_background.xml
+++ b/android/app/src/main/res/drawable/chat_bubble_background.xml
@@ -1,11 +1,11 @@
-
+ android:shape="rectangle" android:padding="8dp">
+
+ android:bottomRightRadius="6dp"
+ android:bottomLeftRadius="6dp"
+ android:topLeftRadius="0dp"
+ android:topRightRadius="6dp"/>
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/chat_input_cursor.xml b/android/app/src/main/res/drawable/chat_input_cursor.xml
new file mode 100644
index 00000000..be0a58b0
--- /dev/null
+++ b/android/app/src/main/res/drawable/chat_input_cursor.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/chat_input_view_background.xml b/android/app/src/main/res/drawable/chat_input_view_background.xml
new file mode 100644
index 00000000..11b812f5
--- /dev/null
+++ b/android/app/src/main/res/drawable/chat_input_view_background.xml
@@ -0,0 +1,8 @@
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/chat_video_bubble_background.xml b/android/app/src/main/res/drawable/chat_video_bubble_background.xml
new file mode 100644
index 00000000..cc2b43b0
--- /dev/null
+++ b/android/app/src/main/res/drawable/chat_video_bubble_background.xml
@@ -0,0 +1,10 @@
+
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/close_icon.xml b/android/app/src/main/res/drawable/close_icon.xml
new file mode 100644
index 00000000..cea4ebe6
--- /dev/null
+++ b/android/app/src/main/res/drawable/close_icon.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/default_avatar.png b/android/app/src/main/res/drawable/default_avatar.png
new file mode 100644
index 00000000..658ecc77
Binary files /dev/null and b/android/app/src/main/res/drawable/default_avatar.png differ
diff --git a/android/app/src/main/res/drawable/default_video_thumbnail.png b/android/app/src/main/res/drawable/default_video_thumbnail.png
new file mode 100644
index 00000000..035b188a
Binary files /dev/null and b/android/app/src/main/res/drawable/default_video_thumbnail.png differ
diff --git a/android/app/src/main/res/drawable/ic_ask_influencer_question.xml b/android/app/src/main/res/drawable/ic_ask_influencer_question.xml
new file mode 100644
index 00000000..bad37ca7
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_ask_influencer_question.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/android/app/src/main/res/drawable/ic_chat_bottom_bar_gradient.xml b/android/app/src/main/res/drawable/ic_chat_bottom_bar_gradient.xml
new file mode 100644
index 00000000..f99d67f1
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_chat_bottom_bar_gradient.xml
@@ -0,0 +1,11 @@
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/ic_chat_message_bubble_rounded_rectangle.xml b/android/app/src/main/res/drawable/ic_chat_message_bubble_rounded_rectangle.xml
new file mode 100644
index 00000000..a8b409b1
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_chat_message_bubble_rounded_rectangle.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/ic_chat_reaction_turner.xml b/android/app/src/main/res/drawable/ic_chat_reaction_turner.xml
new file mode 100644
index 00000000..b6dfd6d7
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_chat_reaction_turner.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/android/app/src/main/res/drawable/ic_chat_send.png b/android/app/src/main/res/drawable/ic_chat_send.png
new file mode 100644
index 00000000..04dde2bd
Binary files /dev/null and b/android/app/src/main/res/drawable/ic_chat_send.png differ
diff --git a/android/app/src/main/res/drawable/ic_play_button.xml b/android/app/src/main/res/drawable/ic_play_button.xml
new file mode 100644
index 00000000..dad8c89d
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_play_button.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/android/app/src/main/res/drawable/ic_play_small.xml b/android/app/src/main/res/drawable/ic_play_small.xml
new file mode 100644
index 00000000..ac6dd54b
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_play_small.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/android/app/src/main/res/drawable/ic_volume_off.xml b/android/app/src/main/res/drawable/ic_volume_off.xml
new file mode 100644
index 00000000..13a65729
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_volume_off.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/android/app/src/main/res/drawable/ic_volume_on.xml b/android/app/src/main/res/drawable/ic_volume_on.xml
new file mode 100644
index 00000000..3247fb4d
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_volume_on.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/android/app/src/main/res/drawable/influencer_question.png b/android/app/src/main/res/drawable/influencer_question.png
new file mode 100644
index 00000000..bfddc244
Binary files /dev/null and b/android/app/src/main/res/drawable/influencer_question.png differ
diff --git a/android/app/src/main/res/drawable/pin_chat_message_background.xml b/android/app/src/main/res/drawable/pin_chat_message_background.xml
new file mode 100644
index 00000000..c109c0c8
--- /dev/null
+++ b/android/app/src/main/res/drawable/pin_chat_message_background.xml
@@ -0,0 +1,10 @@
+
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/pin_icon.xml b/android/app/src/main/res/drawable/pin_icon.xml
new file mode 100644
index 00000000..4110a93c
--- /dev/null
+++ b/android/app/src/main/res/drawable/pin_icon.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/pin_video_play_background.xml b/android/app/src/main/res/drawable/pin_video_play_background.xml
new file mode 100644
index 00000000..7dccb0ab
--- /dev/null
+++ b/android/app/src/main/res/drawable/pin_video_play_background.xml
@@ -0,0 +1,9 @@
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/user_input_background.xml b/android/app/src/main/res/drawable/user_input_background.xml
new file mode 100644
index 00000000..1e3e5135
--- /dev/null
+++ b/android/app/src/main/res/drawable/user_input_background.xml
@@ -0,0 +1,10 @@
+
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/video_alert_rounded_corner_black_background.xml b/android/app/src/main/res/drawable/video_alert_rounded_corner_black_background.xml
new file mode 100644
index 00000000..ee455244
--- /dev/null
+++ b/android/app/src/main/res/drawable/video_alert_rounded_corner_black_background.xml
@@ -0,0 +1,9 @@
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/video_view_background.xml b/android/app/src/main/res/drawable/video_view_background.xml
new file mode 100644
index 00000000..750af112
--- /dev/null
+++ b/android/app/src/main/res/drawable/video_view_background.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/font/antenna_bold.otf b/android/app/src/main/res/font/antenna_bold.otf
new file mode 100644
index 00000000..592b5292
Binary files /dev/null and b/android/app/src/main/res/font/antenna_bold.otf differ
diff --git a/android/app/src/main/res/font/noto_sans_display_bold.ttf b/android/app/src/main/res/font/noto_sans_display_bold.ttf
new file mode 100644
index 00000000..47f07289
Binary files /dev/null and b/android/app/src/main/res/font/noto_sans_display_bold.ttf differ
diff --git a/android/app/src/main/res/font/noto_sans_display_italic.ttf b/android/app/src/main/res/font/noto_sans_display_italic.ttf
new file mode 100644
index 00000000..9cbdab91
Binary files /dev/null and b/android/app/src/main/res/font/noto_sans_display_italic.ttf differ
diff --git a/android/app/src/main/res/font/noto_sans_display_regular.ttf b/android/app/src/main/res/font/noto_sans_display_regular.ttf
new file mode 100644
index 00000000..b695c97b
Binary files /dev/null and b/android/app/src/main/res/font/noto_sans_display_regular.ttf differ
diff --git a/android/app/src/main/res/font/noto_sans_display_semibold.ttf b/android/app/src/main/res/font/noto_sans_display_semibold.ttf
new file mode 100644
index 00000000..39c27e2a
Binary files /dev/null and b/android/app/src/main/res/font/noto_sans_display_semibold.ttf differ
diff --git a/android/app/src/main/res/font/notosansdisplay_bold.ttf b/android/app/src/main/res/font/notosansdisplay_bold.ttf
new file mode 100644
index 00000000..47f07289
Binary files /dev/null and b/android/app/src/main/res/font/notosansdisplay_bold.ttf differ
diff --git a/android/app/src/main/res/layout/chat_fragment_layout.xml b/android/app/src/main/res/layout/chat_fragment_layout.xml
deleted file mode 100644
index 413026a7..00000000
--- a/android/app/src/main/res/layout/chat_fragment_layout.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/chat_input.xml b/android/app/src/main/res/layout/chat_input.xml
new file mode 100644
index 00000000..8af79f9a
--- /dev/null
+++ b/android/app/src/main/res/layout/chat_input.xml
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/layout/chat_user_profile_bar.xml b/android/app/src/main/res/layout/chat_user_profile_bar.xml
new file mode 100644
index 00000000..eaa07136
--- /dev/null
+++ b/android/app/src/main/res/layout/chat_user_profile_bar.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/layout/chat_view.xml b/android/app/src/main/res/layout/chat_view.xml
new file mode 100644
index 00000000..d836c73a
--- /dev/null
+++ b/android/app/src/main/res/layout/chat_view.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/layout/default_chat_cell.xml b/android/app/src/main/res/layout/default_chat_cell.xml
new file mode 100644
index 00000000..df988b97
--- /dev/null
+++ b/android/app/src/main/res/layout/default_chat_cell.xml
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/fc_chat_view.xml b/android/app/src/main/res/layout/fc_chat_view.xml
new file mode 100644
index 00000000..63d0a9f6
--- /dev/null
+++ b/android/app/src/main/res/layout/fc_chat_view.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/fc_custom_ask_a_widget.xml b/android/app/src/main/res/layout/fc_custom_ask_a_widget.xml
new file mode 100644
index 00000000..6d7ada7b
--- /dev/null
+++ b/android/app/src/main/res/layout/fc_custom_ask_a_widget.xml
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/layout/fc_widget_view.xml b/android/app/src/main/res/layout/fc_widget_view.xml
new file mode 100644
index 00000000..e19c92d7
--- /dev/null
+++ b/android/app/src/main/res/layout/fc_widget_view.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/layout/pin_hybrid_message.xml b/android/app/src/main/res/layout/pin_hybrid_message.xml
new file mode 100644
index 00000000..41fe58de
--- /dev/null
+++ b/android/app/src/main/res/layout/pin_hybrid_message.xml
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/video_view.xml b/android/app/src/main/res/layout/video_view.xml
new file mode 100644
index 00000000..23dcaa61
--- /dev/null
+++ b/android/app/src/main/res/layout/video_view.xml
@@ -0,0 +1,279 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml
index d938bb5b..f2026235 100644
--- a/android/app/src/main/res/values/colors.xml
+++ b/android/app/src/main/res/values/colors.xml
@@ -28,6 +28,24 @@
#213335
#213335
#213335
+ #f0f0f0
+ #ff5000
+ #dcdee4
+ #121212
+ #121212
+ #000000
+ #f6fbff
+ #1c2c55
+ #000000
+ #000000
+ #787878
+ #787878
+ #ffffff
+ #000000
+ #121212
+ #ff5000
+
+ 787878
- @color/green
- @color/red
diff --git a/android/app/src/main/res/values/dimens.xml b/android/app/src/main/res/values/dimens.xml
index e698f8ab..93aafafd 100644
--- a/android/app/src/main/res/values/dimens.xml
+++ b/android/app/src/main/res/values/dimens.xml
@@ -24,4 +24,106 @@
16sp
16sp
18sp
+ 4dp
+ 30dp
+ 6dp
+ 6sp
+ 180dp
+ 8dp
+ 146dp
+ 268dp
+
+
+ 12sp
+ 12sp
+
+ 28dp
+ 28dp
+
+ 2dp
+ 0dp
+
+ 28dp
+ 28dp
+
+ 72dp
+ 40dp
+ 14sp
+
+ 12dp
+ 0dp
+ 0dp
+
+ 6dp
+ 16dp
+ 4dp
+ 16dp
+ 6dp
+ 6dp
+ 20dp
+ 20dp
+ 20dp
+ 32dp
+ 32dp
+ 12dp
+ 16sp
+ 12dp
+ 83dp
+ 12dp
+ 14dp
+ 14dp
+ 12dp
+ 12dp
+ 11dp
+ 72dp
+ 38dp
+ 8dp
+ 12sp
+ 8dp
+ 40dp
+ 40dp
+ 8dp
+ 14sp
+
+ 55dp
+ 16dp
+ 4dp
+ 8dp
+ 12dp
+ 8dp
+ 40dp
+ 72dp
+ 13dp
+ 13dp
+ 4dp
+ 4dp
+ 2dp
+ 14dp
+ 14dp
+ 12dp
+ 3dp
+ 6dp
+ 6dp
+ 18dp
+ 18dp
+
+ 38dp
+ 12dp
+ 8dp
+ 8dp
+ 8dp
+ 8dp
+ 8dp
+ 4dp
+ 4dp
+ 40dp
+ 40dp
+ 8dp
+ 16dp
+ 16dp
+ 4dp
+ 12sp
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/integer.xml b/android/app/src/main/res/values/integer.xml
new file mode 100644
index 00000000..40c53b0b
--- /dev/null
+++ b/android/app/src/main/res/values/integer.xml
@@ -0,0 +1,5 @@
+
+
+ 250
+ 240
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index 0e8f486d..d67d1b88 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -2,4 +2,10 @@
ReactLivelike
contain
false
+ Send a message
+ Ask Expert
+ Type your question here..
+ SEND
+ Your comment has been posted. Let\'s see if our guest responds
+ Ask a question
\ No newline at end of file
diff --git a/android/app/src/main/res/values/theme.xml b/android/app/src/main/res/values/theme.xml
index d41d69c6..2fd0e3b2 100644
--- a/android/app/src/main/res/values/theme.xml
+++ b/android/app/src/main/res/values/theme.xml
@@ -1,181 +1,131 @@
\ No newline at end of file
diff --git a/android/build.gradle b/android/build.gradle
index 959b5fcc..faa8dcdf 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -4,10 +4,10 @@ buildscript {
ext {
buildToolsVersion = "29.0.3"
minSdkVersion = 21
- compileSdkVersion = 30
- targetSdkVersion = 30
- kotlin_version = "1.4.21"
- kotlinVersion = '1.4.21'
+ compileSdkVersion = 31
+ targetSdkVersion = 31
+ kotlin_version = "1.6.0"
+ kotlinVersion = '1.6.0'
dokkaVersion = "0.10.0"
mavenPluginVersion = "2.1"
bugsnagVersion = "4+"
@@ -16,12 +16,12 @@ buildscript {
dokkaVersion = "0.10.0"
expressoVersion = "3.0.2"
exoplayerVersion = "2.9.3"
- coroutinesVersion = "1.4.1"
+ coroutinesVersion = "1.6.0"
glideVersion = '4.12.0'
gradleVersion = '3.5.0'
gsonVersion = '2.8.6'
junitVersion = '4.13.2'
- kotlinVersion = '1.4.21'
+ kotlinVersion = '1.6.0'
ktlintVersion = '0.30.0'
lottieVersion = '3.5.0'
mavenPluginVersion = '2.1'
@@ -41,7 +41,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath("com.android.tools.build:gradle:4.1.0")
+ classpath('com.android.tools.build:gradle:7.3.1')
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index 7665b0fa..b1159fc5 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/android/local.properties b/android/local.properties
deleted file mode 100644
index adbda979..00000000
--- a/android/local.properties
+++ /dev/null
@@ -1,8 +0,0 @@
-## This file must *NOT* be checked into Version Control Systems,
-# as it contains information specific to your local configuration.
-#
-# Location of the SDK. This is only used by Gradle.
-# For customization when using a Version Control System, please read the
-# header note.
-#Thu Jan 13 14:54:27 IST 2022
-sdk.dir=/Users/changdeojadhav/Library/Android/sdk
diff --git a/ios/ReactLivelike.xcworkspace/xcuserdata/changdeojadhav.xcuserdatad/UserInterfaceState.xcuserstate b/ios/ReactLivelike.xcworkspace/xcuserdata/changdeojadhav.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 00000000..033e529b
Binary files /dev/null and b/ios/ReactLivelike.xcworkspace/xcuserdata/changdeojadhav.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/package.json b/package.json
index d11d83f5..429e58b5 100644
--- a/package.json
+++ b/package.json
@@ -9,17 +9,17 @@
"eject": "expo eject"
},
"dependencies": {
- "expo": "~44.0.0",
- "expo-status-bar": "~1.2.0",
- "react": "17.0.1",
- "react-dom": "17.0.1",
- "react-native": "0.64.3",
- "react-native-web": "0.17.1",
- "expo-splash-screen": "~0.14.1",
- "expo-dev-client": "~0.8.0"
+ "expo": "^47.0.0",
+ "expo-status-bar": "~1.4.2",
+ "react": "18.1.0",
+ "react-dom": "18.1.0",
+ "react-native": "0.70.5",
+ "react-native-web": "~0.18.7",
+ "expo-splash-screen": "~0.17.5",
+ "expo-dev-client": "~2.0.1"
},
"devDependencies": {
- "@babel/core": "^7.12.9"
+ "@babel/core": "^7.19.3"
},
"private": true
}