): Title? {
+
+ // Set PreTitle
+ val preTitleText = (attributes["preTitleText"] as? String) ?: return null
+
+ // Set PreTitle Color
+ val preTitleTextColor = (attributes["preTitleTextColor"] as? String) ?: return null
+ val preTitleTextColor1 = Color.parseColor(preTitleTextColor)
+
+ // Set Title
+ val titleText = (attributes["titleText"] as? String) ?: return null
+
+ // Set SubTitle
+ val subTitleText = (attributes["subTitleText"] as? String) ?: return null
+
+ return Title(preTitleText,preTitleTextColor1, titleText, subTitleText)
+ }
+ }
+}
diff --git a/app/src/main/java/com/bulletin/models/Version.kt b/app/src/main/java/com/bulletin/models/Version.kt
new file mode 100644
index 0000000..31bf615
--- /dev/null
+++ b/app/src/main/java/com/bulletin/models/Version.kt
@@ -0,0 +1,19 @@
+package com.bulletin.models
+
+import com.bulletin.extension.validVersion
+import java.io.Serializable
+
+data class Version(val version: String) : Serializable {
+
+ companion object {
+
+ fun init(version: String): Version? {
+
+ // Set Title
+ val validVersion = version.validVersion() ?: return null
+
+ return Version(validVersion)
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/bulletin/utilities/AppStorageHelper.kt b/app/src/main/java/com/bulletin/utilities/AppStorageHelper.kt
new file mode 100644
index 0000000..ccd86ab
--- /dev/null
+++ b/app/src/main/java/com/bulletin/utilities/AppStorageHelper.kt
@@ -0,0 +1,26 @@
+package com.bulletin.utilities
+
+import android.content.Context
+import android.content.SharedPreferences
+import com.bulletin.BulletinApp.Companion.applicationContext
+
+object AppStorageHelper {
+
+ private const val SHARED_PREFS_NAME = "Bulletin_Shared_Prefs"
+ val shared: SharedPreferences =
+ applicationContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE)
+
+ inline fun SharedPreferences.edit(operation: (SharedPreferences.Editor) -> Unit) {
+ val edit = edit()
+ operation(edit)
+ edit.apply()
+ }
+
+ var SharedPreferences.clearValue
+ set(newValue) {
+ edit {
+ it.clear()
+ }
+ }
+ get() = run { }
+}
diff --git a/app/src/main/java/com/bulletin/utilities/AppUpdatedChecker.kt b/app/src/main/java/com/bulletin/utilities/AppUpdatedChecker.kt
new file mode 100644
index 0000000..d7dc56e
--- /dev/null
+++ b/app/src/main/java/com/bulletin/utilities/AppUpdatedChecker.kt
@@ -0,0 +1,42 @@
+package com.bulletin.utilities
+
+import com.bulletin.extension.lastSeenVersion
+
+
+object AppUpdatedChecker {
+
+ enum class AppRunningState(val value: Int) {
+ NORMAL(1),
+ FRESHINSTALL(2),
+ AFTERUPDATE(3);
+ }
+
+ var appRunningState = AppRunningState.NORMAL
+
+
+ fun currentAppRunningState() {
+
+ // Fetch Last Known App Version from User Defaults
+ val lastKnownAppVersion: String? =
+ AppStorageHelper.shared.lastSeenVersion?.version // get(LAST_KNOWN_APP_VERSION_KEY);
+
+ // Fetch Current App Version
+ val currentAppVersion: String? = VersionUtil.getApplicationVersion()
+ appRunningState = if (lastKnownAppVersion != null) {
+ if (currentAppVersion == lastKnownAppVersion) {
+
+ // App is running on Same Version
+ AppRunningState.NORMAL
+ } else {
+
+ // App is running newer version then Last known App Version
+ AppRunningState.AFTERUPDATE
+ }
+ } else {
+
+ // App Running After Fresh Install
+ AppRunningState.FRESHINSTALL
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/bulletin/utilities/DeviceUtils.kt b/app/src/main/java/com/bulletin/utilities/DeviceUtils.kt
new file mode 100644
index 0000000..90c5488
--- /dev/null
+++ b/app/src/main/java/com/bulletin/utilities/DeviceUtils.kt
@@ -0,0 +1,65 @@
+package com.bulletin.utilities
+
+import android.content.Context
+import android.util.DisplayMetrics
+
+
+object DeviceUtil {
+
+ /**
+ * This method converts dp unit to equivalent pixels, depending on device density.
+ *
+ * @param dp A value in dp (density independent pixels) unit. Which we need to convert into pixels
+ * @return A float value to represent px equivalent to dp depending on device density
+ */
+ fun convertDpToPixel(context: Context, dp: Float): Int {
+ val resources = context.resources
+ val metrics = resources.displayMetrics
+ return dp.toInt() * (metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT)
+ }
+
+ /**
+ * This method converts device specific pixels to density independent pixels.
+ *
+ * @param px A value in px (pixels) unit. Which we need to convert into db
+ * @return A float value to represent dp equivalent to px value
+ */
+ fun convertPixelsToDp(context: Context, px: Float): Float {
+ val resources = context.resources
+ val metrics = resources.displayMetrics
+ return px / (metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT)
+ }
+
+ /**
+ * This method converts sp unit to equivalent pixels, depending on device density.
+ *
+ * @param sp A value in sp (scale independent pixels) unit. Which we need to convert into pixels
+ * @return A float value to represent px equivalent to sp depending on device scale
+ */
+ fun convertSpToPixel(context: Context, sp: Float): Int {
+ val scaledDensity = context.resources.displayMetrics.scaledDensity
+ return (sp * scaledDensity).toInt()
+ }
+
+ /**
+ * This method converts device specific pixels to scale independent pixels.
+ *
+ * @param px A value in px (pixels) unit. Which we need to convert into db
+ * @return A float value to represent sp equivalent to px value
+ */
+ fun convertPixelsToSp(context: Context, px: Float): Float {
+ val scaledDensity = context.resources.displayMetrics.scaledDensity
+ return px / scaledDensity
+ }
+
+ fun getDensity(context: Context): String {
+ val density = context.resources.displayMetrics.density
+ return if (density <= 1.0f) {
+ "1x"
+ } else if (density > 1.0f && density <= 2.0f) {
+ "2x"
+ } else {
+ "3x"
+ }
+ }
+}
diff --git a/app/src/main/java/com/bulletin/utilities/GsonHelper.kt b/app/src/main/java/com/bulletin/utilities/GsonHelper.kt
new file mode 100644
index 0000000..8fe3ec0
--- /dev/null
+++ b/app/src/main/java/com/bulletin/utilities/GsonHelper.kt
@@ -0,0 +1,28 @@
+package com.bulletin.utilities
+
+import com.bulletin.models.BulletinItem
+import com.google.gson.Gson
+import com.google.gson.GsonBuilder
+import com.wrx.wazirx.views.bulletin.model.Media
+
+object GsonHelper {
+
+ private val gsonBuilder: GsonBuilder by lazy {
+ val _gsonBuilder = GsonBuilder()
+ _gsonBuilder.setDateFormat("yyyy-MM-dd'T'HH:mm:ss+Z")
+ _gsonBuilder.enableComplexMapKeySerialization()
+ setupGsonBuilder(_gsonBuilder)
+ _gsonBuilder
+ }
+
+ val gsonInstance: Gson by lazy {
+ gsonBuilder.create()
+ }
+
+ private fun setupGsonBuilder(gsonBuilder: GsonBuilder) {
+
+ gsonBuilder.registerTypeAdapterFactory(Media.runtimeTypeAdapterFactory)
+ gsonBuilder.registerTypeAdapterFactory(BulletinItem.runtimeTypeAdapterFactory)
+ }
+
+}
diff --git a/app/src/main/java/com/bulletin/utilities/RuntimeTypeAdapterFactory.kt b/app/src/main/java/com/bulletin/utilities/RuntimeTypeAdapterFactory.kt
new file mode 100644
index 0000000..824b49f
--- /dev/null
+++ b/app/src/main/java/com/bulletin/utilities/RuntimeTypeAdapterFactory.kt
@@ -0,0 +1,223 @@
+package com.bulletin.utilities
+
+
+import com.google.gson.*
+import com.google.gson.internal.Streams
+import com.google.gson.reflect.TypeToken
+import com.google.gson.stream.JsonReader
+import com.google.gson.stream.JsonWriter
+import java.io.IOException
+
+/**
+ * Adapts values whose runtime type may differ from their declaration type. This
+ * is necessary when a field's type is not the same type that GSON should create
+ * when deserializing that field. For example, consider these types:
+ * `abstract class Shape {
+ * int x;
+ * int y;
+ * }
+ * class Circle extends Shape {
+ * int radius;
+ * }
+ * class Rectangle extends Shape {
+ * int width;
+ * int height;
+ * }
+ * class Diamond extends Shape {
+ * int width;
+ * int height;
+ * }
+ * class Drawing {
+ * Shape bottomShape;
+ * Shape topShape;
+ * }
+` *
+ *
+ * Without additional type information, the serialized JSON is ambiguous. Is
+ * the bottom shape in this drawing a rectangle or a diamond? `{
+ * "bottomShape": {
+ * "width": 10,
+ * "height": 5,
+ * "x": 0,
+ * "y": 0
+ * },
+ * "topShape": {
+ * "radius": 2,
+ * "x": 4,
+ * "y": 1
+ * }
+ * }`
+ * This class addresses this problem by adding type information to the
+ * serialized JSON and honoring that type information when the JSON is
+ * deserialized: `{
+ * "bottomShape": {
+ * "type": "Diamond",
+ * "width": 10,
+ * "height": 5,
+ * "x": 0,
+ * "y": 0
+ * },
+ * "topShape": {
+ * "type": "Circle",
+ * "radius": 2,
+ * "x": 4,
+ * "y": 1
+ * }
+ * }`
+ * Both the type field name (`"type"`) and the type labels (`"Rectangle"`) are configurable.
+ *
+ *
+ * Registering Types
+ * Create a `RuntimeTypeAdapterFactory` by passing the base type and type field
+ * name to the [.of] factory method. If you don't supply an explicit type
+ * field name, `"type"` will be used. `RuntimeTypeAdapterFactory shapeAdapterFactory
+ * = RuntimeTypeAdapterFactory.of(Shape.class, "type");
+` *
+ * Next register all of your subtypes. Every subtype must be explicitly
+ * registered. This protects your application from injection attacks. If you
+ * don't supply an explicit type label, the type's simple name will be used.
+ * `shapeAdapter.registerSubtype(Rectangle.class, "Rectangle");
+ * shapeAdapter.registerSubtype(Circle.class, "Circle");
+ * shapeAdapter.registerSubtype(Diamond.class, "Diamond");
+` *
+ * Finally, register the type adapter factory in your application's GSON builder:
+ * `Gson gson = new GsonBuilder()
+ * .registerTypeAdapterFactory(shapeAdapterFactory)
+ * .create();
+` *
+ * Like `GsonBuilder`, this API supports chaining: `RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
+ * .registerSubtype(Rectangle.class)
+ * .registerSubtype(Circle.class)
+ * .registerSubtype(Diamond.class);
+` *
+ */
+class RuntimeTypeAdapterFactory private constructor(
+ baseType: Class<*>?,
+ typeFieldName: String?
+) : TypeAdapterFactory {
+ private val baseType: Class<*>
+ private var defaultTypeLabel: String? = null
+ private val typeFieldName: String
+ private val labelToSubtype: MutableMap> = LinkedHashMap()
+ /**
+ * Registers `type` identified by `label`. Labels are case
+ * sensitive.
+ *
+ * @throws IllegalArgumentException if either `type` or `label`
+ * have already been registered on this type adapter.
+ */
+ /**
+ * Registers `type` identified by its [simple][Class.getSimpleName]. Labels are case sensitive.
+ *
+ * @throws IllegalArgumentException if either `type` or its simple name
+ * have already been registered on this type adapter.
+ */
+ @JvmOverloads
+ fun registerSubtype(
+ type: Class?,
+ label: String? = type!!.simpleName
+ ): RuntimeTypeAdapterFactory {
+ if (type == null || label == null) {
+ throw NullPointerException()
+ }
+ labelToSubtype[label] = type
+ return this
+ }
+
+ /**
+ * Registers `defaultType` which is used when the type is null or unknown
+ * You must call this method if you want to handle the cases where you expect the type to be null.
+ * If this method is not called then JsonParseException will be thrown if any null type is encountered.
+ */
+ fun registerDefaultSubtype(type: Class?, label: String?): RuntimeTypeAdapterFactory {
+ if (type == null || label == null) {
+ throw NullPointerException()
+ }
+ defaultTypeLabel = label
+ registerSubtype(type, label)
+ return this
+ }
+
+ override fun create(gson: Gson, type: TypeToken): TypeAdapter? {
+ if (type.rawType != baseType) {
+ return null
+ }
+ val labelToDelegate: MutableMap> = LinkedHashMap()
+ val subtypeToDelegate: MutableMap, TypeAdapter<*>> = LinkedHashMap()
+ for ((key, value) in labelToSubtype) {
+ val delegate = gson.getDelegateAdapter(this, TypeToken.get(value))
+ labelToDelegate[key] = delegate
+ subtypeToDelegate[value] = delegate
+ }
+ return object : TypeAdapter() {
+ @Throws(IOException::class)
+ override fun read(`in`: JsonReader): R {
+ val jsonElement = Streams.parse(`in`)
+ val labelJsonElement = jsonElement.asJsonObject[typeFieldName]
+ val label: String
+ label = if (labelJsonElement == null) ({
+ if (defaultTypeLabel == null) {
+ throw JsonParseException(
+ "cannot deserialize " + baseType
+ + " because it does not define a default " + typeFieldName
+ )
+ } else {
+ defaultTypeLabel
+ }
+
+ }).toString() else {
+ labelJsonElement.asString
+ }
+
+ var delegate// registration requires that subtype extends T
+ = labelToDelegate[label] as TypeAdapter?
+ if (delegate == null) {
+ delegate = labelToDelegate[defaultTypeLabel] as TypeAdapter?
+ }
+ return delegate!!.fromJsonTree(jsonElement)
+ }
+
+ @Throws(IOException::class)
+ override fun write(out: JsonWriter, value: R) {
+ val srcType: Class<*> = value!!::class.java
+ val delegate// registration requires that subtype extends T
+ = subtypeToDelegate[srcType] as TypeAdapter?
+ ?: throw JsonParseException(
+ "cannot serialize " + srcType.name
+ + "; did you forget to register a subtype?"
+ )
+ val jsonObject = delegate.toJsonTree(value).asJsonObject
+ val clone = JsonObject()
+ for ((key, value1) in jsonObject.entrySet()) {
+ clone.add(key, value1)
+ }
+ Streams.write(clone, out)
+ }
+ }.nullSafe()
+ }
+
+ companion object {
+ /**
+ * Creates a new runtime type adapter using for `baseType` using `typeFieldName` as the type field name. Type field names are case sensitive.
+ */
+ fun of(baseType: Class?, typeFieldName: String?): RuntimeTypeAdapterFactory {
+ return RuntimeTypeAdapterFactory(baseType, typeFieldName)
+ }
+
+ /**
+ * Creates a new runtime type adapter for `baseType` using `"type"` as
+ * the type field name.
+ */
+ fun of(baseType: Class?): RuntimeTypeAdapterFactory {
+ return RuntimeTypeAdapterFactory(baseType, "type")
+ }
+ }
+
+ init {
+ if (typeFieldName == null || baseType == null) {
+ throw NullPointerException()
+ }
+ this.baseType = baseType
+ this.typeFieldName = typeFieldName
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/bulletin/utilities/ThemeUtils.kt b/app/src/main/java/com/bulletin/utilities/ThemeUtils.kt
new file mode 100644
index 0000000..69c93fa
--- /dev/null
+++ b/app/src/main/java/com/bulletin/utilities/ThemeUtils.kt
@@ -0,0 +1,29 @@
+package com.bulletin.utilities
+
+import android.content.Context
+import android.graphics.drawable.GradientDrawable
+import android.util.TypedValue
+import android.view.View
+import androidx.annotation.AttrRes
+import androidx.annotation.ColorInt
+
+
+object ThemeUtils {
+ @ColorInt
+ fun getAttributedColor(@AttrRes attr: Int, context: Context): Int {
+ val value = TypedValue()
+ context.theme.resolveAttribute(attr, value, true)
+ return value.data
+ }
+
+ fun applyThemeDrawable(view: View, @AttrRes attr: Int) {
+ val color = getAttributedColor(attr, view.context)
+ if (color != 0) {
+ val drawable = view.background
+ if (drawable is GradientDrawable) {
+ drawable.setColor(color)
+ }
+ view.setBackgroundDrawable(drawable)
+ }
+ }
+}
diff --git a/app/src/main/java/com/bulletin/utilities/VersionUtil.kt b/app/src/main/java/com/bulletin/utilities/VersionUtil.kt
new file mode 100644
index 0000000..d1ecca4
--- /dev/null
+++ b/app/src/main/java/com/bulletin/utilities/VersionUtil.kt
@@ -0,0 +1,91 @@
+package com.bulletin.utilities
+
+import android.content.pm.PackageManager
+import com.bulletin.BulletinApp
+import com.bulletin.extension.isEmpty
+
+object VersionUtil {
+
+ /**
+ * if v1>v2 : returns 1
+ * if v1 vnum2) return 1
+
+ if (vnum2 > vnum1) return -1
+
+ // if equal, reset variables and
+ // go for next numeric part
+ vnum2 = 0
+ vnum1 = vnum2
+ i++
+ j++
+ }
+ return 0
+ }
+
+ fun getLatestVersion(v1: String?, v2: String?): String? {
+ if (v1.isEmpty()) return v2
+ if (v2.isEmpty()) return v1
+
+ // if (v1!=null && v2!=null) call versionCompare
+ return if (v1?.let { v2?.let { it1 -> versionCompare(it, it1) } } == -1) {
+ v2
+ } else {
+ v1
+ }
+ }
+
+ fun getApplicationVersion(): String? {
+ try {
+ val pInfo =
+ BulletinApp.shared.packageManager.getPackageInfo(BulletinApp.shared.packageName, 0)
+ return pInfo.versionName
+ } catch (e: PackageManager.NameNotFoundException) {
+ e.printStackTrace()
+ }
+ return ""
+ }
+
+ fun getVersionInAscendingOrder(set: MutableSet): List {
+ val list: List = set.toList()
+ return list.sortedWith { version, version1 -> versionCompare(version1, version) };
+ }
+
+}
diff --git a/app/src/main/java/com/bulletin/utilities/ViewUtil.kt b/app/src/main/java/com/bulletin/utilities/ViewUtil.kt
new file mode 100644
index 0000000..4132216
--- /dev/null
+++ b/app/src/main/java/com/bulletin/utilities/ViewUtil.kt
@@ -0,0 +1,89 @@
+package com.bulletin.utilities
+
+import android.annotation.SuppressLint
+import android.view.MotionEvent
+import android.view.View
+import android.view.animation.Animation
+import android.view.animation.DecelerateInterpolator
+import android.view.animation.ScaleAnimation
+
+
+object ViewUtil {
+
+ @SuppressLint("ClickableViewAccessibility")
+ fun addBounceEffect(view: View) {
+ view.setOnTouchListener { v: View, event: MotionEvent ->
+ onButtonTouch(
+ v,
+ event
+ )
+ }
+ }
+
+ private fun onButtonTouch(button: View, event: MotionEvent): Boolean {
+ if (event.action == MotionEvent.ACTION_DOWN) {
+ onButtonPressed(button)
+ return true
+ } else if (event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL || event.action == MotionEvent.ACTION_OUTSIDE) {
+ onButtonReleased(button)
+ }
+
+ // Handle click event
+ if (event.action == MotionEvent.ACTION_UP) {
+ button.performClick()
+ }
+ return false
+ }
+
+ private fun onButtonTouchListener(
+ button: View,
+ event: MotionEvent,
+ touchListener: TouchListener
+ ): Boolean {
+ if (event.action == MotionEvent.ACTION_DOWN) {
+ // onButtonPressed(button);
+ touchListener.onButtonPressed()
+ return true
+ } else if (event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL || event.action == MotionEvent.ACTION_OUTSIDE) {
+ // onButtonReleased(button);
+ touchListener.onButtonReleased()
+ }
+
+ // Handle click event
+ if (event.action == MotionEvent.ACTION_UP) {
+ button.performClick()
+ }
+ return false
+ }
+
+ fun onButtonPressed(button: View) {
+ val scaleAnimation = ScaleAnimation(
+ 1f, 0.9f,
+ 1f, 0.9f,
+ Animation.RELATIVE_TO_SELF, 0.5f,
+ Animation.RELATIVE_TO_SELF, 0.5f
+ )
+ scaleAnimation.interpolator = DecelerateInterpolator()
+ scaleAnimation.duration = 100
+ scaleAnimation.fillAfter = true
+ button.startAnimation(scaleAnimation)
+ }
+
+ fun onButtonReleased(button: View) {
+ val scaleAnimation = ScaleAnimation(
+ 0.9f, 1f,
+ 0.9f, 1f,
+ Animation.RELATIVE_TO_SELF, 0.5f,
+ Animation.RELATIVE_TO_SELF, 0.5f
+ )
+ scaleAnimation.interpolator = DecelerateInterpolator()
+ scaleAnimation.duration = 100
+ scaleAnimation.fillAfter = true
+ button.startAnimation(scaleAnimation)
+ }
+
+ interface TouchListener {
+ fun onButtonPressed()
+ fun onButtonReleased()
+ }
+}
diff --git a/app/src/main/java/com/bulletin/viewHolder/BaseViewHolder.kt b/app/src/main/java/com/bulletin/viewHolder/BaseViewHolder.kt
new file mode 100644
index 0000000..4ba4297
--- /dev/null
+++ b/app/src/main/java/com/bulletin/viewHolder/BaseViewHolder.kt
@@ -0,0 +1,42 @@
+package com.bulletin.viewHolder
+
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+import androidx.viewbinding.ViewBinding
+import com.bulletin.FormRecyclerViewAdapter
+import com.bulletin.models.BulletinItem
+
+
+abstract class BaseViewHolder(
+ VB: ViewBinding,
+ listener: FormRecyclerViewAdapter.OnItemClickListener
+) : RecyclerView.ViewHolder(VB.root) {
+
+ var _listener: FormRecyclerViewAdapter.OnItemClickListener? = null
+
+ /** The view binding instance. */
+ protected var _binding: ViewBinding? = null
+ protected var topCellSeparatorView: View? = null
+ protected var bottomCellSeparatorView: View? = null
+ protected var bgView: View? = null
+ lateinit var baseItem: T
+
+ init {
+ _listener = listener
+ _binding = VB
+ }
+
+ // MARK: - Override Methods
+ open fun updateAppearance() {
+
+ }
+
+ // region Abstract Method
+ open fun bind(item: T) {
+ baseItem = item
+ updateAppearance()
+ }
+
+ abstract fun getBackgroundView(): View?
+ // endregion
+}
diff --git a/app/src/main/java/com/bulletin/viewHolder/FormSectionActionButtonViewHolder.kt b/app/src/main/java/com/bulletin/viewHolder/FormSectionActionButtonViewHolder.kt
new file mode 100644
index 0000000..e07cfe7
--- /dev/null
+++ b/app/src/main/java/com/bulletin/viewHolder/FormSectionActionButtonViewHolder.kt
@@ -0,0 +1,83 @@
+package com.bulletin.viewHolder
+
+import android.content.res.ColorStateList
+import android.view.View
+import com.bulletin.FormRecyclerViewAdapter
+import com.bulletin.extension.textAppearence
+import com.bulletin.models.ActionButton
+import com.bulletin.models.BulletinItem
+import com.bulletin.utilities.DeviceUtil
+import com.bulletin.utilities.ThemeUtils
+import com.bulletin.utilities.ViewUtil
+import com.example.bulletin.R
+import com.example.bulletin.databinding.LayoutFormSectionActionButtonBinding
+
+
+class FormSectionActionButtonViewHolder(
+ val viewBinding: LayoutFormSectionActionButtonBinding,
+ listener: FormRecyclerViewAdapter.OnItemClickListener
+) :
+ BaseViewHolder(viewBinding, listener) {
+
+ // region Methods
+ override fun bind(item: ActionButton) {
+ super.bind(item)
+
+ // Set Title
+ if (!item.title.isNullOrBlank()) {
+ viewBinding.actionButton.text = item.title
+ viewBinding.actionButton.setVisibility(View.VISIBLE)
+ } else {
+ viewBinding.actionButton.text = ""
+ viewBinding.actionButton.setVisibility(View.GONE)
+ }
+
+ viewBinding.actionButton.setOnClickListener {
+ _listener?.formDidTriggerEvent(
+ BulletinItem.EventType.TRIGGER_ACTION,
+ item,
+ adapterPosition
+ )
+ }
+
+ ViewUtil.addBounceEffect(viewBinding.actionButton)
+
+ }
+
+ override fun getBackgroundView(): View? {
+ return null
+ }
+
+ override fun updateAppearance() {
+ super.updateAppearance()
+
+ viewBinding.actionButton.textAppearence(R.style.base_semi_bold)
+
+ viewBinding.actionButton.setTextColor(
+ ThemeUtils.getAttributedColor(
+ R.attr.brand_text_primary,
+ viewBinding.actionButton.context
+ )
+ )
+ viewBinding.actionButton.setBackgroundColor(
+ ThemeUtils.getAttributedColor(
+ R.attr.main_bg_surface_alt,
+ viewBinding.actionButton.context
+ )
+ )
+
+ viewBinding.actionButton.strokeWidth =
+ DeviceUtil.convertDpToPixel(viewBinding.actionButton.context, 1f)
+ viewBinding.actionButton.setStrokeColor(
+ ColorStateList.valueOf(
+ ThemeUtils.getAttributedColor(
+ R.attr.brand_bg_primary,
+ viewBinding.actionButton.context
+ )
+ )
+ )
+
+ }
+ // endregion
+
+}
diff --git a/app/src/main/java/com/bulletin/viewHolder/FormSectionBulletPointViewHolder.kt b/app/src/main/java/com/bulletin/viewHolder/FormSectionBulletPointViewHolder.kt
new file mode 100644
index 0000000..cedfaa5
--- /dev/null
+++ b/app/src/main/java/com/bulletin/viewHolder/FormSectionBulletPointViewHolder.kt
@@ -0,0 +1,128 @@
+package com.bulletin.viewHolder
+
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.view.View
+import androidx.core.text.HtmlCompat
+import com.bulletin.FormRecyclerViewAdapter
+import com.bulletin.extension.loadImageWithUrl
+import com.bulletin.extension.textAppearence
+import com.bulletin.models.Bullet
+import com.bulletin.models.BulletPoint
+import com.bulletin.utilities.ThemeUtils
+import com.bumptech.glide.request.target.CustomTarget
+import com.bumptech.glide.request.transition.Transition
+import com.example.bulletin.R
+import com.example.bulletin.databinding.LayoutFormBulletPointBinding
+
+
+class FormSectionBulletPointViewHolder(
+ val viewBinding: LayoutFormBulletPointBinding,
+ listener: FormRecyclerViewAdapter.OnItemClickListener
+) :
+ BaseViewHolder(viewBinding, listener) {
+
+ // region Methods
+ override fun bind(item: BulletPoint) {
+ super.bind(item)
+
+ viewBinding.bulletImageView.setVisibility(View.GONE)
+ viewBinding.bulletLabel.setVisibility(View.GONE)
+
+ when (item.bullet?.bulletType) {
+ Bullet.BulletType.UNICODE -> {
+
+ val unicode = item.bullet?.unicode
+ if (!unicode.isNullOrBlank()) {
+ val fromHtml = HtmlCompat.fromHtml(unicode, HtmlCompat.FROM_HTML_MODE_COMPACT)
+ viewBinding.bulletLabel.text = fromHtml
+ viewBinding.bulletLabel.setVisibility(View.VISIBLE)
+ } else {
+ viewBinding.bulletLabel.text = ""
+ viewBinding.bulletLabel.setVisibility(View.GONE)
+ }
+
+ }
+ Bullet.BulletType.IMAGE -> {
+ (item.bullet?.imageUrl)?.let {
+
+ viewBinding.bulletImageView.loadImageWithUrl(
+ viewBinding.bulletImageView.getContext(),
+ it,
+ null,
+ object : CustomTarget() {
+
+ override fun onResourceReady(
+ resource: Drawable,
+ transition: Transition?
+ ) {
+ val bitmap = (resource as BitmapDrawable).bitmap
+ viewBinding.bulletImageView.setImageBitmap(bitmap)
+ viewBinding.bulletLabel.setVisibility(View.GONE)
+ viewBinding.bulletImageView.setVisibility(View.VISIBLE)
+ }
+
+ override fun onLoadFailed(errorDrawable: Drawable?) {
+ print("onLoadFailed")
+ }
+
+ override fun onLoadCleared(placeholder: Drawable?) {
+ print("onLoadCleared")
+ }
+
+ })
+ }
+ }
+ else -> {
+
+ }
+ }
+
+ // Set Pre Title
+ if (!item.titleText.isNullOrBlank()) {
+ viewBinding.titleLabel.text = item.titleText
+ viewBinding.titleLabel.setVisibility(View.VISIBLE)
+ } else {
+ viewBinding.titleLabel.text = ""
+ viewBinding.titleLabel.setVisibility(View.GONE)
+ }
+
+ // Set Title
+ if (!item.subTitleText.isNullOrBlank()) {
+ viewBinding.subtitleLabel.text = item.subTitleText
+ viewBinding.subtitleLabel.setVisibility(View.VISIBLE)
+ } else {
+ viewBinding.subtitleLabel.text = ""
+ viewBinding.subtitleLabel.setVisibility(View.GONE)
+ }
+
+ }
+
+ override fun updateAppearance() {
+ super.updateAppearance()
+
+ // Set Default Properties
+ viewBinding.titleLabel.textAppearence(R.style.large_semi_bold)
+ viewBinding.subtitleLabel.textAppearence(R.style.base_regular)
+
+ viewBinding.titleLabel.setTextColor(
+ ThemeUtils.getAttributedColor(
+ R.attr.main_text_primary,
+ viewBinding.titleLabel.context
+ )
+ )
+ viewBinding.subtitleLabel.setTextColor(
+ ThemeUtils.getAttributedColor(
+ R.attr.main_text_secondary,
+ viewBinding.subtitleLabel.context
+ )
+ )
+
+ }
+
+ override fun getBackgroundView(): View? {
+ return null
+ }
+ // endregion
+
+}
diff --git a/app/src/main/java/com/bulletin/viewHolder/FormSectionMediaViewHolder.kt b/app/src/main/java/com/bulletin/viewHolder/FormSectionMediaViewHolder.kt
new file mode 100644
index 0000000..1afe615
--- /dev/null
+++ b/app/src/main/java/com/bulletin/viewHolder/FormSectionMediaViewHolder.kt
@@ -0,0 +1,72 @@
+package com.bulletin.viewHolder
+
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.view.View
+import com.bulletin.FormRecyclerViewAdapter
+import com.bulletin.extension.loadImageWithUrl
+import com.bulletin.utilities.DeviceUtil
+import com.bumptech.glide.request.target.CustomTarget
+import com.bumptech.glide.request.transition.Transition
+import com.example.bulletin.databinding.LayoutFormSectionMediaBinding
+import com.wrx.wazirx.views.bulletin.model.Media
+
+
+class FormSectionMediaViewHolder(
+ val viewBinding: LayoutFormSectionMediaBinding,
+ listener: FormRecyclerViewAdapter.OnItemClickListener
+) :
+ BaseViewHolder(viewBinding, listener) {
+
+ // region Methods
+ override fun bind(item: Media) {
+ super.bind(item)
+
+ // Set Image
+ viewBinding.bannerImageView.setVisibility(View.GONE)
+
+ item.size?.let {
+ viewBinding.bannerImageView.layoutParams.width = DeviceUtil.convertPixelsToDp(viewBinding.bannerImageView.context,it.width.toFloat()).toInt()
+ viewBinding.bannerImageView.layoutParams.height = DeviceUtil.convertPixelsToDp(viewBinding.bannerImageView.context,it.height.toFloat()).toInt()
+ }
+
+ (item.url)?.let {
+
+ viewBinding.bannerImageView.loadImageWithUrl(
+ viewBinding.bannerImageView.getContext(),
+ it,
+ null,
+ object : CustomTarget() {
+
+ override fun onResourceReady(
+ resource: Drawable,
+ transition: Transition?
+ ) {
+ val bitmap = (resource as BitmapDrawable).bitmap
+ viewBinding.bannerImageView.setImageBitmap(bitmap)
+ viewBinding.bannerImageView.setVisibility(View.VISIBLE)
+ }
+
+ override fun onLoadFailed(errorDrawable: Drawable?) {
+ print("onLoadFailed")
+ }
+
+ override fun onLoadCleared(placeholder: Drawable?) {
+ print("onLoadCleared")
+ }
+
+ })
+ }
+
+ }
+
+ override fun getBackgroundView(): View? {
+ return null
+ }
+
+ override fun updateAppearance() {
+ super.updateAppearance()
+ }
+ // endregion
+
+}
diff --git a/app/src/main/java/com/bulletin/viewHolder/FormSectionMessageViewHolder.kt b/app/src/main/java/com/bulletin/viewHolder/FormSectionMessageViewHolder.kt
new file mode 100644
index 0000000..321039a
--- /dev/null
+++ b/app/src/main/java/com/bulletin/viewHolder/FormSectionMessageViewHolder.kt
@@ -0,0 +1,74 @@
+package com.bulletin.viewHolder
+
+import android.view.View
+import androidx.core.text.HtmlCompat
+import com.bulletin.FormRecyclerViewAdapter
+import com.bulletin.extension.textAppearence
+import com.bulletin.models.Message
+import com.bulletin.utilities.ThemeUtils
+import com.example.bulletin.R
+import com.example.bulletin.databinding.LayoutFormSectionMessageBinding
+
+
+class FormSectionMessageViewHolder(
+ val viewBinding: LayoutFormSectionMessageBinding,
+ listener: FormRecyclerViewAdapter.OnItemClickListener
+) :
+ BaseViewHolder(viewBinding, listener) {
+
+ // region Methods
+ override fun bind(item: Message) {
+
+ super.bind(item)
+
+
+ when (item.messageType) {
+ Message.MessageType.HTML -> {
+ // Set Subttitle
+ if (!item.text.isNullOrBlank()) {
+ val fromHtml = HtmlCompat.fromHtml(item.text, HtmlCompat.FROM_HTML_MODE_COMPACT)
+ viewBinding.messageLabel.text = fromHtml
+ viewBinding.messageLabel.setVisibility(View.VISIBLE)
+ } else {
+ viewBinding.messageLabel.text = ""
+ viewBinding.messageLabel.setVisibility(View.GONE)
+ }
+ }
+ Message.MessageType.TEXT -> {
+ // Set Subttitle
+ if (!item.text.isNullOrBlank()) {
+ viewBinding.messageLabel.text = item.text
+ viewBinding.messageLabel.setVisibility(View.VISIBLE)
+ } else {
+ viewBinding.messageLabel.text = ""
+ viewBinding.messageLabel.setVisibility(View.GONE)
+ }
+ }
+ else -> {
+ viewBinding.messageLabel.text = ""
+ viewBinding.messageLabel.setVisibility(View.GONE)
+ }
+ }
+
+ }
+
+ override fun getBackgroundView(): View? {
+ return null
+ }
+
+ override fun updateAppearance() {
+ super.updateAppearance()
+
+ // Set Default Properties
+ viewBinding.messageLabel.textAppearence(R.style.base_regular)
+
+ viewBinding.messageLabel.setTextColor(
+ ThemeUtils.getAttributedColor(
+ R.attr.main_text_primary,
+ viewBinding.messageLabel.context
+ )
+ )
+ }
+ // endregion
+
+}
diff --git a/app/src/main/java/com/bulletin/viewHolder/FormSectionTitleViewHolder.kt b/app/src/main/java/com/bulletin/viewHolder/FormSectionTitleViewHolder.kt
new file mode 100644
index 0000000..0b43637
--- /dev/null
+++ b/app/src/main/java/com/bulletin/viewHolder/FormSectionTitleViewHolder.kt
@@ -0,0 +1,90 @@
+package com.bulletin.viewHolder
+
+import android.os.Build
+import android.view.View
+import com.bulletin.FormRecyclerViewAdapter
+import com.bulletin.extension.textAppearence
+import com.bulletin.models.Title
+import com.bulletin.utilities.ThemeUtils
+import com.example.bulletin.R
+import com.example.bulletin.databinding.LayoutFormSectionTitleBinding
+
+
+class FormSectionTitleViewHolder(
+ val viewBinding: LayoutFormSectionTitleBinding,
+ listener: FormRecyclerViewAdapter.OnItemClickListener
+) :
+ BaseViewHolder(viewBinding, listener) {
+
+ // region Methods
+ override fun bind(item: Title) {
+ super.bind(item)
+
+ itemView.setOnClickListener {
+
+ }
+
+ // Set App Title
+ if (!item.preTitleText.isNullOrBlank()) {
+ viewBinding.preTitleLabel.text = item.preTitleText
+ viewBinding.preTitleLabel.setVisibility(View.VISIBLE)
+ } else {
+ viewBinding.preTitleLabel.text = ""
+ viewBinding.preTitleLabel.setVisibility(View.GONE)
+ }
+
+ // Set App Version
+ if (!item.titleText.isNullOrBlank()) {
+ viewBinding.titleLabel.text = item.titleText
+ viewBinding.titleLabel.setVisibility(View.VISIBLE)
+ } else {
+ viewBinding.titleLabel.text = ""
+ viewBinding.titleLabel.setVisibility(View.GONE)
+ }
+
+ // Set App Version
+ if (!item.subTitleText.isNullOrBlank()) {
+ viewBinding.subtitleLabel.text = item.subTitleText
+ viewBinding.subtitleLabel.setVisibility(View.VISIBLE)
+ } else {
+ viewBinding.subtitleLabel.text = ""
+ viewBinding.subtitleLabel.setVisibility(View.GONE)
+ }
+
+ }
+
+ override fun getBackgroundView(): View? {
+ return null
+ }
+
+ override fun updateAppearance() {
+ super.updateAppearance()
+
+ // Set Default Properties
+ viewBinding.preTitleLabel.textAppearence(R.style.small_medium)
+ viewBinding.titleLabel.textAppearence(R.style.heading_4_semi_bold)
+ viewBinding.subtitleLabel.textAppearence(R.style.base_regular)
+
+ viewBinding.preTitleLabel.setTextColor(
+ ThemeUtils.getAttributedColor(
+ R.attr.success_text_primary,
+ viewBinding.preTitleLabel.context
+ )
+ )
+ viewBinding.titleLabel.setTextColor(
+ ThemeUtils.getAttributedColor(
+ R.attr.main_text_primary,
+ viewBinding.titleLabel.context
+ )
+ )
+ viewBinding.subtitleLabel.setTextColor(
+ ThemeUtils.getAttributedColor(
+ R.attr.main_text_primary,
+ viewBinding.subtitleLabel.context
+ )
+ )
+
+ }
+ // endregion
+
+}
diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/header_background.xml b/app/src/main/res/drawable/header_background.xml
new file mode 100644
index 0000000..afbb247
--- /dev/null
+++ b/app/src/main/res/drawable/header_background.xml
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/image_loading_bg.xml b/app/src/main/res/drawable/image_loading_bg.xml
new file mode 100644
index 0000000..0d0ca2b
--- /dev/null
+++ b/app/src/main/res/drawable/image_loading_bg.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/view_background.xml b/app/src/main/res/drawable/view_background.xml
new file mode 100644
index 0000000..783b849
--- /dev/null
+++ b/app/src/main/res/drawable/view_background.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/app/src/main/res/font/ibm_plex_sans_bold.ttf b/app/src/main/res/font/ibm_plex_sans_bold.ttf
new file mode 100644
index 0000000..e5389d8
Binary files /dev/null and b/app/src/main/res/font/ibm_plex_sans_bold.ttf differ
diff --git a/app/src/main/res/font/ibm_plex_sans_medium.ttf b/app/src/main/res/font/ibm_plex_sans_medium.ttf
new file mode 100644
index 0000000..9395402
Binary files /dev/null and b/app/src/main/res/font/ibm_plex_sans_medium.ttf differ
diff --git a/app/src/main/res/font/ibm_plex_sans_regular.ttf b/app/src/main/res/font/ibm_plex_sans_regular.ttf
new file mode 100644
index 0000000..b581964
Binary files /dev/null and b/app/src/main/res/font/ibm_plex_sans_regular.ttf differ
diff --git a/app/src/main/res/font/ibm_plex_sans_semi_bold.ttf b/app/src/main/res/font/ibm_plex_sans_semi_bold.ttf
new file mode 100644
index 0000000..a5bd9ee
Binary files /dev/null and b/app/src/main/res/font/ibm_plex_sans_semi_bold.ttf differ
diff --git a/app/src/main/res/layout/bulletin_dialog.xml b/app/src/main/res/layout/bulletin_dialog.xml
new file mode 100644
index 0000000..040f3e2
--- /dev/null
+++ b/app/src/main/res/layout/bulletin_dialog.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_form_bullet_point.xml b/app/src/main/res/layout/layout_form_bullet_point.xml
new file mode 100644
index 0000000..1d9d159
--- /dev/null
+++ b/app/src/main/res/layout/layout_form_bullet_point.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_form_section_action_button.xml b/app/src/main/res/layout/layout_form_section_action_button.xml
new file mode 100644
index 0000000..c98c027
--- /dev/null
+++ b/app/src/main/res/layout/layout_form_section_action_button.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_form_section_media.xml b/app/src/main/res/layout/layout_form_section_media.xml
new file mode 100644
index 0000000..e24d801
--- /dev/null
+++ b/app/src/main/res/layout/layout_form_section_media.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_form_section_message.xml b/app/src/main/res/layout/layout_form_section_message.xml
new file mode 100644
index 0000000..c2cc8ed
--- /dev/null
+++ b/app/src/main/res/layout/layout_form_section_message.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_form_section_title.xml b/app/src/main/res/layout/layout_form_section_title.xml
new file mode 100644
index 0000000..3794581
--- /dev/null
+++ b/app/src/main/res/layout/layout_form_section_title.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..c209e78
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b2dfe3d
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..62b611d
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1b9a695
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9126ae3
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..86c04a6
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,83 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
+ #00000000
+
+ #18222C
+
+ ------------------ New Color ------------------------
+ #000000
+ #ffffff
+
+ #FBFCFE
+ #F2F2F8
+ #D9E1EB
+ #A0AAB5
+ #6F7F90
+ #4C5C70
+ #2E3E54
+ #232F3F
+ #212C3A
+ #1A212B
+
+ #FFEFEF
+ #fed7d7
+ #feb2b2
+ #fc8181
+ #f56565
+ #e53e3e
+ #c53030
+ #9b2c2c
+ #742a2a
+ #4B2830
+
+ #fffff0
+ #fefcbf
+ #faf089
+ #f6e05e
+ #ecc94b
+ #d69e2e
+ #b7791f
+ #975a16
+ #744210
+ #4F3502
+
+ #E0FAEE
+ #ABEDCF
+ #7BDBB0
+ #56C292
+ #3BA073
+ #287D57
+ #1D5E41
+ #16422E
+ #123023
+
+ #E8EEFD
+ #B6C9FA
+ #96B2F7
+ #2DA4FE
+ #167CF9
+ #3067F0
+ #1746BD
+ #2A5573
+ #19364A
+
+ #FDEFE9
+ #FAE0D4
+ #F6C4AD
+ #F1A07B
+ #EE895B
+ #E86427
+ #BF5728
+ #964929
+ #713D29
+ #4B312A
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..3d45ab9
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,8 @@
+
+ Bulletin
+
+ OKay, Go it
+ What’s new in this update?
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..0476db3
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,283 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/theme_attrs.xml b/app/src/main/res/values/theme_attrs.xml
new file mode 100644
index 0000000..f24f314
--- /dev/null
+++ b/app/src/main/res/values/theme_attrs.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..9ece324
--- /dev/null
+++ b/app/src/main/res/values/themes.xml
@@ -0,0 +1,178 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000..fa0f996
--- /dev/null
+++ b/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000..9ee9997
--- /dev/null
+++ b/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/test/java/com/example/bulletin/ExampleUnitTest.kt b/app/src/test/java/com/example/bulletin/ExampleUnitTest.kt
new file mode 100644
index 0000000..c4f07b7
--- /dev/null
+++ b/app/src/test/java/com/example/bulletin/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.example.bulletin
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..eaf7c0c
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,10 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ id 'com.android.application' version '7.2.2' apply false
+ id 'com.android.library' version '7.2.2' apply false
+ id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..cd0519b
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,23 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..150f49e
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Sep 20 16:28:33 IST 2022
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME