Skip to content

Commit

Permalink
Add gsr reservation widget files
Browse files Browse the repository at this point in the history
  • Loading branch information
Divak2004 committed Dec 8, 2024
1 parent bfa19f6 commit 3476704
Show file tree
Hide file tree
Showing 10 changed files with 392 additions and 0 deletions.
14 changes: 14 additions & 0 deletions PennMobile/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
android:theme="@style/AppTheme.Launcher"
android:usesCleartextTraffic="true"
tools:targetApi="m">

<receiver
android:name="com.pennapps.labs.pennmobile.dining.widget.DiningHallWidget"
android:exported="false">
Expand All @@ -39,6 +40,19 @@
android:resource="@xml/dining_hall_widget_info" />
</receiver>

<receiver
android:name="com.pennapps.labs.pennmobile.gsr.widget.GsrReservationsWidget"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.ACTION_CLICK" />
</intent-filter>

<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/gsr_reservations_widget_info" />
</receiver>

<activity
android:name=".MainActivity"
android:exported="true"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.pennapps.labs.pennmobile.api;

import com.pennapps.labs.pennmobile.gsr.classes.GSRReservation;

import java.util.List;

import retrofit.http.GET;
import retrofit.http.Header;
import rx.Observable;

public interface GsrReservationsRequest {
@GET("/gsr/reservations")
Observable<List<GSRReservation>> getGsrReservations(
@Header("Authorization") String bearerToken
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package com.pennapps.labs.pennmobile.gsr.widget

import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.widget.RemoteViews
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import com.pennapps.labs.pennmobile.MainActivity
import com.pennapps.labs.pennmobile.R
import com.pennapps.labs.pennmobile.api.GsrReservationsRequest
import com.pennapps.labs.pennmobile.api.Serializer
import com.pennapps.labs.pennmobile.gsr.classes.GSRReservation
import com.squareup.okhttp.OkHttpClient
import retrofit.RestAdapter
import retrofit.client.OkClient
import retrofit.converter.GsonConverter
import java.util.concurrent.TimeUnit

class GsrReservationsWidget : AppWidgetProvider() {
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray,
) {
for (appWidgetId in appWidgetIds) {
val mainActivityIntent = Intent(context, MainActivity::class.java)
mainActivityIntent.apply {
flags += Intent.FLAG_ACTIVITY_NEW_TASK
flags += Intent.FLAG_ACTIVITY_CLEAR_TOP
}

mainActivityIntent.putExtra("Gsr_Tab_Switch", 1)

val serviceIntent = Intent(context, GsrReservationsWidgetAdapter::class.java)
val pendingIntent: PendingIntent =
PendingIntent.getActivity(
context,
appWidgetId,
mainActivityIntent,
PendingIntent.FLAG_IMMUTABLE,
)
serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)

// setData allows the system to distinguish between different service intents. Without
// setData, onGetViewFactory is called only once for multiple widgets and send
// the same intent to all of them.
serviceIntent.setData(Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME)))

// Setting up the widget remoteViews; change cardview to something else
val views = RemoteViews(context.packageName, R.layout.gsr_reservations_widget)
views.setRemoteAdapter(R.id.gsr_reservation_widget_stack_view, serviceIntent)
views.setEmptyView(
R.id.gsr_reservation_widget_stack_view,
R.id.gsr_reservation_widget_empty_view,
)

// Setting up the intents for the remoteview for both when it is empty and
// when it loads the collection view (in this case we use setPendingIntentTemplate to
// the entire stackView that contains all the items).
views.setPendingIntentTemplate(R.id.gsr_reservation_widget_stack_view, pendingIntent)
views.setOnClickPendingIntent(R.id.gsr_reservation_widget_empty_view, pendingIntent)

// Notify appwidgetviewdata has changed to call getViewAt to set up the widget UI
// and handle update for every appwidget item in the Collection widget.
appWidgetManager.notifyAppWidgetViewDataChanged(
appWidgetId,
R.id.gsr_reservation_widget_stack_view,
)
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}

// onEnabled and onDisabled are typically used for alarmManager testing and logs to check whether
// appwidget is properly enabled/disabled.
override fun onEnabled(context: Context) {
}

override fun onDisabled(context: Context) {
}

