Author: Adam Pioterek <adam.pioterek@protonmail.ch>
suggestions from VM without offline timetable
app/build.gradle | 2 app/src/main/AndroidManifest.xml | 2 app/src/main/java/ml/adamsprogs/bimba/ProviderProxy.kt | 57 app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt | 65 app/src/main/java/ml/adamsprogs/bimba/activities/SplashActivity.kt | 10 app/src/main/java/ml/adamsprogs/bimba/datasources/VmStopsClient.kt | 95 app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt | 47 app/src/main/res/drawable/icon_dev.xml | 17
diff --git a/app/build.gradle b/app/build.gradle index ceec2719de9cdc6a9df162cb04300385ca2912b3..d20a839f1360318e2dce6b81ef8d8c2fdb0a44aa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,7 +6,7 @@ android { compileSdkVersion 27 buildToolsVersion "28.0.1" defaultConfig { - applicationId "ml.adamsprogs.bimba" + applicationId "ml.adamsprogs.bimba.scheduleless" minSdkVersion 19 targetSdkVersion 27 versionCode 14 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5f4681fca49b83a45a0610ee2e176da39900d4ec..40d8d758b7391a5f9ee273336c2185a987c9c309 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,7 +8,7 @@<application android:allowBackup="true" - android:icon="@mipmap/ic_launcher" + android:icon="@drawable/icon_dev" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> diff --git a/app/src/main/java/ml/adamsprogs/bimba/ProviderProxy.kt b/app/src/main/java/ml/adamsprogs/bimba/ProviderProxy.kt new file mode 100644 index 0000000000000000000000000000000000000000..9f8e1300ab3a7d260f9feaf34af0d674e668ed85 --- /dev/null +++ b/app/src/main/java/ml/adamsprogs/bimba/ProviderProxy.kt @@ -0,0 +1,57 @@ +package ml.adamsprogs.bimba + +import android.content.Context +import kotlinx.coroutines.experimental.android.UI +import kotlinx.coroutines.experimental.* +import ml.adamsprogs.bimba.datasources.VmStopsClient +import ml.adamsprogs.bimba.models.Timetable +import ml.adamsprogs.bimba.models.suggestions.* + +class ProviderProxy(context: Context) { + private val vmStopsClient = VmStopsClient.getVmStopClient() + private val timetable: Timetable = Timetable.getTimetable(context) + private var suggestions = emptyList<GtfsSuggestion>() + + fun getSuggestions(query: String = "", callback: (List<GtfsSuggestion>) -> Unit) { + launch(UI) { + suggestions = withContext(DefaultDispatcher) { + getStopSuggestions(query) //+ getLineSuggestions(query) //todo<p:v+1> + bike stations, train stations, &c + } + callback(filterSuggestions(query)) + } + } + + private suspend fun getStopSuggestions(query: String): List<StopSuggestion> { + val vmSuggestions = withContext(DefaultDispatcher) { + vmStopsClient.getStops(query) + } + + return if (vmSuggestions.isEmpty()) { + if (timetable.isEmpty()) + emptyList() + else + timetable.getStopSuggestions() + } else { + vmSuggestions + } + } + + private fun filterSuggestions(query: String): List<GtfsSuggestion> { + return suggestions.filter { + deAccent(it.name).contains(deAccent(query), true) + } + } + + private fun deAccent(str: String): String { + var result = str.replace('ę', 'e', true) + result = result.replace('ó', 'o', true) + result = result.replace('ą', 'a', true) + result = result.replace('ś', 's', true) + result = result.replace('ł', 'l', true) + result = result.replace('ż', 'z', true) + result = result.replace('ź', 'z', true) + result = result.replace('ć', 'c', true) + result = result.replace('ń', 'n', true) + return result + } +} \ No newline at end of file diff --git a/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt b/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt index b3389a44b0e630c67900e4fd810e80964521eae8..e66054a84ced3c7d34d383ff10b4e251925955cc 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt @@ -3,8 +3,8 @@ import android.annotation.SuppressLint import android.app.Activity import android.content.* -import android.database.sqlite.SQLiteException import android.os.* +import android.preference.PreferenceManager.getDefaultSharedPreferences import android.support.design.widget.* import android.support.v4.widget.* import android.support.v7.widget.* @@ -12,7 +12,6 @@ import android.support.v7.app.* import android.text.Html import android.view.* import android.view.inputmethod.InputMethodManager -import kotlin.concurrent.thread import kotlin.collections.ArrayList import kotlinx.android.synthetic.main.activity_dash.* import java.util.* @@ -34,7 +33,7 @@ FavouritesAdapter.OnMenuItemClickListener, Favourite.OnVmPreparedListener, FavouritesAdapter.ViewHolder.OnClickListener { val context: Context = this private val receiver = MessageReceiver.getMessageReceiver() - var timetable: Timetable? = null + private lateinit var timetable: Timetable private var suggestions: List<GtfsSuggestion>? = null private lateinit var drawerLayout: DrawerLayout private lateinit var drawerView: NavigationView @@ -45,6 +44,7 @@ private lateinit var adapter: FavouritesAdapter private val actionModeCallback = ActionModeCallback() private var actionMode: ActionMode? = null private var isWarned = false + private lateinit var providerProxy: ProviderProxy companion object { const val REQUEST_EDIT_FAVOURITE = 1 @@ -56,11 +56,8 @@ setContentView(R.layout.activity_dash) setSupportActionBar(toolbar) - timetable = try { - Timetable.getTimetable(this) - } catch (e: SQLiteException) { - null - } + providerProxy = ProviderProxy(this) + timetable = Timetable.getTimetable() getSuggestions() @@ -99,7 +96,9 @@ searchView.setOnFocusChangeListener(object : FloatingSearchView.OnFocusChangeListener { override fun onFocus() { favouritesList.visibility = View.GONE - filterSuggestions(searchView.query) + providerProxy.getSuggestions(searchView.query) { + searchView.swapSuggestions(it) + } } override fun onFocusCleared() { @@ -110,7 +109,9 @@ searchView.setOnQueryChangeListener { oldQuery, newQuery -> if (oldQuery != "" && newQuery == "") searchView.clearSuggestions() - filterSuggestions(newQuery) + providerProxy.getSuggestions(newQuery) { + searchView.swapSuggestions(it) + } } searchView.setOnSearchListener(object : FloatingSearchView.OnSearchListener { @@ -152,40 +153,33 @@ searchView.attachNavigationDrawerToMenuButton(drawer_layout as DrawerLayout) } private fun showValidityInDrawer() { - if (timetable == null) { + if (timetable.isEmpty()) { drawerView.menu.findItem(R.id.drawer_validity_since).title = getString(R.string.validity_offline_unavailable) } else { val formatter = DateFormat.getDateInstance(DateFormat.SHORT) - var calendar = calendarFromIsoD(timetable!!.getValidSince()) + var calendar = calendarFromIsoD(timetable.getValidSince()) formatter.timeZone = calendar.timeZone drawerView.menu.findItem(R.id.drawer_validity_since).title = getString(R.string.valid_since, formatter.format(calendar.time)) - calendar = calendarFromIsoD(timetable!!.getValidTill()) + calendar = calendarFromIsoD(timetable.getValidTill()) formatter.timeZone = calendar.timeZone drawerView.menu.findItem(R.id.drawer_validity_till).title = getString(R.string.valid_till, formatter.format(calendar.time)) } } - private fun filterSuggestions(newQuery: String) { - thread { - val newStops = suggestions!!.filter { deAccent(it.name).contains(deAccent(newQuery), true) } //todo<p:2> sorted by similarity - runOnUiThread { searchView.swapSuggestions(newStops) } - } - } - private fun warnTimetableValidity() { if (isWarned) return isWarned = true - if (timetable == null) + if (timetable.isEmpty()) return - val validTill = timetable!!.getValidTill() + val validTill = timetable.getValidTill() val today = Calendar.getInstance().toIsoDate() val tomorrow = Calendar.getInstance().apply { this.add(Calendar.DAY_OF_MONTH, 1) }.toIsoDate() try { - timetable!!.getServiceForToday() + timetable.getServiceForToday() if (today > validTill) { notifyTimetableValidity(-1) suggestions = ArrayList() @@ -202,7 +196,7 @@ return } try { - timetable!!.getServiceForTomorrow() + timetable.getServiceForTomorrow() if (tomorrow == validTill) { notifyTimetableValidity(1) return @@ -246,10 +240,9 @@ favouritesList.adapter.notifyDataSetChanged() } private fun getSuggestions() { - suggestions = if (timetable != null) - (timetable!!.getStopSuggestions(context)).sorted() //+ timetable.getLineSuggestions()).sorted() //todo<p:v+1> + bike stations, train stations, &c - else - emptyList() + providerProxy.getSuggestions { + searchView.swapSuggestions(it) + } } private fun prepareListeners() { @@ -262,7 +255,8 @@ favourites.registerOnVm(receiver, context) } private fun startDownloaderService() { - startService(Intent(context, TimetableDownloader::class.java)) + if (getDefaultSharedPreferences(this).getBoolean("automatic timetable updates", false)) + startService(Intent(context, TimetableDownloader::class.java)) } override fun onBackPressed() { @@ -286,19 +280,6 @@ super.onDestroy() receiver.removeOnTimetableDownloadListener(context as MessageReceiver.OnTimetableDownloadListener) favourites.deregisterOnVm(receiver, context) unregisterReceiver(receiver) - } - - private fun deAccent(str: String): String { - var result = str.replace('ę', 'e', true) - result = result.replace('ó', 'o', true) - result = result.replace('ą', 'a', true) - result = result.replace('ś', 's', true) - result = result.replace('ł', 'l', true) - result = result.replace('ż', 'z', true) - result = result.replace('ź', 'z', true) - result = result.replace('ć', 'c', true) - result = result.replace('ń', 'n', true) - return result } override fun onTimetableDownload(result: String?) { diff --git a/app/src/main/java/ml/adamsprogs/bimba/activities/SplashActivity.kt b/app/src/main/java/ml/adamsprogs/bimba/activities/SplashActivity.kt index 5a2d486aa09d33eae44c5d3d1686830a8adcbb2d..e7c3e0085a8ad06f180703cd533e0848579b9752 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/activities/SplashActivity.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/activities/SplashActivity.kt @@ -14,15 +14,7 @@ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO) - try { - val timetable = Timetable.getTimetable(this) - if (timetable.isEmpty()) - startActivity(Intent(this, NoDbActivity::class.java)) - else - startActivity(Intent(this, DashActivity::class.java)) - } catch (e:Exception) { - startActivity(Intent(this, NoDbActivity::class.java)) - } + startActivity(Intent(this, DashActivity::class.java)) finish() } } diff --git a/app/src/main/java/ml/adamsprogs/bimba/datasources/VmStopsClient.kt b/app/src/main/java/ml/adamsprogs/bimba/datasources/VmStopsClient.kt new file mode 100644 index 0000000000000000000000000000000000000000..a2d47d4e31c9d91aada6130f5b58a2dbcb0303b1 --- /dev/null +++ b/app/src/main/java/ml/adamsprogs/bimba/datasources/VmStopsClient.kt @@ -0,0 +1,95 @@ +package ml.adamsprogs.bimba.datasources + +import com.google.gson.* +import kotlinx.coroutines.experimental.* +import ml.adamsprogs.bimba.models.Plate +import ml.adamsprogs.bimba.models.gtfs.AgencyAndId +import ml.adamsprogs.bimba.models.suggestions.* +import okhttp3.* +import java.io.IOException +import java.util.* +import kotlin.collections.HashMap +import kotlin.collections.HashSet + +class VmStopsClient { + companion object { + private var vmStopsClient: VmStopsClient? = null + + fun getVmStopClient(): VmStopsClient { + if (vmStopsClient == null) + vmStopsClient = VmStopsClient() + return vmStopsClient!! + } + } + + /*suspend fun getBollardsByStopPoint(name: String): Map<String, Set<String>> { + val response = makeRequest("getBollardsByStopPoint", """{"name": "$name"}""") + println("asked for $name and got $response") + val rootObject = response["success"].asJsonObject["bollards"].asJsonArray + val result = HashMap<String, Set<String>>() + rootObject.forEach { + val code = it.asJsonObject["bollard"].asJsonObject["tag"].asString + result[code] = it.asJsonObject["directions"].asJsonArray.map { + """${it.asJsonObject["lineName"].asString} → ${it.asJsonObject["direction"].asString}""" + }.toSet() + } + return result + } + + suspend fun getPlatesByStopPoint(code: String): Set<Plate.ID>? { + val getTimesResponse = makeRequest("getTimes", """{"symbol": "$code"}""") + val name = getTimesResponse["success"].asJsonObject["bollard"].asJsonObject["name"].asString + + val bollards = getBollardsByStopPoint(name) + return bollards.filter { + it.key == code + }.values.flatMap { + it.map { + val (line, headsign) = it.split(" → ") + Plate.ID(AgencyAndId(line), AgencyAndId(code), headsign) + } + }.toSet() + }*/ + + suspend fun getStops(pattern: String): List<StopSuggestion> { + val response = withContext(DefaultDispatcher) { + makeRequest("getStopPoints", """{"pattern": "$pattern"}""") + } + + val points = response["success"].asJsonArray.map { it.asJsonObject } + + val names = HashSet<String>() + + points.forEach { + val name = it["name"].asString + names.add(name) + } + + return names.map { StopSuggestion(it, emptySet(), "", "") } + } + + private suspend fun makeRequest(method: String, data: String): JsonObject { + val client = OkHttpClient() + val url = "http://www.peka.poznan.pl/vm/method.vm?ts=${Calendar.getInstance().timeInMillis}" + val body = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded; charset=UTF-8"), + "method=$method&p0=$data") + val request = okhttp3.Request.Builder() + .url(url) + .post(body) + .build() + println("makeRequest: $request") + + + val responseBody: String? + try { + responseBody = withContext(CommonPool) { + client.newCall(request).execute().body()?.string() + } + } catch (e: IOException) { + return JsonObject() + } + + + return Gson().fromJson(responseBody, JsonObject::class.java) + } +} diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt b/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt index ceed419ddda0ed2bca34443399301c56f5818792..4f5762e92ce634e5e4275ef8558da5615a6a1ec9 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.content.Context import android.database.* import android.database.sqlite.SQLiteDatabase +import android.database.sqlite.SQLiteException import ml.adamsprogs.bimba.* import ml.adamsprogs.bimba.models.gtfs.* import ml.adamsprogs.bimba.models.suggestions.* @@ -37,25 +38,29 @@ private fun constructTimetable(context: Context) { val timetable = Timetable() val filesDir = context.getSecondaryExternalFilesDir() val dbFile = File(filesDir, "timetable.db") - timetable.db = SQLiteDatabase.openDatabase(dbFile.path, null, SQLiteDatabase.OPEN_READONLY) // fixme can be null + timetable.db = try { + SQLiteDatabase.openDatabase(dbFile.path, null, SQLiteDatabase.OPEN_READONLY) + } catch(e: SQLiteException) { + null + } this.timetable = timetable } } - private lateinit var db: SQLiteDatabase + private var db: SQLiteDatabase? = null private var _stops: List<StopSuggestion>? = null fun refresh() { } - fun getStopSuggestions(context: Context, force: Boolean = false): List<StopSuggestion> { + fun getStopSuggestions(/*context: Context, */force: Boolean = false): List<StopSuggestion> { if (_stops != null && !force) return _stops!! val ids = HashMap<String, HashSet<AgencyAndId>>() val zones = HashMap<String, String>() - val cursor = db.rawQuery("select stop_name, stop_id, zone_id from stops", null) + val cursor = db!!.rawQuery("select stop_name, stop_id, zone_id from stops", null) while (cursor.moveToNext()) { val name = cursor.getString(0) @@ -70,20 +75,22 @@ cursor.close() _stops = ids.map { + /*todo val colour = when (zones[it.key]) { "A" -> "#${getColour(R.color.zoneA, context).toString(16)}" "B" -> "#${getColour(R.color.zoneB, context).toString(16)}" "C" -> "#${getColour(R.color.zoneC, context).toString(16)}" else -> "#000000" } - StopSuggestion(it.key, it.value, zones[it.key]!!, colour) + */ + StopSuggestion(it.key, it.value, zones[it.key]!!, "#000000") }.sorted() return _stops!! } fun getLineSuggestions(): List<LineSuggestion> { val routes = ArrayList<LineSuggestion>() - val cursor = db.rawQuery("select * from routes", null) + val cursor = db!!.rawQuery("select * from routes", null) while (cursor.moveToNext()) { val routeId = cursor.getString(0) @@ -100,7 +107,7 @@ val headsigns = HashMap >>() val stopsIndex = HashMap<Int, String>() val where = stops.joinToString(" or ", "where ") { "stop_id = ?" } - var cursor = db.rawQuery("select stop_id, stop_code from stops $where", stops.map { it.toString() }.toTypedArray()) + var cursor = db!!.rawQuery("select stop_id, stop_code from stops $where", stops.map { it.toString() }.toTypedArray()) while (cursor.moveToNext()) { stopsIndex[cursor.getInt(0)] = cursor.getString(1) @@ -108,7 +115,7 @@ } cursor.close() - cursor = db.rawQuery("select stop_id, route_id, trip_headsign " + + cursor = db!!.rawQuery("select stop_id, route_id, trip_headsign " + "from stop_times natural join trips " + where, stops.map { it.toString() }.toTypedArray()) @@ -138,7 +145,7 @@ */ } fun getStopName(stopId: AgencyAndId): String { - val cursor = db.rawQuery("select stop_name from stops where stop_id = ?", + val cursor = db!!.rawQuery("select stop_name from stops where stop_id = ?", arrayOf(stopId.id)) cursor.moveToNext() val name = cursor.getString(0) @@ -148,7 +155,7 @@ return name } fun getStopCode(stopId: AgencyAndId): String { - val cursor = db.rawQuery("select stop_code from stops where stop_id = ?", + val cursor = db!!.rawQuery("select stop_code from stops where stop_id = ?", arrayOf(stopId.id)) cursor.moveToNext() val code = cursor.getString(0) @@ -159,7 +166,7 @@ } fun getStopDepartures(stopId: AgencyAndId): Map<AgencyAndId, List<Departure>> { val map = HashMap<AgencyAndId, ArrayList<Departure>>() - val cursor = db.rawQuery("select route_id, service_id, departure_time, " + + val cursor = db!!.rawQuery("select route_id, service_id, departure_time, " + "wheelchair_accessible, stop_sequence, trip_id, trip_headsign, route_desc " + "from stop_times natural join trips natural join routes where stop_id = ?", arrayOf(stopId.id)) @@ -197,7 +204,7 @@ "(stop_id = ${it.stop} and route_id = '${it.line}' and trip_headsign = '${it.headsign}')" } ?: listOf() }.joinToString(" or ") - val cursor = db.rawQuery("select route_id, service_id, departure_time, " + + val cursor = db!!.rawQuery("select route_id, service_id, departure_time, " + "wheelchair_accessible, stop_sequence, trip_id, trip_headsign, route_desc " + "from stop_times natural join trips natural join routes where $wheres", null) @@ -245,7 +252,7 @@ } fun calendarToMode(serviceId: AgencyAndId): List<Int> { val days = ArrayList<Int>() - val cursor = db.rawQuery("select * from calendar where service_id = ?", + val cursor = db!!.rawQuery("select * from calendar where service_id = ?", arrayOf(serviceId.id)) cursor.moveToNext() @@ -308,10 +315,12 @@ } @SuppressLint("Recycle") fun isEmpty(): Boolean { + if (db == null) + return true var result: Boolean var cursor: Cursor? = null try { - cursor = db.rawQuery("select * from feed_info", null) + cursor = db!!.rawQuery("select * from feed_info", null) result = !cursor.moveToNext() } catch (e: Exception) { result = true @@ -322,7 +331,7 @@ return result } fun getValidSince(): String { - val cursor = db.rawQuery("select feed_start_date from feed_info", null) + val cursor = db!!.rawQuery("select feed_start_date from feed_info", null) cursor.moveToNext() val validTill = cursor.getString(0) @@ -332,7 +341,7 @@ return validTill } fun getValidTill(): String { - val cursor = db.rawQuery("select feed_end_date from feed_info", null) + val cursor = db!!.rawQuery("select feed_end_date from feed_info", null) cursor.moveToNext() val validTill = cursor.getString(0) @@ -355,7 +364,7 @@ } fun getServiceFor(day: Int): AgencyAndId { val dayColumn = arrayOf("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday")[((day + 5) % 7)] - val cursor = db.rawQuery("select service_id from calendar where $dayColumn = 1", null) + val cursor = db!!.rawQuery("select service_id from calendar where $dayColumn = 1", null) val service: Int cursor.moveToNext() @@ -370,7 +379,7 @@ } fun getPlatesForStop(stop: AgencyAndId): Set<Plate.ID> { val plates = HashSet<Plate.ID>() - val cursor = db.rawQuery("select route_id, trip_headsign " + + val cursor = db!!.rawQuery("select route_id, trip_headsign " + "from stop_times natural join trips where stop_id = ? " + "group by route_id, trip_headsign", arrayOf(stop.id)) @@ -387,7 +396,7 @@ fun getTripGraphs(id: AgencyAndId): Array<TripGraph> { val graphs = arrayOf(TripGraph(), TripGraph()) - val cursor = db.rawQuery("select trip_id, trip_headsign, direction_id, stop_id, " + + val cursor = db!!.rawQuery("select trip_id, trip_headsign, direction_id, stop_id, " + "stop_sequence, pickup_type, stop_name, zone_id " + "from stop_times natural join trips natural join stops" + "where route_id = ?", arrayOf(id.id)) diff --git a/app/src/main/res/drawable/icon_dev.xml b/app/src/main/res/drawable/icon_dev.xml new file mode 100644 index 0000000000000000000000000000000000000000..a4b238053d6fdefaa6c74cfe50a206f56b769aff --- /dev/null +++ b/app/src/main/res/drawable/icon_dev.xml @@ -0,0 +1,17 @@ +<vector android:height="24dp" android:viewportHeight="293" + android:viewportWidth="298" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillAlpha="1" android:fillColor="#5E5E5E" + android:pathData="m62.925,292.677v0c-3.69,-0.897 -5.684,-3.789 -4.288,-6.482L90.846,223.472c1.296,-2.593 5.485,-3.989 9.174,-2.992v0c3.69,0.897 5.684,3.789 4.288,6.482l-32.209,62.723c-1.296,2.593 -5.385,3.989 -9.174,2.992z" android:strokeWidth="0.99718279"/> + <path android:fillAlpha="1" android:fillColor="#5E5E5E" + android:pathData="m229.156,292.677v0c3.69,-0.897 5.684,-3.789 4.288,-6.482l-32.209,-62.723c-1.296,-2.593 -5.485,-3.989 -9.174,-2.992v0c-3.69,0.897 -5.684,3.789 -4.288,6.482l32.209,62.723c1.296,2.593 5.485,3.989 9.174,2.992z" android:strokeWidth="0.99718279"/> + <path android:fillAlpha="1" android:fillColor="#5E5E5E" + android:pathData="m151.076,37.797 l-0.598,0.199c-1.795,0.698 -3.789,-0.299 -4.487,-2.094L136.418,9.277c-0.698,-1.795 0.299,-3.789 2.094,-4.487l0.598,-0.199c1.795,-0.698 3.789,0.299 4.487,2.094l9.573,26.625c0.698,1.895 -0.299,3.889 -2.094,4.487z" android:strokeWidth="0.99718279"/> + <path android:fillAlpha="1" android:fillColor="#5E5E5E" + android:pathData="m180.294,4.79v0c0,2.094 -1.695,3.789 -3.789,3.789h-60.828c-2.094,0 -3.789,-1.695 -3.789,-3.789v0c0,-2.094 1.695,-3.789 3.789,-3.789h60.828c2.094,0 3.789,1.695 3.789,3.789z" android:strokeWidth="0.99718279"/> + <path android:fillAlpha="1" android:fillColor="#5E5E5E" + android:pathData="M218.286,237.034H73.795c-13.263,0 -23.932,-10.77 -23.932,-23.932V105.406c0,-41.184 33.406,-74.689 74.689,-74.689h43.178c41.184,0 74.689,33.406 74.689,74.689v107.696c-0.1,13.163 -10.869,23.932 -24.132,23.932z" android:strokeWidth="0.99718279"/> + <path android:fillColor="#FFFFFF" android:pathData="M212.104,146.789L79.977,146.789c-5.584,0 -10.171,-4.487 -10.171,-10.171l0,-34.802c0,-16.852 13.661,-30.514 30.514,-30.514L191.761,71.302c16.852,0 30.514,13.661 30.514,30.514l0,34.802c-0.1,5.684 -4.587,10.171 -10.171,10.171z"/> + <path android:fillColor="#FFFFFF" android:pathData="M161.148,56.344L130.933,56.344c-3.191,0 -5.684,-2.593 -5.684,-5.684l0,0c0,-3.191 2.593,-5.684 5.684,-5.684l30.215,0c3.191,0 5.684,2.593 5.684,5.684l0,0c0,3.091 -2.593,5.684 -5.684,5.684z"/> + <path android:fillColor="#FFFFFF" android:pathData="M86.957,192.36m-14.758,0a14.758,14.758 0,1 1,29.517 0a14.758,14.758 0,1 1,-29.517 0"/> + <path android:fillColor="#FFFFFF" android:pathData="M205.223,192.36m-14.758,0a14.758,14.758 0,1 1,29.517 0a14.758,14.758 0,1 1,-29.517 0"/> +</vector>