Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gsr reservation widget with linting pls work #634

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
16 changes: 16 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.GsrReservationWidget"
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_reservation_widget_info" />
</receiver>

<activity
android:name=".MainActivity"
android:exported="true"
Expand All @@ -55,6 +69,8 @@
</activity>
<service android:name=".dining.widget.DiningHallWidgetAdapter"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<service android:name=".gsr.widget.GsrReservationWidgetAdapter"
android:permission="android.permission.BIND_REMOTEVIEWS" />

<meta-data
android:name="com.google.android.gms.version"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,15 @@ class MainActivity : AppCompatActivity() {
if (diningWidgetBroadCast != -1) {
setTab(DINING_ID)
}

var gsrReservationWidgetBroadCast = 0
if (intent != null) {
gsrReservationWidgetBroadCast =
intent.getIntExtra("Gsr_Tab_Switch", -1)
}
if (gsrReservationWidgetBroadCast != -1) {
setTab(GSR_ID)
}
}

private fun onExpandableBottomNavigationItemSelected() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@
public interface DiningRequest {
@GET("/dining/venues")
Observable<List<Venue>> venues();
}
}
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 GsrReservationWidget : 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, GsrReservationWidgetAdapter::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_reservation_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 GsrReservationWidgetAdapter : 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 = GsrReservationWidget.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_reservation_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()
}
}
}
}
33 changes: 33 additions & 0 deletions PennMobile/src/main/res/layout/gsr_reservation_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
Loading