companion object {
private var mGSRReservationsRequest: GsrReservationsRequest? = null

@JvmStatic
val gsrReservationsRequestInstance: GsrReservationsRequest
get() {
if (mGSRReservationsRequest == null) {
val gsonBuilder = GsonBuilder()

// RegisterTypeAdapter with GsrReservationSerializer
// since we are only accessing gsr reservations
gsonBuilder.registerTypeAdapter(
object : TypeToken<MutableList<GSRReservation?>?>() {}.type,
Serializer.GsrReservationSerializer(),
)

val gson = gsonBuilder.create()
val okHttpClient = OkHttpClient()
okHttpClient.setConnectTimeout(35, TimeUnit.SECONDS) // Connection timeout
okHttpClient.setReadTimeout(35, TimeUnit.SECONDS) // Read timeout
okHttpClient.setWriteTimeout(35, TimeUnit.SECONDS) // Write timeout

val restAdapter =
RestAdapter
.Builder()
.setConverter(GsonConverter(gson))
.setClient(OkClient(okHttpClient))
.setEndpoint("https://pennmobile.org/api")
.build()
mGSRReservationsRequest = restAdapter.create(GsrReservationsRequest::class.java)
}
return mGSRReservationsRequest!!
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package com.pennapps.labs.pennmobile.gsr.widget

import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import android.os.Bundle
import android.widget.RemoteViews
import android.widget.RemoteViewsService
import androidx.preference.PreferenceManager
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.pennapps.labs.pennmobile.R
import com.pennapps.labs.pennmobile.api.GsrReservationsRequest
import com.pennapps.labs.pennmobile.gsr.classes.GSRReservation
import org.joda.time.format.DateTimeFormat
import org.joda.time.format.DateTimeFormatter
import rx.Observable
import java.net.HttpURLConnection
import java.net.URL

class GsrReservationsWidgetAdapter : RemoteViewsService() {
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory = GsrReservationWidgetFactory(applicationContext, intent)

class GsrReservationWidgetFactory(
private val context: Context,
intent: Intent,
) : RemoteViewsFactory {
private var mGsrReservationsRequest: GsrReservationsRequest? = null
private var appWidgetId: Int =
intent.getIntExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID,
)
private var dataSet: List<GSRReservation> = emptyList()
private var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)

override fun onCreate() {
mGsrReservationsRequest = GsrReservationsWidget.gsrReservationsRequestInstance
getWidgetGsrReservations()
}

// Not used since already handled
override fun onDataSetChanged() {
}

override fun onDestroy() {
}

override fun getCount(): Int = dataSet.size

// TODO("Get building name(?), and hopefully support click behavior")
override fun getViewAt(index: Int): RemoteViews {
val reservation = dataSet[index]
val roomName = reservation.name

val formatter: DateTimeFormatter =
DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssZ")
val from = formatter.parseDateTime(reservation.fromDate)
val to = formatter.parseDateTime(reservation.toDate)
val day = from.toString("EEEE, MMMM d")
val fromHour = from.toString("h:mm a")
val toHour = to.toString("h:mm a")

val imageUrl =
reservation.info?.get("thumbnail")
?: "https://s3.us-east-2.amazonaws.com/labs.api/dining/MBA+Cafe.jpg"

val views = RemoteViews(context.packageName, R.layout.gsr_reservations_widget_item)
views.setTextViewText(R.id.gsr_reservation_widget_item_location_tv, roomName)
views.setTextViewText(
R.id.gsr_reservation_widget_item_time_tv,
"$day\n$fromHour-$toHour",
)

try {
val urlConnection = URL(imageUrl)
val connection =
urlConnection
.openConnection() as HttpURLConnection
connection.doInput = true
connection.connect()
val input = connection.inputStream
val myBitmap = BitmapFactory.decodeStream(input)
views.setImageViewBitmap(R.id.gsr_reservation_widget_item_iv, myBitmap)
} catch (e: Exception) {
e.printStackTrace()
}

val extras = Bundle()
val fillInIntent = Intent()
fillInIntent.putExtras(extras)
views.setOnClickFillInIntent(R.id.gsr_reservation_widget_item_root, fillInIntent)

return views
}

override fun getLoadingView(): RemoteViews? = null

override fun getViewTypeCount(): Int = 1

override fun getItemId(id: Int): Long = id.toLong()

override fun hasStableIds(): Boolean = true

private fun getWidgetGsrReservations() {
try {
if (mGsrReservationsRequest != null) {
val token =
sharedPreferences.getString(
context.getString(R.string.access_token),
"",
)
mGsrReservationsRequest!!
.getGsrReservations("Bearer $token")
.flatMap { reservations -> Observable.from(reservations) }
.flatMap { reservation ->
Observable.just(reservation)
}.toList()
.subscribe { reservations ->
dataSet = reservations
println("subscribed")
val appWidgetManager: AppWidgetManager =
AppWidgetManager.getInstance(context)
appWidgetManager.notifyAppWidgetViewDataChanged(
appWidgetId,
R.id.gsr_reservation_widget_stack_view,
)
}
}
} catch (e: Exception) {
FirebaseCrashlytics.getInstance().recordException(e)
e.printStackTrace()
}
}
}
}
4 changes: 4 additions & 0 deletions PennMobile/src/main/res/layout/fragment_dining_holder.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
android:maxLines="1"
/>

<include
layout="@layout/fragment_menu_tab"
/>

<ImageView
android:id="@+id/divider"
android:layout_width="match_parent"
Expand Down
33 changes: 33 additions & 0 deletions PennMobile/src/main/res/layout/gsr_reservations_widget.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:theme="@style/Theme.Pennmobileandroid.AppWidgetContainer"
android:id="@+id/gsr_reservation_widget_root" >
<FrameLayout
android:id="@+id/widgetframe"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/Theme.Pennmobileandroid.AppWidgetContainer"
>
<ListView
android:id="@+id/gsr_reservation_widget_stack_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:loopViews="true"
android:divider="@android:color/transparent"
android:dividerHeight="8dp"
/>

<ImageView
android:contentDescription="Penn Logo"
android:id="@+id/gsr_reservation_widget_empty_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="@drawable/app_widget_background"
android:scaleType="centerCrop"
android:src="@drawable/splash_screen435"
/>
</FrameLayout>
</LinearLayout>
Loading

0 comments on commit 3476704

Please sign in to comment.