diff options
| -rw-r--r-- | android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NotificationBanner.kt | 108 | ||||
| -rw-r--r-- | android/src/main/res/layout/notification_banner.xml | 50 |
2 files changed, 158 insertions, 0 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NotificationBanner.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NotificationBanner.kt new file mode 100644 index 0000000000..73bebc2955 --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NotificationBanner.kt @@ -0,0 +1,108 @@ +package net.mullvad.mullvadvpn.ui.widget + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.widget.FrameLayout +import android.widget.ImageView +import android.widget.TextView +import net.mullvad.mullvadvpn.R +import net.mullvad.mullvadvpn.ui.notification.InAppNotification +import net.mullvad.mullvadvpn.ui.notification.InAppNotificationController +import net.mullvad.mullvadvpn.ui.notification.StatusLevel + +class NotificationBanner : FrameLayout { + private val container = + context.getSystemService(Context.LAYOUT_INFLATER_SERVICE).let { service -> + val inflater = service as LayoutInflater + + inflater.inflate(R.layout.notification_banner, this) + } + + private val errorImage = resources.getDrawable(R.drawable.icon_notification_error, null) + private val warningImage = resources.getDrawable(R.drawable.icon_notification_warning, null) + + private val status: ImageView = container.findViewById(R.id.notification_status) + private val title: TextView = container.findViewById(R.id.notification_title) + private val message: TextView = container.findViewById(R.id.notification_message) + private val icon: View = container.findViewById(R.id.notification_icon) + + val notifications = InAppNotificationController { notification -> + if (notification != null) { + update(notification) + } + + animateChange() + } + + constructor(context: Context) : super(context) {} + constructor(context: Context, attributes: AttributeSet) : super(context, attributes) {} + + constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : + super(context, attributes, defaultStyleAttribute) {} + + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int, + defaultStyleResource: Int + ) : super(context, attributes, defaultStyleAttribute, defaultStyleResource) {} + + init { + setBackgroundResource(R.color.darkBlue) + } + + fun onResume() { + notifications.onResume() + } + + fun onPause() { + notifications.onPause() + } + + fun onDestroy() { + notifications.onDestroy() + } + + private fun update(notification: InAppNotification) { + val notificationMessage = notification.message + val clickAction = notification.onClick + + when (notification.status) { + StatusLevel.Error -> status.setImageDrawable(errorImage) + StatusLevel.Warning -> status.setImageDrawable(warningImage) + } + + title.text = notification.title + + if (notificationMessage != null) { + message.text = notificationMessage + message.visibility = View.VISIBLE + } else { + message.visibility = View.GONE + } + + if (notification.showIcon) { + icon.visibility = View.VISIBLE + } else { + icon.visibility = View.GONE + } + + setClickable(clickAction != null) + } + + private fun animateChange() { + val shouldShow = notifications.current != null + + if (shouldShow && visibility == View.INVISIBLE) { + visibility = View.VISIBLE + translationY = -height.toFloat() + animate().translationY(0.0F).setDuration(350).start() + } else if (!shouldShow && visibility == View.VISIBLE) { + animate().translationY(-height.toFloat()).setDuration(350).withEndAction { + visibility = View.INVISIBLE + } + } + } +} diff --git a/android/src/main/res/layout/notification_banner.xml b/android/src/main/res/layout/notification_banner.xml new file mode 100644 index 0000000000..82d3792f07 --- /dev/null +++ b/android/src/main/res/layout/notification_banner.xml @@ -0,0 +1,50 @@ +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingVertical="8dp" + android:paddingLeft="20dp" + android:paddingRight="10dp" + android:background="?android:attr/selectableItemBackground"> + <RelativeLayout android:id="@+id/notification_status_container" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + android:layout_alignBottom="@id/notification_title"> + <ImageView android:id="@+id/notification_status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:src="@drawable/icon_notification_error" /> + </RelativeLayout> + <TextView android:id="@+id/notification_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_toLeftOf="@id/notification_icon" + android:layout_toRightOf="@id/notification_status_container" + android:layout_marginLeft="7dp" + android:textSize="13sp" + android:textStyle="bold" + android:text="@string/blocking_internet" + android:textAllCaps="true" /> + <TextView android:id="@+id/notification_message" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignWithParentIfMissing="true" + android:layout_toLeftOf="@id/notification_icon" + android:layout_alignLeft="@id/notification_title" + android:layout_below="@id/notification_title" + android:textSize="13sp" + android:textColor="@color/white60" + android:text="" + android:visibility="gone" /> + <ImageView android:id="@+id/notification_icon" + android:layout_width="12dp" + android:layout_height="12dp" + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:alpha="0.6" + android:src="@drawable/icon_extlink" + android:visibility="gone" /> +</RelativeLayout> |
