diff --git a/.circleci/config.yml b/.circleci/config.yml index 3ebdd89..038e7cf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,11 +1,12 @@ -version: 2 +version: '2.1' +orbs: + android: circleci/android@2.0 jobs: build: - working_directory: ~/code - docker: - - image: circleci/android:api-29 - environment: - JVM_OPTS: -Xmx3200m + executor: + name: android/android-machine + resource-class: medium + tag: 2021.10.1 steps: - checkout - restore_cache: diff --git a/.gitignore b/.gitignore index 70c4606..e5582e8 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ out .DS_Store build /captures +apks/ +app/release/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 1259af3..db063d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,123 +1,253 @@ -RELEASE NOTES -============= +# CHANGELOG -2017-01-XX / 1.8.3 ---- +Possible tags: - - Change links from SpaceAPI.net to SpaceDirectory.org - - Display all webcams +- [info] An information not directly related to code changes +- [feature] A new feature or an improvement +- [bug] A bugfix +- [change] A change that's neither a feature nor a bugfix +- [i18n] Internationalization / translation -2016-07-02 / 1.8.2 ---- - - Fix camera and stream url being displayed - - Fix twitter link to the new url format +# Unreleased -2016-05-06 / 1.8.1 ---- - - Uses custom API directory end point (https://spaceapi.fixme.ch/directory.json) - - Allow editing of the API directory end point and the current hackerspace API - - Add Danish translation (thanks Mikkel) +# v2.1.2 (2023-10-02) -2016-04-14 / 1.8 ---- +- [feature] Show state "last change" timestamp as localized datetime ([#47]) +- [bug] Use correct mastodon property ([#49]) +- [bug] Fix network error message +- [change] Drop support for Android 5–7, require at least Android 8 +- [change] Remove old autolinking-workaround for HTC devices ([#48]) - - Supports invalid SSL certificates - - Allow widget to be resized - - Add Dutch translation - - Fix http to https redirection - - General fixes +Contributors to this version: -2014-08-26 / 1.7.4.1 -------- +- cyroxx (@cyroxx) +- Danilo Bargen (@dbrgn) - - Fix crash when there's no error message +Thanks! -2014-08-07 / 1.7.4 ---- +[#47]: https://github.com/spaceapi-community/my-hackerspace/pull/47 +[#48]: https://github.com/spaceapi-community/my-hackerspace/pull/48 +[#49]: https://github.com/spaceapi-community/my-hackerspace/pull/49 - - German translation (thanks to Lokke and Phervieux) - - Better hs list with alphabetical index - - Better errors messages - - Caching for http requests (images, hs directory) - - Add status message to the widget (thanks Fpletz) - - Fix bugs: widget updates, ignore ext fields, click from widget -2013-10-25 / 1.7.3 ------ +## v2.1.1 (2023-04-27) - - Fix regression with widget custom open/close logo - - Fix order of hackerspaces with different cases +- [bug] Fix a bug when parsing v14 endpoints that contain a SpaceFED key + without a "spacephone" field ([#44]) +- [bug] Temporarily use directory from GitHub directly to speed up loading + ([#45]) -2013-09-09 / 1.7.2 ------ +Contributors to this version: - - Better layout for sensors - - Support more fields for sensors (machines, names, properties) +- Danilo Bargen (@dbrgn) -2013-09-06 / 1.7.1 ------ +Thanks! - - Faster http requests (Use DefaultHttpClient instead of HttpURLConnection) +[#44]: https://github.com/spaceapi-community/my-hackerspace/pull/44 +[#45]: https://github.com/spaceapi-community/my-hackerspace/pull/45 -2013-09-05 / 1.7 ------ - - Full support of SpaceAPI 0.13, drops mixed api definition: hackerspaces must comply to the level they declare! - - Widget transparency preference added (by default transparency is deactivated) +## v2.1.0 (2023-04-16) -2013-06-04 / 1.6.1 ------ +- [feature] Add information about the app to settings ([#18]) +- [bug] Fix widget crashing on Android 12 ([#24]) +- [bug] Export widget config activity to fix crashing OpenLauncher ([#33]) +- [change] Reverse latitude and longitude displayed on the screen ([#37]) +- [change] Improvements for widget ([#34]) +- [change] Upgrade dependencies, change TargetSDK to 33 ([#42]) - - French translation - - Fix the widget's image not updating - - Change to the new spaceapi url +Contributors to this version: -2013-01-02 / 1.6 ---- +- Danilo Bargen (@dbrgn) +- Nicco Kunzmann (@niccokunzmann) +- Adrian Pascu (@adipascu) +- Frieder Hannenheim (@FriederHannenheim) - - Better layout in general - - Use Holo light theme for Android >=3 - - Refresh the current hackerspace - - Default to 15mn for the Widget - - Settings button to change the widget interval - - Fix lat/lon link - - Fix crash when maps/email app not found +Thanks! -2012-10-29 / 1.5.1 ------ +[#18]: https://github.com/spaceapi-community/my-hackerspace/pull/18 +[#24]: https://github.com/spaceapi-community/my-hackerspace/pull/24 +[#33]: https://github.com/spaceapi-community/my-hackerspace/pull/33 +[#34]: https://github.com/spaceapi-community/my-hackerspace/pull/34 +[#37]: https://github.com/spaceapi-community/my-hackerspace/pull/37 +[#42]: https://github.com/spaceapi-community/my-hackerspace/pull/42 +[#44]: https://github.com/spaceapi-community/my-hackerspace/pull/44 - - Bug fixes - - Add a spinner when loading image - - Faster download -2012-05-19 / 1.5 ---- +## v2.0.2 (2022-08-07) - - Only download image if there is a change of state (better battery live and reduce network usage) +Unfortunately the app was pulled down from Google Play by Google due to +"developer inactivity". Due to the lack of e-mail forwarding, we never noticed +the warnings, so the app is now gone. -2012-05-15 / 1.4 ---- +To fix this, we had to change the app ID. Additionally, there will be an F-Droid release. - - Add Cam and Stream links if present - - Link for adresses opening GMaps - - Sort Hackerspaces by name - - Accept untrusted SSL certificates - - Better error reporting - - BUGFIX: Theme shoud be correct on all devices/versions - - BUGFIX: Should work after reboot correctly +Changes: -2012-05-08 / 1.3 ---- +- [change] Update dependencies +- [change] Rename package to `io.spaceapi.community.myhackerspace` +- [info] Add F-Droid metadata - - White theme by default (may break on samsung devices) - - Check if network is enabled - - Handle rotation correctly -2012-05-06 / 1.2 -2012-05-04 / 1.1 -2012-04-29 / 1.0 ---- +## v2.0.1 (2021-05-14) - - Initial release +- [bug] Fix refresh button ([#5][i5]) + +[i5]: https://github.com/spaceapi-community/my-hackerspace/pull/5 + + +## v2.0.0 (2021-02-20) + +- [info] App was re-released by the SpaceAPI project under a new package name ([#1][i1]) +- [info] GitHub is now at https://github.com/spaceapi-community/my-hackerspace/ +- [info] The app now requires at least Android 5 (API 21) ([#75][i75]) +- [feature] Support for SpaceAPI v14 ([#85][i85]) +- [feature] New app launcher icon ([#3][i3]) +- [feature] More modern icons in app UI ([#74][i74]) +- [bug] Don't save empty data in application state ([#64][i64]) +- [change] Update all domains to spaceapi.io ([#65][i65], [#71][i71]) +- [change] Switch to Java 8 ([#73][i73]) +- [change] Remove MemorizingTrustManager ([#65][i65]) +- [change] Upgrade dependencies ([#69][i69]) +- [change] Switch to CircleCI ([#69][i69]) +- [change] Add support for annotations ([#77][i77]) +- [i18n] Improved translations + +[i1]: https://github.com/spaceapi-community/my-hackerspace/pull/1 +[i3]: https://github.com/spaceapi-community/my-hackerspace/pull/3 +[i64]: https://github.com/fixme-lausanne/MyHackerspace/pull/64 +[i65]: https://github.com/fixme-lausanne/MyHackerspace/pull/65 +[i69]: https://github.com/fixme-lausanne/MyHackerspace/pull/69 +[i71]: https://github.com/fixme-lausanne/MyHackerspace/pull/71 +[i73]: https://github.com/fixme-lausanne/MyHackerspace/pull/73 +[i74]: https://github.com/fixme-lausanne/MyHackerspace/pull/74 +[i75]: https://github.com/fixme-lausanne/MyHackerspace/pull/75 +[i77]: https://github.com/fixme-lausanne/MyHackerspace/pull/77 +[i85]: https://github.com/fixme-lausanne/MyHackerspace/pull/85 + + +## v1.8.3 (2017-01-XX) + +- Change links from SpaceAPI.net to SpaceDirectory.org +- Display all webcams + + +## v1.8.2 (2016-07-02) + +- Fix camera and stream url being displayed +- Fix twitter link to the new url format + + +## v1.8.1 (2016-05-06) + +- Uses custom API directory end point (https://spaceapi.fixme.ch/directory.json) +- Allow editing of the API directory end point and the current hackerspace API +- Add Danish translation (thanks Mikkel) + + +## v1.8 (2016-04-14) + +- Supports invalid SSL certificates +- Allow widget to be resized +- Add Dutch translation +- Fix http to https redirection +- General fixes + + +## v1.7.4.1 (2014-08-26) + +- Fix crash when there's no error message + + +## v1.7.4 (2014-08-07) + +- German translation (thanks to Lokke and Phervieux) +- Better hs list with alphabetical index +- Better errors messages +- Caching for http requests (images, hs directory) +- Add status message to the widget (thanks Fpletz) +- Fix bugs: widget updates, ignore ext fields, click from widget + + +## v1.7.3 (2013-10-25) + +- Fix regression with widget custom open/close logo +- Fix order of hackerspaces with different cases + + +## v1.7.2 (2013-09-09) + +- Better layout for sensors +- Support more fields for sensors (machines, names, properties) + + +## v1.7.1 (2013-09-06) + +- Faster http requests (Use DefaultHttpClient instead of HttpURLConnection) + + +## v1.7 (2013-09-05) + +- Full support of SpaceAPI 0.13, drops mixed api definition: hackerspaces must comply to the level they declare! +- Widget transparency preference added (by default transparency is deactivated) + + +## v1.6.1 (2013-06-04) + +- French translation +- Fix the widget's image not updating +- Change to the new spaceapi url + + +## v1.6 (2013-01-02) + +- Better layout in general +- Use Holo light theme for Android >=3 +- Refresh the current hackerspace +- Default to 15mn for the Widget +- Settings button to change the widget interval +- Fix lat/lon link +- Fix crash when maps/email app not found + + +## v1.5.1 (2012-10-29) + +- Bug fixes +- Add a spinner when loading image +- Faster download + + +## v1.5 (2012-05-19) + +- Only download image if there is a change of state (better battery live and reduce network usage) + + +## v1.4 (2012-05-15) + +- Add Cam and Stream links if present +- Link for adresses opening GMaps +- Sort Hackerspaces by name +- Accept untrusted SSL certificates +- Better error reporting +- BUGFIX: Theme shoud be correct on all devices/versions +- BUGFIX: Should work after reboot correctly + + +## v1.3 (2012-05-08) + +- White theme by default (may break on samsung devices) +- Check if network is enabled +- Handle rotation correctly + + +## v1.2 (2012-05-06) + + +## v1.1 (2012-05-04) + + +## v1.0 (2012-04-29) + +- Initial release diff --git a/MAINTENANCE.md b/MAINTENANCE.md new file mode 100644 index 0000000..899d04e --- /dev/null +++ b/MAINTENANCE.md @@ -0,0 +1,31 @@ +# Maintenance + +This project is maintained by the SpaceAPI community. + +## Current Maintainers + +Maintainers (may review and merge): + +- @dbrgn +- @niccokunzmann + +People with publication rights on Google Play: + +- @dbrgn + +## Review and Merge Policy + +In case there are multiple maintainers, non-trivial changes should be reviewed +by another team member. A review is requested through the GitHub UI. + +If a change is not reviewed within 1-2 weeks, the merge request may be merged +without a review. + +For urgent bugfixes, the review may be skipped. + +## Non-Responsive Maintainers + +If maintainers are not responding, send a reminder and/or try to contact them +through other communication channels. If this situation persists for a longer +time period, other members of the SpaceAPI community can and should take over +the maintenance of the project. diff --git a/README.md b/README.md index 25dde92..566591e 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,65 @@ -My Hackerspace -============== +
+ + +

+
-- Status of hackerspaces using the [SpaceAPI](https://spaceapi.io/) +# My Hackerspace + +[![Build status](https://circleci.com/gh/spaceapi-community/my-hackerspace.svg?style=shield&circle-token=:circle-token)](https://circleci.com/gh/spaceapi-community/my-hackerspace) + +This is an Android app with the following features: + +- Show the opening status of hacker- and makerspaces using the [SpaceAPI](https://spaceapi.io/) - Show information about the space (contact, location, sensors, ...) - Status widget, multiple widgets supported -- Available on [f-droid](https://f-droid.org/repository/browse/?fdid=ch.fixme.status) and [play store](https://play.google.com/store/apps/details?id=ch.fixme.status) -Master branch: [![Build Status](https://travis-ci.org/fixme-lausanne/MyHackerspace.svg?branch=master)](https://travis-ci.org/fixme-lausanne/MyHackerspace) +The app was originally developed in 2012 by [@rorist] from [FIXME Lausanne]. In +2021, the app was transferred to the [SpaceAPI community repositories] and is +now mainly being developed by members of [Coredump]. +You can join our [Matrix](https://matrix.org/) chat at `#spaceapi:matrix.coredump.ch`. -HOW TO COMPILE -============= +[@rorist]: https://github.com/rorist +[FIXME Lausanne]: https://fixme.ch/ +[SpaceAPI community repositories]: https://github.com/spaceapi-community/ +[Coredump]: https://www.coredump.ch/ -First, get the sources. +Get it on F-Droid +Get it on Google Play - git clone --recursive https://github.com/fixme-lausanne/MyHackerspace.git - cd MyHackerspace +## How it works -Get the 3rd party librairies +The app will get the list of hackspaces from [https://directory.spaceapi.io](https://directory.spaceapi.io). +You can then choose the space by its name from a list. +When the space is chosen, the associated data is retrieved from the space's +SpaceAPI endpoint (which is registered in the SpaceAPI directory). +If you would like to add your space to the directory, have a look at +[the SpaceAPI website](https://spaceapi.io/provide-an-endpoint/). - git submodule init - git submodule update +### The Widget -Android Studio --------------- +The image for the widget is specified in the SpaceAPI endpoint JSON. +Have a look at the [schema documentation](https://spaceapi.io/docs/) to make your +widget more pretty! + +1. `open.icon` - if present, the widget chooses the specific open/closed images +2. `logo` - the widget chooses the logo of the hackspace to display + +## How to Compile + +First, get the sources. + + git clone --recursive https://github.com/spaceapi-community/my-hackerspace.git + cd my-hackerspace + +### Android Studio With Android Studio, simply open the project directory and you should be set. -Command Line ------------- +### Command Line You can build the project using Gradle. -You'll first need the Android SDK, and install build tools 21.1.1 which is considered obsolete. -You can find this version by ticking obsolete in the Android SDK Manager. - The following examples use the gradle wrapper script which will automatically download gradle to your local directory. If you want to use your own system-wide installation instead, simply replace `./gradlew` commands with @@ -56,24 +82,4 @@ To see other tasks that gradle offers, run ./gradlew tasks -LOCAL DIRECTORY -=============== - -For testing purposes you can run a local directory using this technique: - -* Create a new Android AVD called for instance "android6" -* Start the AVD from the command line: - `emulator -avd android6 -shared-net-id 16` -* Make sure a network interface on your host computer is reachable by this IP: - `sudo ifconfig eth0 10.0.2.3` -* On the host, go to the test directory and run the serv.py script: - `./serv.py` -* Go in the app preferences and set the OpenSpaceDirectory URL to the following: - `https://10.0.2.3:8443/directory.json` - -TODO -==== - -- Auto recognize field types in the API (array, obj, string, etc) -- Integrate woozzu library as 3rd party diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..eab589a --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,21 @@ +# Releasing + +Set variables: + + $ export VERSION=X.Y.Z + $ export GPG_KEY=20EE002D778AE197EF7D0D2CB993FF98A90C9AB1 # Danilo + +Update version numbers: + + $ vim app/build.gradle + +Update changelog: + + $ vim CHANGELOG.md + +Add the changelog to `metadata/en-US/changelogs/.txt` as well. + +Commit & tag: + + $ git commit -S${GPG_KEY} -m "Release v${VERSION}" + $ git tag -s -u ${GPG_KEY} v${VERSION} -m "Version ${VERSION}" diff --git a/apks/MyHackerspace-1.6.1.apk b/apks/MyHackerspace-1.6.1.apk deleted file mode 100644 index d0d42cb..0000000 Binary files a/apks/MyHackerspace-1.6.1.apk and /dev/null differ diff --git a/apks/MyHackerspace-1.6.apk b/apks/MyHackerspace-1.6.apk deleted file mode 100644 index a8bbfd4..0000000 Binary files a/apks/MyHackerspace-1.6.apk and /dev/null differ diff --git a/apks/MyHackerspace-1.7.3.apk b/apks/MyHackerspace-1.7.3.apk deleted file mode 100644 index 4d7cf75..0000000 Binary files a/apks/MyHackerspace-1.7.3.apk and /dev/null differ diff --git a/apks/MyHackerspace-1.7.4.apk b/apks/MyHackerspace-1.7.4.apk deleted file mode 100644 index 97c69c1..0000000 Binary files a/apks/MyHackerspace-1.7.4.apk and /dev/null differ diff --git a/apks/MyHackerspace-1.8.2.apk b/apks/MyHackerspace-1.8.2.apk deleted file mode 100644 index 39c24ce..0000000 Binary files a/apks/MyHackerspace-1.8.2.apk and /dev/null differ diff --git a/app/build.gradle b/app/build.gradle index 45dc106..1cd2343 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,15 +1,15 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 29 - buildToolsVersion "29.0.2" + namespace 'io.spaceapi.community.myhackerspace' defaultConfig { - applicationId "ch.fixme.status" - minSdkVersion 21 - targetSdkVersion 29 - versionCode 20 - versionName "1.8.1" + applicationId "io.spaceapi.community.myhackerspace" + minSdkVersion 26 + compileSdk 34 + targetSdkVersion 34 + versionCode 105 + versionName "2.1.2" } buildTypes { @@ -18,12 +18,23 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } } + compileOptions { - sourceCompatibility = 1.8 - targetCompatibility = 1.8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + lint { + warning 'MissingTranslation' } } dependencies { - implementation "androidx.annotation:annotation:1.1.0" + implementation "androidx.annotation:annotation:1.8.0" + + // SpaceAPI Library + implementation "io.github.spaceapi-community:spaceapi-kt:0.6.1" + + // JUnit for testing + testImplementation 'junit:junit:4.13.2' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 343f498..7e51668 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,17 +1,17 @@ - + + android:launchMode="singleTask" + android:exported="true"> @@ -39,20 +40,25 @@ + android:label="@string/app_name" + android:exported="true"> - + - + diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000..c960fff Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/java/ch/fixme/status/Parse12.java b/app/src/main/java/ch/fixme/status/Parse12.java deleted file mode 100644 index 1a6f3b7..0000000 --- a/app/src/main/java/ch/fixme/status/Parse12.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2012-2017 Aubort Jean-Baptiste (Rorist) - * Licensed under GNU's GPL 3, see README - */ -package ch.fixme.status; - -import android.util.Log; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.sql.Date; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.HashMap; - -public class Parse12 extends ParseGeneric { - - public Parse12(JSONObject jsonObject) throws JSONException { - super(jsonObject); - } - - protected HashMap parse() throws JSONException { - - // Mandatory fields - mResult.put(API_STATUS, mApi.getBoolean(API_STATUS)); - mResult.put(API_NAME, mApi.getString(API_NAME)); - mResult.put(API_URL, mApi.getString(API_URL)); - mResult.put(API_LOGO, mApi.getString(API_LOGO)); - - // Status icons - JSONObject icons = mApi.getJSONObject(API_ICON); - mResult.put(API_ICON + API_ICON_OPEN, icons.getString(API_ICON_OPEN)); - mResult.put(API_ICON + API_ICON_CLOSED, - icons.getString(API_ICON_CLOSED)); - - // Status text - if (!mApi.isNull(API_STATUS_TXT)) { - mResult.put(API_STATUS_TXT, mApi.getString(API_STATUS_TXT)); - } - - // Last change date - if (!mApi.isNull(API_LASTCHANGE)) { - Date date = new Date(mApi.getLong(API_LASTCHANGE) * 1000); - DateFormat formatter = SimpleDateFormat.getDateTimeInstance(); - mResult.put(API_LASTCHANGE, formatter.format(date)); - } - - // Location - if (!mApi.isNull(API_LON) && !mApi.isNull(API_LAT)) { - mResult.put(API_LON, mApi.getString(API_LON)); - mResult.put(API_LAT, mApi.getString(API_LAT)); - } - if (!mApi.isNull(API_ADDRESS)) { - mResult.put(API_ADDRESS, mApi.getString(API_ADDRESS)); - } - - // Contact - if (!mApi.isNull(API_CONTACT)) { - JSONObject contact = mApi.getJSONObject(API_CONTACT); - - // Phone - if (!contact.isNull(API_PHONE)) { - mResult.put(API_PHONE, contact.getString(API_PHONE)); - } - // Twitter - if (!contact.isNull(API_TWITTER)) { - mResult.put(API_TWITTER, contact.getString(API_TWITTER)); - } - // IRC - if (!contact.isNull(API_IRC)) { - mResult.put(API_IRC, contact.getString(API_IRC)); - } - // Email - if (!contact.isNull(API_EMAIL)) { - mResult.put(API_EMAIL, contact.getString(API_EMAIL)); - } - // Mailing-List - if (!contact.isNull(API_ML)) { - mResult.put(API_ML, contact.getString(API_ML)); - } - } - - // Sensors - if (!mApi.isNull(API_SENSORS)) { - JSONArray sensors = mApi.getJSONArray(API_SENSORS); - JSONObject elem; - HashMap>> result = new HashMap<>(sensors.length()); - for (int i = 0; i < sensors.length(); i++) { - elem = (JSONObject) sensors.get(i); - try { - for (int j = 0; j < elem.length(); j++) { - ArrayList> elem_value = new ArrayList<>(); - String name = (String) elem.names().get(j); - JSONObject obj = elem.getJSONObject(name); - for (int k = 0; k < obj.length(); k++) { - String name2 = (String) obj.names().get(k); - HashMap elem_value_map = new HashMap<>(); - elem_value_map.put(API_SENSOR_NAME, name2); - elem_value_map.put(API_SENSOR_VALUE, obj.getString(name2)); - elem_value.add(elem_value_map); - } - result.put(name, elem_value); - } - } catch (Exception e) { - Log.e(Main.TAG, e.getLocalizedMessage()); - e.printStackTrace(); - ArrayList> elem_value = new ArrayList<>(); - HashMap elem_value_map = new HashMap<>(); - elem_value_map.put(API_SENSOR_VALUE, elem.toString()); - elem_value.add(elem_value_map); - result.put((String) elem.names().get(0), elem_value); - } - } - mResult.put(API_SENSORS, result); - } - - if (!mApi.isNull(API_STREAM) || !mApi.isNull(API_CAM)) { - // Stream - if (!mApi.isNull(API_STREAM)) { - JSONObject stream = mApi.optJSONObject(API_STREAM); - if (stream != null) { - HashMap streamMap = new HashMap<>(stream.length()); - JSONArray names = stream.names(); - for (int i = 0; i < stream.length(); i++) { - final String type = names.getString(i); - final String url = stream.getString(type); - streamMap.put(type, url); - } - mResult.put(API_STREAM, streamMap); - } - } - // Cam - if (!mApi.isNull(API_CAM)) { - JSONArray cam = mApi.optJSONArray(API_CAM); - if (cam != null) { - HashMap camMap = new HashMap<>(cam.length()); - for (int i = 0; i < cam.length(); i++) { - camMap.put("http", cam.getString(i)); - } - mResult.put(API_CAM, camMap); - } - } - } - - return mResult; - } -} diff --git a/app/src/main/java/ch/fixme/status/Parse13.java b/app/src/main/java/ch/fixme/status/Parse13.java deleted file mode 100644 index a1480e6..0000000 --- a/app/src/main/java/ch/fixme/status/Parse13.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (C) 2012-2017 Aubort Jean-Baptiste (Rorist) - * Licensed under GNU's GPL 3, see README - */ -package ch.fixme.status; - -import android.util.Log; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.sql.Date; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.HashMap; - -public class Parse13 extends ParseGeneric { - - public Parse13(JSONObject jsonObject) throws JSONException { - super(jsonObject); - } - - protected HashMap parse() throws JSONException { - - // Mandatory fields - JSONObject state = mApi.getJSONObject(API_STATE); - if (!state.isNull(API_STATUS)){ - mResult.put(API_STATUS, state.getBoolean(API_STATUS)); - } else { - mResult.put(API_STATUS, null); - } - mResult.put(API_NAME, mApi.getString(API_NAME)); - mResult.put(API_URL, mApi.getString(API_URL)); - mResult.put(API_LOGO, mApi.getString(API_LOGO)); - - // Status icons - if (!state.isNull(API_ICON)) { - JSONObject icon = state.getJSONObject(API_ICON); - mResult.put(API_ICON + API_ICON_OPEN, icon.getString(API_ICON_OPEN)); - mResult.put(API_ICON + API_ICON_CLOSED, - icon.getString(API_ICON_CLOSED)); - } - - // Status text - if (!state.isNull(API_STATE_MESSAGE)) { - mResult.put(API_STATUS_TXT, state.getString(API_STATE_MESSAGE)); - } - - // Last change date - if (!state.isNull(API_LASTCHANGE)) { - Date date = new Date(state.getLong(API_LASTCHANGE) * 1000); - DateFormat formatter = SimpleDateFormat.getDateTimeInstance(); - mResult.put(API_LASTCHANGE, formatter.format(date)); - } - - // Duration (FIXME addition) - if (!state.isNull(API_EXT_DURATION)) { - mResult.put(API_EXT_DURATION, state.getString(API_EXT_DURATION)); - } - - // Location (Mandatory) - if (!mApi.isNull(API_LOCATION)) { - JSONObject loc = mApi.getJSONObject(API_LOCATION); - if (!loc.isNull(API_LON) && !loc.isNull(API_LAT)) { - mResult.put(API_LON, loc.getString(API_LON)); - mResult.put(API_LAT, loc.getString(API_LAT)); - } - if (!loc.isNull(API_ADDRESS)) { - mResult.put(API_ADDRESS, loc.getString(API_ADDRESS)); - } - } - - // Contact - if (!mApi.isNull(API_CONTACT)) { - JSONObject contact = mApi.getJSONObject(API_CONTACT); - - // Phone - if (!contact.isNull(API_PHONE)) { - mResult.put(API_PHONE, contact.getString(API_PHONE)); - } - // SIP - if (!contact.isNull(API_SIP)) { - mResult.put(API_SIP, contact.getString(API_SIP)); - } - // Twitter - if (!contact.isNull(API_TWITTER)) { - mResult.put(API_TWITTER, contact.getString(API_TWITTER)); - } - // Identica - if (!contact.isNull(API_IDENTICA)) { - mResult.put(API_IDENTICA, contact.getString(API_IDENTICA)); - } - // Foursquare - if (!contact.isNull(API_FOURSQUARE)) { - mResult.put(API_FOURSQUARE, contact.getString(API_FOURSQUARE)); - } - // IRC - if (!contact.isNull(API_IRC)) { - mResult.put(API_IRC, contact.getString(API_IRC)); - } - // Jabber - if (!contact.isNull(API_JABBER)) { - mResult.put(API_JABBER, contact.getString(API_JABBER)); - } - // Email - if (!contact.isNull(API_EMAIL)) { - mResult.put(API_EMAIL, contact.getString(API_EMAIL)); - } - // Mailing-List - if (!contact.isNull(API_ML)) { - mResult.put(API_ML, contact.getString(API_ML)); - } - } - - // Sensors - if (!mApi.isNull(API_SENSORS)) { - JSONObject sensors = mApi.getJSONObject(API_SENSORS); - JSONArray names = sensors.names(); - JSONArray elem; - ArrayList> elem_value; - HashMap>> result = new HashMap>>( - sensors.length()); - for (int i = 0; i < names.length(); i++) { - String sensor_name = names.getString(i); - if (sensor_name.startsWith(API_EXT) || sensor_name.startsWith(API_RADIATION)) { - continue; - } - elem = sensors.getJSONArray(sensor_name); - elem_value = new ArrayList<>(elem.length()); - for (int j = 0; j < elem.length(); j++) { - HashMap elem_value_map = new HashMap<>(); - try { - JSONObject obj = (JSONObject) elem.get(j); - if (!obj.isNull(API_SENSOR_VALUE) - && !"".equals(obj.getString(API_SENSOR_VALUE))) { - elem_value_map.put(API_SENSOR_VALUE, obj.getString(API_SENSOR_VALUE)); - } - if (!obj.isNull(API_SENSOR_UNIT) - && !"".equals(obj.getString(API_SENSOR_UNIT))) { - elem_value_map.put(API_SENSOR_UNIT, obj.getString(API_SENSOR_UNIT)); - } - if (!obj.isNull(API_SENSOR_NAME) - && !"".equals(obj.getString(API_SENSOR_NAME))) { - elem_value_map.put(API_SENSOR_NAME, obj.getString(API_SENSOR_NAME)); - } - if (!obj.isNull(API_SENSOR_LOCATION) - && !"".equals(obj.getString(API_SENSOR_LOCATION))) { - elem_value_map.put(API_SENSOR_LOCATION, obj.getString(API_SENSOR_LOCATION)); - } - if (!obj.isNull(API_SENSOR_DESCRIPTION) - && !"".equals(obj.getString(API_SENSOR_DESCRIPTION))) { - elem_value_map.put(API_SENSOR_DESCRIPTION, obj.getString(API_SENSOR_DESCRIPTION)); - } - if (!obj.isNull(API_SENSOR_MACHINES) && obj.getJSONArray(API_SENSOR_MACHINES).length() > 0) { - elem_value_map.put(API_SENSOR_MACHINES, obj.get(API_SENSOR_MACHINES).toString()); - } - if (!obj.isNull(API_SENSOR_NAMES) && obj.getJSONArray(API_SENSOR_NAMES).length() > 0) { - elem_value_map.put(API_SENSOR_NAMES, obj.get(API_SENSOR_NAMES).toString()); - } - if (!obj.isNull(API_SENSOR_PROPERTIES)) { - - JSONObject obj2 = obj.getJSONObject(API_SENSOR_PROPERTIES); - String prop = ""; - for (int k = 0; k < obj2.length(); k++) { - String name = (String) obj2.names().get(k); - JSONObject obj3 = obj2.getJSONObject(name); - prop += name + ": " + obj3.getString(API_SENSOR_VALUE) + " " + obj3.getString(API_SENSOR_UNIT) + ", "; - } - elem_value_map.put(API_SENSOR_PROPERTIES, prop.substring(0, prop.length() - 2)); - } - } catch (Exception e) { - Log.e(Main.TAG, e.getMessage()); - elem_value_map.put(API_SENSOR_VALUE, elem.get(j).toString()); - } - elem_value.add(elem_value_map); - } - result.put(sensor_name, elem_value); - } - mResult.put(API_SENSORS, result); - } - - // Stream - if (!mApi.isNull(API_STREAM)) { - JSONObject stream = mApi.optJSONObject(API_STREAM); - if (stream != null) { - HashMap streamMap = new HashMap<>( - stream.length()); - JSONArray names = stream.names(); - for (int i = 0; i < stream.length(); i++) { - final String type = names.getString(i); - final String url = stream.getString(type); - streamMap.put(type, url); - } - mResult.put(API_STREAM, streamMap); - } - } - - // Cam - if (!mApi.isNull(API_CAM)) { - JSONArray cam = mApi.optJSONArray(API_CAM); - if (cam != null) { - ArrayList camList = new ArrayList<>(cam.length()); - for (int i = 0; i < cam.length(); i++) { - camList.add(cam.getString(i)); - } - mResult.put(API_CAM, camList); - } - } - - return mResult; - } -} diff --git a/app/src/main/java/ch/fixme/status/ParseGeneric.java b/app/src/main/java/ch/fixme/status/ParseGeneric.java deleted file mode 100644 index bc21ebf..0000000 --- a/app/src/main/java/ch/fixme/status/ParseGeneric.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2012-2017 Aubort Jean-Baptiste (Rorist) - * Licensed under GNU's GPL 3, see README - */ -package ch.fixme.status; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.HashMap; - -public class ParseGeneric { - protected HashMap mResult = new HashMap<>(); - protected JSONObject mApi; - - protected static final String API_NAME = "space"; - protected static final String API_LON = "lon"; - protected static final String API_LOCATION = "location"; - protected static final String API_LAT = "lat"; - protected static final String API_URL = "url"; - protected static final String API_LEVEL = "api"; - protected static final String API_STATE = "state"; - protected static final String API_STATE_MESSAGE = "message"; - protected static final String API_STATUS_TXT = "status"; - protected static final String API_EXT_DURATION = "ext_duration"; - protected static final String API_ADDRESS = "address"; - protected static final String API_CONTACT = "contact"; - protected static final String API_EMAIL = "email"; - protected static final String API_IRC = "irc"; - protected static final String API_JABBER = "jabber"; - protected static final String API_PHONE = "phone"; - protected static final String API_SIP = "sip"; - protected static final String API_TWITTER = "twitter"; - protected static final String API_IDENTICA = "identica"; - protected static final String API_FOURSQUARE = "foursquare"; - protected static final String API_ML = "ml"; - protected static final String API_STREAM = "stream"; - protected static final String API_CAM = "cam"; - protected static final String API_SENSORS = "sensors"; - protected static final String API_EXT = "ext_"; - protected static final String API_RADIATION = "radiation"; - - // Sensors - protected static final String API_SENSOR_VALUE = "value"; - protected static final String API_SENSOR_UNIT = "unit"; - protected static final String API_SENSOR_LOCATION = "location"; - protected static final String API_SENSOR_NAME = "name"; - protected static final String API_SENSOR_DESCRIPTION = "description"; - protected static final String API_SENSOR_MACHINES = "machines"; - protected static final String API_SENSOR_NAMES = "names"; - protected static final String API_SENSOR_PROPERTIES = "properties"; - - // State - protected static final String API_DEFAULT = "https://fixme.ch/status.json"; - protected static final String API_ICON = "icon"; - protected static final String API_ICON_OPEN = "open"; - protected static final String API_ICON_CLOSED = "closed"; - protected static final String API_LOGO = "logo"; - protected static final String API_STATUS = "open"; - protected static final String API_LASTCHANGE = "lastchange"; - - public ParseGeneric(JSONObject jsonObject) { - mApi = jsonObject; - } - - public ParseGeneric(String jsonString) throws JSONException { - mApi = new JSONObject(jsonString); - } - - public HashMap getData() throws JSONException { - if ("0.13".equals(mApi.getString(ParseGeneric.API_LEVEL))) { - mResult = new Parse13(mApi).parse(); - } else if ("0.12".equals(mApi.getString(ParseGeneric.API_LEVEL))) { - mResult = new Parse12(mApi).parse(); - } else { - throw new JSONException("API LEVEL NOT SUPPORTED: " - + mApi.getString(ParseGeneric.API_LEVEL)); - } - return mResult; - } - -} diff --git a/app/src/main/java/io/spaceapi/community/myhackerspace/AboutLayout.java b/app/src/main/java/io/spaceapi/community/myhackerspace/AboutLayout.java new file mode 100644 index 0000000..5a087d9 --- /dev/null +++ b/app/src/main/java/io/spaceapi/community/myhackerspace/AboutLayout.java @@ -0,0 +1,40 @@ +package io.spaceapi.community.myhackerspace; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.Nullable; + +public class AboutLayout extends LinearLayout { + + public AboutLayout(Context context) { + super(context); + } + + public AboutLayout(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public AboutLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public AboutLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public void init() { + TextView version = findViewById(R.id.about_version_text); + version.setText(BuildConfig.VERSION_NAME + " (" + Integer.toString(BuildConfig.VERSION_CODE) + ")"); + } + + public static AboutLayout create(Context context) { + LayoutInflater layoutInflater = LayoutInflater.from(context); + AboutLayout about = (AboutLayout) layoutInflater.inflate(R.layout.about, null, false); + about.init(); + return about; + } +} diff --git a/app/src/main/java/ch/fixme/status/Main.java b/app/src/main/java/io/spaceapi/community/myhackerspace/Main.java similarity index 54% rename from app/src/main/java/ch/fixme/status/Main.java rename to app/src/main/java/io/spaceapi/community/myhackerspace/Main.java index 4581ac0..16f720b 100644 --- a/app/src/main/java/ch/fixme/status/Main.java +++ b/app/src/main/java/io/spaceapi/community/myhackerspace/Main.java @@ -1,9 +1,9 @@ /* * Copyright (C) 2012-2017 Aubort Jean-Baptiste (Rorist) + * Copyright (C) 2020-2023 Danilo Bargen (dbrgn) * Licensed under GNU's GPL 3, see README */ - -package ch.fixme.status; +package io.spaceapi.community.myhackerspace; import android.app.Activity; import android.app.AlertDialog; @@ -19,7 +19,6 @@ import android.graphics.drawable.AnimationDrawable; import android.net.ConnectivityManager; import android.net.NetworkInfo; -import android.net.Uri; import android.net.http.HttpResponseCache; import android.os.AsyncTask; import android.os.Bundle; @@ -32,8 +31,6 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.LinearLayout; @@ -42,6 +39,11 @@ import android.widget.SectionIndexer; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.annotation.UiThread; + import com.woozzu.android.util.StringMatcher; import com.woozzu.android.widget.IndexableListView; @@ -50,22 +52,37 @@ import java.io.File; import java.io.IOException; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; -import java.util.Locale; -import java.util.Map.Entry; -import java.util.Set; +import java.util.Objects; import java.util.regex.Pattern; -import androidx.annotation.UiThread; +import io.spaceapi.SpaceApiParser; +import io.spaceapi.types.AccountBalance; +import io.spaceapi.types.Barometer; +import io.spaceapi.types.BeverageSupply; +import io.spaceapi.types.DoorLocked; +import io.spaceapi.types.Humidity; +import io.spaceapi.types.MemberCount; +import io.spaceapi.types.NetworkConnection; +import io.spaceapi.types.PeoplePresent; +import io.spaceapi.types.PowerConsumption; +import io.spaceapi.types.Status; +import io.spaceapi.types.Temperature; public class Main extends Activity { + protected static final String TAG = "MyHackerspace"; // API: https://spaceapi.io/ - protected static final String TAG = "MyHackerspace"; + static final String API_DEFAULT = "https://fixme.ch/status.json"; + protected static final String PREF_API_URL_WIDGET = "api_url_widget_"; protected static final String PREF_LAST_WIDGET = "last_widget_"; protected static final String PREF_FORCE_WIDGET = "force_widget_"; @@ -74,14 +91,17 @@ public class Main extends Activity { protected static final String STATE_URL = "url"; private static final int DIALOG_LOADING = 0; private static final int DIALOG_LIST = 1; - private static final String TWITTER = "https://twitter.com/"; - private static final String FOURSQUARE = "https://foursquare.com/v/"; - private static final String MAP_SEARCH = "geo:0,0?q="; - private static final String MAP_COORD = "geo:%s,%s?z=23&q=%s&"; + private static final String MAP_COORD = "geo:%s,%s?z=23&q="; + + private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM); + // Shared preferences private SharedPreferences mPrefs; + // Hashmap with the endpoint URL as key and the endpoint JSON string as value private HashMap mResultHs; + // Contains directory endpoint JSON data as string public String mResultDir; + // The endpoint URL of the currently showing space private String mApiUrl; private boolean finishApi = false; private boolean finishDir = false; @@ -97,18 +117,25 @@ public class Main extends Activity { @UiThread public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + // Load layout setContentView(R.layout.main); + + // Hide views until loaded setViewVisibility(false); + + // Load shared prefs mPrefs = PreferenceManager.getDefaultSharedPreferences(Main.this); + + // Load data mResultHs = new HashMap<>(); - if (checkNetwork()) { - Log.d(TAG, "onCreate() intent="+ getIntent().toString()); + if (hasNetwork()) { + Log.d(TAG, "onCreate() intent=" + getIntent().toString()); setCache(); - getHsList(savedInstanceState); - showHsInfo(getIntent()); + getHsList(); + showHsInfo(getIntent(), true); } else { - showError(getString(R.string.error_title) + getString(R.string.error_network_title), - getString(R.string.error_network_msg)); + showNetworkError(); } } @@ -116,7 +143,7 @@ public void onCreate(Bundle savedInstanceState) { @UiThread protected void onNewIntent(Intent intent) { Log.d(TAG, "onNewIntent()=" + intent); - showHsInfo(intent); + showHsInfo(intent, false); } @Override @@ -143,24 +170,22 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_refresh: - if (checkNetwork()){ - showHsInfo(getIntent()); + final int id = item.getItemId(); + if (id == R.id.menu_refresh) { + if (hasNetwork()) { + showHsInfo(getIntent(), true); } else { - showError(getString(R.string.error_title) + getString(R.string.error_network_title), - getString(R.string.error_network_msg)); + showNetworkError(); } return true; - case R.id.menu_choose: + } else if (id == R.id.menu_choose) { showDialog(DIALOG_LIST); return true; - case R.id.menu_prefs: + } else if (id == R.id.menu_prefs) { startActivity(new Intent(Main.this, Prefs.class)); return true; - default: - return super.onOptionsItemSelected(item); } + return super.onOptionsItemSelected(item); } @Override @@ -194,14 +219,14 @@ protected void onSaveInstanceState(Bundle data) { @Override protected Dialog onCreateDialog(int id) { - AlertDialog dialog = null; + ProgressDialog dialog = null; switch (id) { case DIALOG_LOADING: dialog = new ProgressDialog(this); dialog.setCancelable(false); dialog.setMessage(getString(R.string.msg_loading)); dialog.setCancelable(true); - ((ProgressDialog) dialog).setIndeterminate(true); + dialog.setIndeterminate(true); break; case DIALOG_LIST: return createHsDialog(); @@ -209,27 +234,6 @@ protected Dialog onCreateDialog(int id) { return dialog; } - @Override - @UiThread - public void startActivity(Intent intent) { - // http://stackoverflow.com/questions/13691241/autolink-not-working-on-htc-htclinkifydispatcher - try { - /* First attempt at fixing an HTC broken by evil Apple patents. */ - if (intent.getComponent() != null - && ".HtcLinkifyDispatcherActivity".equals(intent - .getComponent().getShortClassName())) - intent.setComponent(null); - super.startActivity(intent); - } catch (ActivityNotFoundException e) { - /* - * Probably an HTC broken by evil Apple patents. This is not - * perfect, but better than crashing the whole application. - */ - Log.e(Main.TAG, e.getMessage()); - super.startActivity(Intent.createChooser(intent, null)); - } - } - private void setViewVisibility(boolean show) { int visibility1 = View.GONE; int visibility2 = View.VISIBLE; @@ -281,7 +285,7 @@ private AlertDialog createHsDialog() { edit.putString(Prefs.KEY_API_URL, url); getApiTask = new GetApiTask(); getApiTask.execute(url); - edit.commit(); + edit.apply(); setIntent(null); dismissDialog(DIALOG_LIST); Log.i(TAG, "Item clicked=" + url + " (" + position + ")"); @@ -296,7 +300,10 @@ private AlertDialog createHsDialog() { } } - private void getHsList(Bundle savedInstanceState) { + /** + * Fetch the directory and update the `mResultDir` variable. + */ + private void getHsList() { final Bundle data = (Bundle) getLastNonConfigurationInstance(); if (data == null) { Log.d(TAG, "getHsList(fresh data)"); @@ -310,35 +317,39 @@ private void getHsList(Bundle savedInstanceState) { } } - private void showHsInfo(Intent intent) { + /** + * Fetch the endpoint and update the `mApiUrl` and `mResultHs` variables. + */ + private void showHsInfo(@Nullable Intent intent, boolean skipCache) { final Bundle data = (Bundle) getLastNonConfigurationInstance(); - // Get hackerspace api url - if(data != null && data.containsKey(STATE_URL)) { + + // Get space endpoint URL + if (data != null && data.containsKey(STATE_URL)) { Log.d(TAG, "showHsInfo(uri from state)"); mApiUrl = data.getString(STATE_URL); - } else if (intent != null - && intent.hasExtra(AppWidgetManager.EXTRA_APPWIDGET_ID)) { + } else if (intent != null && intent.hasExtra(AppWidgetManager.EXTRA_APPWIDGET_ID)) { Log.d(TAG, "showHsInfo(uri from widget intent)"); mApiUrl = mPrefs.getString( PREF_API_URL_WIDGET + intent.getIntExtra( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID), - ParseGeneric.API_DEFAULT); + API_DEFAULT); } else if (intent != null && intent.hasExtra(STATE_HS)) { Log.d(TAG, "showHsInfo(uri from intent)"); mApiUrl = intent.getStringExtra(STATE_HS); } else { Log.d(TAG, "showHsInfo(uri from prefs)"); - mApiUrl = mPrefs.getString(Prefs.KEY_API_URL, ParseGeneric.API_DEFAULT); + mApiUrl = mPrefs.getString(Prefs.KEY_API_URL, API_DEFAULT); } - // Get Data - if(data != null && data.containsKey(STATE_HS)) { + + // Now that we have the URL, fetch the data + if (data != null && data.containsKey(STATE_HS)) { Log.d(TAG, "showHsInfo(data from state)"); finishApi = true; mResultHs = (HashMap) data.getSerializable(STATE_HS); populateDataHs(); - } else if(mResultHs.containsKey(mApiUrl)) { + } else if(mResultHs.containsKey(mApiUrl) && !skipCache) { Log.d(TAG, "showHsInfo(data from cache)"); finishApi = true; populateDataHs(); @@ -352,14 +363,20 @@ private void showHsInfo(Intent intent) { Widget.UpdateAllWidgets(getApplicationContext(), false); } - private boolean checkNetwork() { - return checkNetwork(getApplicationContext()); + /** + * Return whether the phone has an active network connection or not. + */ + private boolean hasNetwork() { + return hasNetwork(getApplicationContext()); } - protected static boolean checkNetwork(Context ctxt) { - ConnectivityManager cm = (ConnectivityManager) ctxt - .getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo netInfo = cm.getActiveNetworkInfo(); + /** + * Return whether the phone has an active network connection or not. + */ + protected static boolean hasNetwork(Context context) { + final ConnectivityManager cm = (ConnectivityManager) context + .getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo netInfo = Objects.requireNonNull(cm).getActiveNetworkInfo(); return netInfo != null && netInfo.isConnected(); } @@ -380,7 +397,7 @@ private void showError(String title, String msg) { private AlertDialog showError(String title, String msg, boolean ret) { if (title != null && msg != null) { AlertDialog dialog = new AlertDialog.Builder(Main.this) - .setTitle(getString(R.string.error_title) + title) + .setTitle(getString(R.string.error_title) + " " + title) .setMessage(msg) .setNeutralButton(getString(R.string.ok), null).create(); if (ret) { @@ -392,6 +409,10 @@ private AlertDialog showError(String title, String msg, boolean ret) { return null; } + private void showNetworkError() { + showError(getString(R.string.error_network_title), getString(R.string.error_network_msg)); + } + private void dismissLoading() { if (finishApi && finishDir) { try { @@ -476,7 +497,7 @@ protected void onPostExecute(String result) { dismissLoading(); if (mErrorMsg == null) { mResultHs.put(mUrl, result); - showHsInfo(getIntent()); + showHsInfo(getIntent(), false); } else { setViewVisibility(false); showError(mErrorTitle, mErrorMsg); @@ -540,24 +561,89 @@ private TextView addEntry(LayoutInflater inflater, LinearLayout vg, String value vg.addView(tv); return tv; } - private TextView addTitle(LayoutInflater inflater, LinearLayout vg, int value) { + + /** + * Shortcut function. If the value is not null, add it as an entry. + */ + private @Nullable TextView addEntryIfValueNotNull( + @NonNull LayoutInflater inflater, + @NonNull LinearLayout vg, + @Nullable @StringRes int title, + @Nullable Object value + ) { + if (value != null) { + return addEntry(inflater, vg, getString(title) + ": " + value.toString()); + } + return null; + } + + private TextView addTitle(LayoutInflater inflater, LinearLayout vg, @StringRes int value) { return addTitle(inflater, vg, getString(value)); } - private TextView addTitle(LayoutInflater inflater, LinearLayout vg, String value) { - TextView title = (TextView) inflater.inflate(R.layout.title, null); + private TextView addTitle(LayoutInflater inflater, LinearLayout vg, @NonNull String value) { + final TextView title = (TextView) inflater.inflate(R.layout.title, null); title.setText(value); vg.addView(title); inflater.inflate(R.layout.separator, vg); return title; } + private TextView addSubtitle(LayoutInflater inflater, LinearLayout vg, @StringRes int value) { + return addSubtitle(inflater, vg, getString(value)); + } + + private TextView addSubtitle(LayoutInflater inflater, LinearLayout vg, @NonNull String value) { + final TextView title = (TextView) inflater.inflate(R.layout.subtitle, null); + title.setText(value); + vg.addView(title); + return title; + } + + /** + * Add a sensor with the "sensor" layout. + */ + private void addSensor( + @NonNull LayoutInflater inflater, + @NonNull LinearLayout vg, + @NonNull String value, + @Nullable String details1, + @Nullable String details2 + ) { + final RelativeLayout rl = (RelativeLayout) inflater.inflate(R.layout.entry_sensor, null); + final TextView viewValue = rl.findViewById(R.id.entry_value); + final TextView viewDetails1 = rl.findViewById(R.id.entry_details1); + final TextView viewDetails2 = rl.findViewById(R.id.entry_details2); + viewValue.setText(value); + if (details1 != null) { + viewDetails1.setText(details1); + } else { + viewDetails1.setVisibility(View.GONE); + } + if (details2 != null) { + viewDetails2.setText(details2); + } else { + viewDetails2.setVisibility(View.GONE); + } + vg.addView(rl); + } + + /** + * Parse the endpoint JSON and populate UI with parsed information. + */ private void populateDataHs() { try { Log.i(TAG, "populateDataHs()=" + mApiUrl); setViewVisibility(false); - HashMap data = new ParseGeneric(mResultHs.get(mApiUrl)) - .getData(); + + // Look up endpoint JSOn + final String endpointJson = mResultHs.get(mApiUrl); + if (endpointJson == null) { + throw new IllegalStateException("Endpoint JSON not found"); + } + + // Parse the JSON string using the `spaceapi-kt` library. + final Status data = SpaceApiParser.parseString(endpointJson); // Initialize views LayoutInflater iftr = getLayoutInflater(); @@ -567,234 +653,261 @@ private void populateDataHs() { scroll.addView(vg); // Mandatory fields - ((TextView) findViewById(R.id.space_name)).setText((String) data - .get(ParseGeneric.API_NAME)); - ((TextView) findViewById(R.id.space_url)).setText((String) data - .get(ParseGeneric.API_URL)); + ((TextView) findViewById(R.id.space_name)).setText(data.space); + ((TextView) findViewById(R.id.space_url)).setText(data.url.toString()); getImageTask = new GetImage(R.id.space_image); - getImageTask.execute((String) data.get(ParseGeneric.API_LOGO)); + getImageTask.execute(data.logo); // Status text String status_txt; - if (data.get(ParseGeneric.API_STATUS) == null) { + if (data.state == null) { status_txt = getString(R.string.status_unknown); ((TextView) findViewById(R.id.status_txt)) - .setCompoundDrawablesWithIntrinsicBounds( - android.R.drawable.presence_invisible, 0, 0, 0); - } else if ((Boolean) data.get(ParseGeneric.API_STATUS)) { + .setCompoundDrawablesWithIntrinsicBounds( + android.R.drawable.presence_invisible, 0, 0, 0); + } else if (Boolean.TRUE.equals(data.state.open)) { status_txt = getString(R.string.status_open); ((TextView) findViewById(R.id.status_txt)) - .setCompoundDrawablesWithIntrinsicBounds( - android.R.drawable.presence_online, 0, 0, 0); + .setCompoundDrawablesWithIntrinsicBounds( + android.R.drawable.presence_online, 0, 0, 0); } else { status_txt = getString(R.string.status_closed); ((TextView) findViewById(R.id.status_txt)) - .setCompoundDrawablesWithIntrinsicBounds( - android.R.drawable.presence_busy, 0, 0, 0); + .setCompoundDrawablesWithIntrinsicBounds( + android.R.drawable.presence_busy, 0, 0, 0); } - if (data.containsKey(ParseGeneric.API_STATUS_TXT)) { - status_txt += ": " - + data.get(ParseGeneric.API_STATUS_TXT); + if (data.state.message != null) { + status_txt += ": " + data.state.message; } ((TextView) findViewById(R.id.status_txt)).setText(status_txt); // Status last change - if (data.containsKey(ParseGeneric.API_LASTCHANGE)) { - addEntry(iftr, vg, getString(R.string.api_lastchange) + " " - + data.get(ParseGeneric.API_LASTCHANGE)); - } - - // Status duration - if (data.containsKey(ParseGeneric.API_EXT_DURATION) - && data.get(ParseGeneric.API_STATUS) != null - && (Boolean) data.get(ParseGeneric.API_STATUS)) { - addEntry(iftr, vg, getString(R.string.api_duration) + " " - + data.get(ParseGeneric.API_EXT_DURATION) - + getString(R.string.api_duration_hours)); + if (data.state != null && data.state.lastchange != null) { + final ZonedDateTime lastchange = data.state.lastchange.atZone(ZoneId.systemDefault()); + final String lastchangeString = lastchange.format(this.dateTimeFormatter); + addEntry(iftr, vg, getString(R.string.api_lastchange) + " " + lastchangeString); } // Location - Pattern ptn = Pattern.compile("^.*$", Pattern.DOTALL); - if (data.containsKey(ParseGeneric.API_ADDRESS) - || data.containsKey(ParseGeneric.API_LON)) { - - addTitle(iftr, vg, R.string.api_location); - - // Address - if (data.containsKey(ParseGeneric.API_ADDRESS)) { - TextView tv = addEntry(iftr, vg, (String) data.get(ParseGeneric.API_ADDRESS)); - Linkify.addLinks(tv, ptn, MAP_SEARCH); - } - // Lon/Lat - if (data.containsKey(ParseGeneric.API_LON) - && data.containsKey(ParseGeneric.API_LAT)) { - TextView tv = addEntry(iftr, vg, data.get(ParseGeneric.API_LON) + ", " - + data.get(ParseGeneric.API_LAT)); - String addr = (data.containsKey(ParseGeneric.API_ADDRESS)) ? (String) data - .get(ParseGeneric.API_ADDRESS) : getString(R.string.empty); - Linkify.addLinks(tv, ptn, String.format(MAP_COORD, - data.get(ParseGeneric.API_LAT), data.get(ParseGeneric.API_LON), addr)); - } + addTitle(iftr, vg, R.string.api_location); + TextView latLonTv = addEntry(iftr, vg, data.location.lat + ", " + data.location.lon); + Linkify.addLinks( + latLonTv, + Pattern.compile("^.*$", Pattern.DOTALL), + String.format(MAP_COORD, data.location.lat, data.location.lon) + ); + + // Postal address + if (data.location.address != null) { + addTitle(iftr, vg, R.string.api_postal_addr); + addEntry(iftr, vg, data.location.address); } // Contact - if (data.containsKey(ParseGeneric.API_PHONE) - || data.containsKey(ParseGeneric.API_TWITTER) - || data.containsKey(ParseGeneric.API_IRC) - || data.containsKey(ParseGeneric.API_EMAIL) - || data.containsKey(ParseGeneric.API_ML)) { - - addTitle(iftr, vg, R.string.api_contact); - - if (data.containsKey(ParseGeneric.API_PHONE)) { - addEntry(iftr, vg, (String) data.get(ParseGeneric.API_PHONE)); - } - if (data.containsKey(ParseGeneric.API_SIP)) { - addEntry(iftr, vg, (String) data.get(ParseGeneric.API_SIP)); - } - if (data.containsKey(ParseGeneric.API_TWITTER)) { - addEntry(iftr, vg, TWITTER - + ((String) data.get(ParseGeneric.API_TWITTER)).replace("@", "")); + addTitle(iftr, vg, R.string.api_contact); + addEntryIfValueNotNull(iftr, vg, R.string.api_contact_phone, data.contact.phone); + addEntryIfValueNotNull(iftr, vg, R.string.api_contact_sip, data.contact.sip); + addEntryIfValueNotNull(iftr, vg, R.string.api_contact_email, data.contact.email); + addEntryIfValueNotNull(iftr, vg, R.string.api_contact_ml, data.contact.ml); + addEntryIfValueNotNull(iftr, vg, R.string.api_contact_irc, data.contact.irc); + addEntryIfValueNotNull(iftr, vg, R.string.api_contact_twitter, data.contact.twitter); + addEntryIfValueNotNull(iftr, vg, R.string.api_contact_identica, data.contact.identica); + addEntryIfValueNotNull(iftr, vg, R.string.api_contact_mastodon, data.contact.mastodon); + addEntryIfValueNotNull(iftr, vg, R.string.api_contact_facebook, data.contact.facebook); // Eeeew! + addEntryIfValueNotNull(iftr, vg, R.string.api_contact_foursquare, data.contact.foursquare); + addEntryIfValueNotNull(iftr, vg, R.string.api_contact_xmpp, data.contact.xmpp); + addEntryIfValueNotNull(iftr, vg, R.string.api_contact_gopher, data.contact.gopher); + addEntryIfValueNotNull(iftr, vg, R.string.api_contact_matrix, data.contact.matrix); + addEntryIfValueNotNull(iftr, vg, R.string.api_contact_mumble, data.contact.mumble); + + // Sensors: Space + if (data.sensors != null && ( + data.sensors.people_now_present.length > 0 + || data.sensors.door_locked.length > 0 + || data.sensors.beverage_supply.length > 0 + || data.sensors.power_consumption.length > 0 + || data.sensors.network_connections.length > 0 + /*|| data.sensors.network_traffic.length > 0*/ + )) { + addTitle(iftr, vg, getString(R.string.api_sensors) + ": " + getString(R.string.api_sensors_space)); + + // People present + if (data.sensors.people_now_present.length > 0) { + addSubtitle(iftr, vg, R.string.api_sensor_people_now_present); + for (PeoplePresent entry : data.sensors.people_now_present) { + addSensor( + iftr, + vg, + String.format("%d", entry.value), + Utils.joinStrings(" / ", entry.location, entry.name), + entry.description + ); + } } - if (data.containsKey(ParseGeneric.API_IDENTICA)) { - addEntry(iftr, vg, (String) data.get(ParseGeneric.API_IDENTICA)); + + // Door status + if (data.sensors.door_locked.length > 0) { + addSubtitle(iftr, vg, R.string.api_sensor_door_locked); + for (DoorLocked entry : data.sensors.door_locked) { + addSensor( + iftr, + vg, + getString(entry.value ? R.string.api_sensor_door_locked_yes : R.string.api_sensor_door_locked_no), + Utils.joinStrings(" / ", entry.location, entry.name), + entry.description + ); + } } - if (data.containsKey(ParseGeneric.API_FOURSQUARE)) { - addEntry(iftr, vg, FOURSQUARE + data.get(ParseGeneric.API_FOURSQUARE)); + + // Beverage supply + if (data.sensors.beverage_supply.length > 0) { + addSubtitle(iftr, vg, R.string.api_sensor_beverage_supply); + for (BeverageSupply entry : data.sensors.beverage_supply) { + addSensor( + iftr, + vg, + String.format("%.1f %s", entry.value, entry.unit), + Utils.joinStrings(" / ", entry.location, entry.name), + entry.description + ); + } } - if (data.containsKey(ParseGeneric.API_IRC)) { - addEntry(iftr, vg, (String) data.get(ParseGeneric.API_IRC)); + + // Power consumption + if (data.sensors.power_consumption.length > 0) { + addSubtitle(iftr, vg, R.string.api_sensor_power_consumption); + for (PowerConsumption entry : data.sensors.power_consumption) { + addSensor( + iftr, + vg, + String.format("%.1f %s", entry.value, entry.unit), + Utils.joinStrings(" / ", entry.location, entry.name), + entry.description + ); + } } - if (data.containsKey(ParseGeneric.API_EMAIL)) { - addEntry(iftr, vg, (String) data.get(ParseGeneric.API_EMAIL)); + + // Network connections + if (data.sensors.network_connections.length > 0) { + addSubtitle(iftr, vg, R.string.api_sensor_network_connections); + for (NetworkConnection entry : data.sensors.network_connections) { + String details = Utils.joinStrings(" / ", entry.location, entry.name); + if (details != null && entry.type != null) { + details += " (" + entry.type + ")"; + } else if (entry.type != null) { + details = entry.type; + } + addSensor( + iftr, + vg, + String.format("%d", entry.value), + details, + entry.description + ); + } } - if (data.containsKey(ParseGeneric.API_JABBER)) { - addEntry(iftr, vg, (String) data.get(ParseGeneric.API_JABBER)); + } + + // Sensors: Environment + if (data.sensors != null && ( + data.sensors.temperature.length > 0 + || data.sensors.humidity.length > 0 + || data.sensors.barometer.length > 0 + || data.sensors.wind.length > 0 + )) { + addTitle(iftr, vg, getString(R.string.api_sensors) + ": " + getString(R.string.api_sensors_environment)); + + // Temperature + if (data.sensors.temperature.length > 0) { + addSubtitle(iftr, vg, R.string.api_sensor_temperature); + for (Temperature entry : data.sensors.temperature) { + addSensor( + iftr, + vg, + String.format("%.1f %s", entry.value, entry.unit), + Utils.joinStrings(" / ", entry.location, entry.name), + entry.description + ); + } } - if (data.containsKey(ParseGeneric.API_ML)) { - addEntry(iftr, vg, (String) data.get(ParseGeneric.API_ML)); + + // Humidity + if (data.sensors.humidity.length > 0) { + addSubtitle(iftr, vg, R.string.api_sensor_humidity); + for (Humidity entry : data.sensors.humidity) { + addSensor( + iftr, + vg, + String.format("%.1f %s", entry.value, entry.unit), + Utils.joinStrings(" / ", entry.location, entry.name), + entry.description + ); + } } - } - // Sensors - if (data.containsKey(ParseGeneric.API_SENSORS)) { - - addTitle(iftr, vg, R.string.api_sensors); - - HashMap>> sensors = - (HashMap>>) - data.get(ParseGeneric.API_SENSORS); - Set names = sensors.keySet(); - for (String name : names) { - - // Subtitle - String name_title = name.toLowerCase(Locale.getDefault()).replace("_", " "); - name_title = name_title.substring(0, 1).toUpperCase(Locale.getDefault()) - + name_title.substring(1, name_title.length()); - TextView subtitle = (TextView) iftr.inflate( - R.layout.subtitle, null); - subtitle.setText(name_title); - vg.addView(subtitle); - - // Sensors data - ArrayList> sensorsData = sensors - .get(name); - for (HashMap elem : sensorsData) { - RelativeLayout rl = (RelativeLayout) iftr.inflate( - R.layout.entry_sensor, null); - if (elem.containsKey(ParseGeneric.API_SENSOR_VALUE)) { - ((TextView) rl.findViewById(R.id.entry_value)) - .setText(elem.get(ParseGeneric.API_SENSOR_VALUE)); - } else { - rl.findViewById(R.id.entry_value).setVisibility( - View.GONE); - } - if (elem.containsKey(ParseGeneric.API_SENSOR_UNIT)) { - ((TextView) rl.findViewById(R.id.entry_unit)) - .setText(elem.get(ParseGeneric.API_SENSOR_UNIT)); - } else { - rl.findViewById(R.id.entry_unit).setVisibility( - View.GONE); - } - if (elem.containsKey(ParseGeneric.API_SENSOR_NAME)) { - ((TextView) rl.findViewById(R.id.entry_name)) - .setText(elem.get(ParseGeneric.API_SENSOR_NAME)); - } else { - rl.findViewById(R.id.entry_name).setVisibility( - View.GONE); - } - if (elem.containsKey(ParseGeneric.API_SENSOR_LOCATION)) { - ((TextView) rl.findViewById(R.id.entry_location)) - .setText(elem - .get(ParseGeneric.API_SENSOR_LOCATION)); - } else { - rl.findViewById(R.id.entry_location).setVisibility( - View.GONE); - } - if (elem.containsKey(ParseGeneric.API_SENSOR_DESCRIPTION)) { - ((TextView) rl.findViewById(R.id.entry_description)) - .setText(elem - .get(ParseGeneric.API_SENSOR_DESCRIPTION)); - } else { - rl.findViewById(R.id.entry_description) - .setVisibility(View.GONE); - } - if (elem.containsKey(ParseGeneric.API_SENSOR_PROPERTIES)) { - ((TextView) rl.findViewById(R.id.entry_properties)) - .setText(elem - .get(ParseGeneric.API_SENSOR_PROPERTIES)); - } else { - rl.findViewById(R.id.entry_properties) - .setVisibility(View.GONE); - } - if (elem.containsKey(ParseGeneric.API_SENSOR_MACHINES)) { - ((TextView) rl.findViewById(R.id.entry_other)) - .setText(elem - .get(ParseGeneric.API_SENSOR_MACHINES)); - } else if (elem.containsKey(ParseGeneric.API_SENSOR_NAMES)) { - ((TextView) rl.findViewById(R.id.entry_other)) - .setText(elem.get(ParseGeneric.API_SENSOR_NAMES)); - } else { - rl.findViewById(R.id.entry_other).setVisibility( - View.GONE); - } - vg.addView(rl); + // Air pressure + if (data.sensors.barometer.length > 0) { + addSubtitle(iftr, vg, R.string.api_sensor_barometer); + for (Barometer entry : data.sensors.barometer) { + addSensor( + iftr, + vg, + String.format("%.1f %s", entry.value, entry.unit), + Utils.joinStrings(" / ", entry.location, entry.name), + entry.description + ); } } + + // Missing: Wind (not used by any space right now) + // and Radiation (I don't know how to interpret and visualize the measurements in a meaningful way) } - // Stream and cam - if (data.containsKey(ParseGeneric.API_STREAM) - || data.containsKey(ParseGeneric.API_CAM)) { - - addTitle(iftr, vg, R.string.api_stream); - - // Stream - if (data.containsKey(ParseGeneric.API_STREAM)) { - HashMap stream = (HashMap) data - .get(ParseGeneric.API_STREAM); - for (Entry entry : stream.entrySet()) { - final String type = entry.getKey(); - final String url = entry.getValue(); - TextView tv = addEntry(iftr, vg, url); - tv.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - Intent i = new Intent(Intent.ACTION_VIEW); - i.setDataAndType(Uri.parse(url), type); - startActivity(i); - } - }); + // Sensors: Organization + if (data.sensors != null && ( + data.sensors.total_member_count.length > 0 + || data.sensors.account_balance.length > 0 + )) { + addTitle(iftr, vg, getString(R.string.api_sensors) + ": " + getString(R.string.api_sensors_organization)); + + // Total Member Count + if (data.sensors.total_member_count.length > 0) { + addSubtitle(iftr, vg, R.string.api_sensor_total_member_count); + for (MemberCount entry : data.sensors.total_member_count) { + addSensor( + iftr, + vg, + String.format("%d", entry.value), + Utils.joinStrings(" / ", entry.location, entry.name), + entry.description + ); } } - // Cam - if (data.containsKey(ParseGeneric.API_CAM)) { - ArrayList cams = (ArrayList) data - .get(ParseGeneric.API_CAM); - for (String value : cams) { - addEntry(iftr, vg, value); + + // Account balance + if (data.sensors.account_balance.length > 0) { + addSubtitle(iftr, vg, R.string.api_sensor_account_balance); + for (AccountBalance entry : data.sensors.account_balance) { + addSensor( + iftr, + vg, + String.format("%.2f %s", entry.value, entry.unit), + Utils.joinStrings(" / ", entry.location, entry.name), + entry.description + ); } } } + + // Webcams + if (data.cam.length > 0) { + addTitle(iftr, vg, R.string.api_webcams); + for (String url : data.cam) { + final TextView tv = addEntry(iftr, vg, url); + Linkify.addLinks(tv, Pattern.compile("^.*$", Pattern.DOTALL), null); + } + } + setViewVisibility(true); } catch (Exception e) { e.printStackTrace(); diff --git a/app/src/main/java/ch/fixme/status/Net.java b/app/src/main/java/io/spaceapi/community/myhackerspace/Net.java similarity index 97% rename from app/src/main/java/ch/fixme/status/Net.java rename to app/src/main/java/io/spaceapi/community/myhackerspace/Net.java index 628bac3..f0056c5 100644 --- a/app/src/main/java/ch/fixme/status/Net.java +++ b/app/src/main/java/io/spaceapi/community/myhackerspace/Net.java @@ -1,9 +1,9 @@ /* * Copyright (C) 2012-2017 Aubort Jean-Baptiste (Rorist) + * Copyright (C) 2020-2023 Danilo Bargen (dbrgn) * Licensed under GNU's GPL 3, see README */ - -package ch.fixme.status; +package io.spaceapi.community.myhackerspace; import android.graphics.Bitmap; import android.graphics.BitmapFactory; diff --git a/app/src/main/java/ch/fixme/status/Network.java b/app/src/main/java/io/spaceapi/community/myhackerspace/Network.java similarity index 81% rename from app/src/main/java/ch/fixme/status/Network.java rename to app/src/main/java/io/spaceapi/community/myhackerspace/Network.java index 24f8277..9bb1df2 100644 --- a/app/src/main/java/ch/fixme/status/Network.java +++ b/app/src/main/java/io/spaceapi/community/myhackerspace/Network.java @@ -1,8 +1,9 @@ /* * Copyright (C) 2012-2017 Aubort Jean-Baptiste (Rorist) + * Copyright (C) 2020-2023 Danilo Bargen (dbrgn) * Licensed under GNU's GPL 3, see README */ -package ch.fixme.status; +package io.spaceapi.community.myhackerspace; import android.content.BroadcastReceiver; import android.content.Context; @@ -13,7 +14,7 @@ public class Network extends BroadcastReceiver { @Override public void onReceive(Context ctxt, Intent intent) { - if (Main.checkNetwork(ctxt)) { + if (Main.hasNetwork(ctxt)) { Log.i(Main.TAG, "Update widget on " + intent.getAction()); Widget.UpdateAllWidgets(ctxt, true); } else { diff --git a/app/src/main/java/ch/fixme/status/Prefs.java b/app/src/main/java/io/spaceapi/community/myhackerspace/Prefs.java similarity index 77% rename from app/src/main/java/ch/fixme/status/Prefs.java rename to app/src/main/java/io/spaceapi/community/myhackerspace/Prefs.java index 84b4767..03cc2e2 100644 --- a/app/src/main/java/ch/fixme/status/Prefs.java +++ b/app/src/main/java/io/spaceapi/community/myhackerspace/Prefs.java @@ -1,20 +1,26 @@ /* * Copyright (C) 2012-2017 Aubort Jean-Baptiste (Rorist) + * Copyright (C) 2020-2023 Danilo Bargen (dbrgn) * Licensed under GNU's GPL 3, see README */ -package ch.fixme.status; +package io.spaceapi.community.myhackerspace; + +import static android.view.ViewGroup.LayoutParams.*; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.os.Bundle; import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.widget.LinearLayout; public class Prefs extends PreferenceActivity implements OnSharedPreferenceChangeListener { public static final String KEY_API_ENDPOINT = "api_endpoint"; - public static final String DEFAULT_API_ENDPOINT = "https://directory.spaceapi.io/"; + public static final String DEFAULT_API_ENDPOINT = "https://raw.githubusercontent.com/SpaceApi/directory/master/directory.json"; public static final String KEY_API_URL = "apiurl"; @@ -30,6 +36,7 @@ public class Prefs extends PreferenceActivity implements public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); + this.getListView().addFooterView(AboutLayout.create(this)); // tried addContentView PreferenceScreen ps = getPreferenceScreen(); ps.getSharedPreferences() .registerOnSharedPreferenceChangeListener(this); diff --git a/app/src/main/java/io/spaceapi/community/myhackerspace/Utils.java b/app/src/main/java/io/spaceapi/community/myhackerspace/Utils.java new file mode 100644 index 0000000..cc70159 --- /dev/null +++ b/app/src/main/java/io/spaceapi/community/myhackerspace/Utils.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2020-2023 Danilo Bargen (dbrgn) + * Licensed under GNU's GPL 3, see README + */ +package io.spaceapi.community.myhackerspace; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class Utils { + /** + * Join the strings using the specified separator. + */ + public static @Nullable String joinStrings(@NonNull String separator, String... strings) { + final StringBuilder builder = new StringBuilder(); + boolean empty = true; + for (String string : strings) { + if (string != null) { + if (empty) { + builder.append(string); + empty = false; + } else { + builder.append(separator).append(string); + } + } + } + return empty ? null : builder.toString(); + } +} diff --git a/app/src/main/java/ch/fixme/status/Widget.java b/app/src/main/java/io/spaceapi/community/myhackerspace/Widget.java similarity index 76% rename from app/src/main/java/ch/fixme/status/Widget.java rename to app/src/main/java/io/spaceapi/community/myhackerspace/Widget.java index a78e849..52670e1 100644 --- a/app/src/main/java/ch/fixme/status/Widget.java +++ b/app/src/main/java/io/spaceapi/community/myhackerspace/Widget.java @@ -1,9 +1,9 @@ /* * Copyright (C) 2012-2017 Aubort Jean-Baptiste (Rorist) + * Copyright (C) 2020-2023 Danilo Bargen (dbrgn) * Licensed under GNU's GPL 3, see README */ - -package ch.fixme.status; +package io.spaceapi.community.myhackerspace; import android.app.AlarmManager; import android.app.IntentService; @@ -17,6 +17,7 @@ import android.content.SharedPreferences.Editor; import android.graphics.Bitmap; import android.os.AsyncTask; +import android.os.Build; import android.os.Handler; import android.os.Looper; import android.preference.PreferenceManager; @@ -25,10 +26,12 @@ import android.widget.RemoteViews; import android.widget.Toast; -import org.json.JSONException; - import java.lang.ref.WeakReference; -import java.util.HashMap; +import java.net.MalformedURLException; +import java.net.URL; + +import io.spaceapi.ParseError; +import io.spaceapi.SpaceApiParser; public class Widget extends AppWidgetProvider { @@ -36,6 +39,18 @@ public class Widget extends AppWidgetProvider { static final String WIDGET_IDS = "widget_ids"; static final String WIDGET_FORCE = "widget_force"; + public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + // see https://intrepidgeeks.com/tutorial/android-app-widgets + final int N = appWidgetIds.length; + + // Perform this loop procedure for each App Widget that belongs to this provider + for (int i=0; i= Build.VERSION_CODES.S ? PendingIntent.FLAG_MUTABLE : 0; + } + protected static Intent getIntent(Context ctxt, int widgetId) { Intent i = new Intent(ctxt, UpdateService.class); i.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId); @@ -102,15 +121,16 @@ protected static void setAlarm(Context ctxt, Intent i, int widgetId, // Set alarm AlarmManager am = (AlarmManager) ctxt .getSystemService(Context.ALARM_SERVICE); - PendingIntent pi = PendingIntent.getService(ctxt, widgetId, i, 0); + PendingIntent pi = PendingIntent.getService(ctxt, widgetId, i, getPendingIntentMutableFlag()); am.cancel(pi); am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, delay, update_interval, pi); // Log.i(TAG, "start notification every " + update_interval / 1000 // + "s"); + startWidgetUpdateTask(ctxt, widgetId); } - private static class GetImage extends AsyncTask { + private static class GetImage extends AsyncTask { private final int mId; private WeakReference mCtxt; @@ -124,9 +144,9 @@ public GetImage(Context ctxt, int id, String text) { } @Override - protected Bitmap doInBackground(String... url) { + protected Bitmap doInBackground(URL... url) { try { - return new Net(url[0]).getBitmap(); + return new Net(url[0].toString()).getBitmap(); } catch (Throwable e) { e.printStackTrace(); mError = e.getMessage(); @@ -186,7 +206,7 @@ protected static void updateWidget(final Context ctxt, int widgetId, Intent clickIntent = new Intent(ctxt, Main.class); clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId); PendingIntent pendingIntent = PendingIntent.getActivity(ctxt, widgetId, - clickIntent, PendingIntent.FLAG_CANCEL_CURRENT); + clickIntent, PendingIntent.FLAG_CANCEL_CURRENT + getPendingIntentMutableFlag()); views.setOnClickPendingIntent(R.id.widget_image, pendingIntent); manager.updateAppWidget(widgetId, views); } @@ -228,60 +248,50 @@ protected void onCancelled() { } @Override - protected void onPostExecute(String result) { + protected void onPostExecute(String endpointJson) { final Context ctxt = mCtxt.get(); if(ctxt == null) { Log.e(TAG, "Context error (postExecute)"); return; } try { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(ctxt); + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctxt); + + final io.spaceapi.types.Status data = SpaceApiParser.parseString(endpointJson); + + boolean statusBool = data.state != null && data.state.open; - HashMap api = new ParseGeneric(result) - .getData(); - boolean statusBool = (Boolean) api.get(ParseGeneric.API_STATUS); // Update only if different than last status or not forced if (prefs.contains(Main.PREF_LAST_WIDGET + mId) && prefs.getBoolean(Main.PREF_LAST_WIDGET + mId, false) == statusBool - && !prefs.getBoolean(Main.PREF_FORCE_WIDGET + mId, - false)) { + && !prefs.getBoolean(Main.PREF_FORCE_WIDGET + mId, false)) { Log.d(TAG, "Nothing to update"); return; } // Mandatory fields - String status = ParseGeneric.API_ICON - + ParseGeneric.API_ICON_CLOSED; - if (statusBool) { - status = ParseGeneric.API_ICON + ParseGeneric.API_ICON_OPEN; - } - Editor edit = prefs.edit(); + final Editor edit = prefs.edit(); edit.putBoolean(Main.PREF_LAST_WIDGET + mId, statusBool); edit.apply(); String status_text = null; - if (prefs.getBoolean(Prefs.KEY_WIDGET_TEXT, - Prefs.DEFAULT_WIDGET_TEXT)) { - if (api.containsKey(ParseGeneric.API_STATUS_TXT)) { - status_text = (String) api - .get(ParseGeneric.API_STATUS_TXT); + if (prefs.getBoolean(Prefs.KEY_WIDGET_TEXT, Prefs.DEFAULT_WIDGET_TEXT)) { + if (data.state != null && data.state.message != null) { + status_text = data.state.message; } else { - status_text = statusBool ? ctxt.getString(R.string.status_open) : - ctxt.getString(R.string.status_closed); + status_text = statusBool + ? ctxt.getString(R.string.status_open) + : ctxt.getString(R.string.status_closed); } } // Status icon or space icon - if (api.containsKey(ParseGeneric.API_ICON - + ParseGeneric.API_ICON_OPEN) - && api.containsKey(ParseGeneric.API_ICON - + ParseGeneric.API_ICON_CLOSED)) { - new GetImage(ctxt, mId, status_text).execute((String) api - .get(status)); + if (data.state != null && data.state.icon != null) { + new GetImage(ctxt, mId, status_text).execute( + statusBool? new URL(data.state.icon.open) : new URL(data.state.icon.closed) + ); } else { - new GetImage(ctxt, mId, status_text).execute((String) api - .get(ParseGeneric.API_LOGO)); + new GetImage(ctxt, mId, status_text).execute(new URL(data.logo)); } - } catch (JSONException e) { + } catch (ParseError | MalformedURLException e) { e.printStackTrace(); String msg = e.getMessage(); printMessage(ctxt, msg); @@ -297,24 +307,26 @@ public UpdateService() { @Override protected void onHandleIntent(Intent intent) { - final Context ctxt = UpdateService.this; + final Context context = UpdateService.this; final int widgetId = intent.getIntExtra( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(ctxt); - if (Main.checkNetwork(ctxt) && prefs.contains(Main.PREF_API_URL_WIDGET + widgetId)) { - final String url = prefs.getString(Main.PREF_API_URL_WIDGET - + widgetId, ParseGeneric.API_DEFAULT); - Log.i(TAG, "Update widgetid " + widgetId + " with url " - + url); - new Handler(Looper.getMainLooper()) - .post(() -> new GetApiTask(ctxt, widgetId).execute(url)); - } + Widget.startWidgetUpdateTask(context, widgetId); stopSelf(); } } + private static void startWidgetUpdateTask(Context context, int widgetId) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(context); + if (Main.hasNetwork(context) && prefs.contains(Main.PREF_API_URL_WIDGET + widgetId)) { + final String url = prefs.getString(Main.PREF_API_URL_WIDGET + widgetId, Main.API_DEFAULT); + Log.i(TAG, "Update widgetId " + widgetId + " with url " + url); + new Handler(Looper.getMainLooper()) + .post(() -> new GetApiTask(context, widgetId).execute(url)); + } + } + public static void UpdateAllWidgets(final Context ctxt, boolean force) { AppWidgetManager man = AppWidgetManager.getInstance(ctxt); int[] ids = man.getAppWidgetIds(new ComponentName(ctxt, Widget.class)); diff --git a/app/src/main/java/ch/fixme/status/Widget_config.java b/app/src/main/java/io/spaceapi/community/myhackerspace/Widget_config.java similarity index 98% rename from app/src/main/java/ch/fixme/status/Widget_config.java rename to app/src/main/java/io/spaceapi/community/myhackerspace/Widget_config.java index f556e4a..c4789dc 100644 --- a/app/src/main/java/ch/fixme/status/Widget_config.java +++ b/app/src/main/java/io/spaceapi/community/myhackerspace/Widget_config.java @@ -1,9 +1,9 @@ /* * Copyright (C) 2012-2017 Aubort Jean-Baptiste (Rorist) + * Copyright (C) 2020-2023 Danilo Bargen (dbrgn) * Licensed under GNU's GPL 3, see README */ - -package ch.fixme.status; +package io.spaceapi.community.myhackerspace; import android.app.Activity; import android.app.AlertDialog; diff --git a/app/src/main/res/drawable-hdpi/myhs.png b/app/src/main/res/drawable-hdpi/myhs.png deleted file mode 100644 index 00bb648..0000000 Binary files a/app/src/main/res/drawable-hdpi/myhs.png and /dev/null differ diff --git a/app/src/main/res/drawable-ldpi/myhs.png b/app/src/main/res/drawable-ldpi/myhs.png deleted file mode 100644 index 2f686fe..0000000 Binary files a/app/src/main/res/drawable-ldpi/myhs.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/myhs.png b/app/src/main/res/drawable-mdpi/myhs.png deleted file mode 100644 index 436b4f9..0000000 Binary files a/app/src/main/res/drawable-mdpi/myhs.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/myhs.png b/app/src/main/res/drawable-xhdpi/myhs.png deleted file mode 100644 index 3ac90ae..0000000 Binary files a/app/src/main/res/drawable-xhdpi/myhs.png and /dev/null differ diff --git a/app/src/main/res/layout/about.xml b/app/src/main/res/layout/about.xml new file mode 100644 index 0000000..121e203 --- /dev/null +++ b/app/src/main/res/layout/about.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/entry_sensor.xml b/app/src/main/res/layout/entry_sensor.xml index fea2769..5653719 100644 --- a/app/src/main/res/layout/entry_sensor.xml +++ b/app/src/main/res/layout/entry_sensor.xml @@ -1,6 +1,6 @@ + android:text="Value" /> - - - - + android:text="More details" /> 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..036d09b --- /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..036d09b --- /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.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..51a1765 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..7a1de26 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..a872b3e Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..0b9b46a Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..ae330ed Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..16a7fbf Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..4802fb7 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..3d8fc7d Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..208bf3a Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..db8b832 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..69ea51a Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..1cab33c Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..ea2662d Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..4d8d80a Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..7d8bad1 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 2b32228..33caa69 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -34,7 +34,7 @@ Netværket er ikke tilgængeligt \n\nKontroller at det aktive SpaceAPI overholder den revision det annoncerer!\n\nSe https://spaceapi.io/ - Rediger OpenSpaceDirectory-URL + Rediger SpaceAPI-Directory-URL URL til den globale SpaceAPI-fortegnelse der skal anvendes (directory.json fil)\nEksempler:\nhttps://directory.spaceapi.io/ Rediger SpaceAPI-URL Ændr URL for det aktive SpaceAPI (mistes ved valg af hackerspace fra fortegnelsen) @@ -51,7 +51,7 @@ time(r) Lokation Kontakt - Videostrøm + Webkameraer Sensorer diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index e863ae4..8aea1fc 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -13,8 +13,8 @@ Licensed under GNU's GPL 3, see README OK - Öffnen - Abgeschlossen + Offen + Geschlossen Unbekannt Logo des Hackerspaces @@ -35,17 +35,17 @@ Licensed under GNU's GPL 3, see README Fehler: Netzwerk Netzwerk deaktiviert - \n\nÜberprüfen Sie ob die API der Version entspricht, welche angeben wurde!\n\nsiehe https://spaceapi.io/ + \n\nÜberprüfe, ob die API der Version entspricht, welche angegeben wurde!\n\nSiehe https://spaceapi.io/ - SpaceAPI Endpoint + SpaceAPI Directory-URL URL zum globalen SpaceAPI-Directory (directory.json)\nBeispiel:\nhttps://directory.spaceapi.io/ - Aktuelle Hackerspace-API - Aktuelle Hackerspace-API überladen (geht verloren, wenn ein anderer Hackerspace in der Liste gewählt wird) + SpaceAPI Endpunkt-URL + Aktuelle SpaceAPI Endpunkt-URL überladen (geht verloren, wenn ein anderer Space in der Liste gewählt wird) Aktualisierungsintervall - Zeitspanne in der das Widget den tatsächlichen Status überprüft. + Zeitspanne, in der das Widget den tatsächlichen Status überprüft. Transparentes Widget - Das Hintengrundbild des Widgets wird transparent statt grau dargestellt. - Widget Statustext + Das Hintergrundbild des Widgets wird transparent statt grau dargestellt. + Widget-Statustext Statustext unter dem Widget hinzufügen Status @@ -53,8 +53,31 @@ Licensed under GNU's GPL 3, see README Dauer: Stunde(n) Ort + Postanschrift + Koordinaten Kontakt - Webcam + Webcams Sensoren + Umwelt + Space + Organisation + Tel + E-Mail + ML + + Anwesende Personen + Türstatus + Geschlossen + Geöffnet + Getränkevorrat + Stromverbrauch + Netzwerkverbindungen + Temperatur + Luftfeuchtigkeit + Luftdruck + Wind + Radioaktivität + Mitgliederanzahl + Kontostand diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 0d055bb..95a223d 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -50,7 +50,7 @@ heure(s) Localisation Contact - Flux + Webcam Capteurs diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 6e49e4c..714f799 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -50,7 +50,7 @@ u(u)r(en) Locatie Kontakt - Stream + Webcams Sensoren diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..c5d5899 --- /dev/null +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #FFFFFF + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b98b486..4f9690e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -34,24 +34,66 @@ Licensed under GNU's GPL 3, see README Network unreachable \n\nCheck that the current SpaceAPI conforms to the level it actually declares!\n\nSee https://spaceapi.io/ - Edit OpenSpaceDirectory URL - URL for the global SpaceAPI directory to use (directory.json file)\nExamples:\nhttps://directory.spaceapi.io/ - Edit SpaceAPI URL - Override current SpaceAPI URL (will be lost when choosing a hackerspace from the directory) + Edit SpaceAPI Directory URL + URL for the global SpaceAPI directory to use (directory.json file).\nExamples:\nhttps://directory.spaceapi.io/ + Edit SpaceAPI Endpoint URL + Override current SpaceAPI endpoint URL (will be lost when choosing a hackerspace from the directory) Check interval Interval in minutes to check the status in the widget Show transparent background Widget\'s background will be transparent instead of gray Show status text Add a text with the status under the widget + About This App Status Last change: Duration: hour(s) Location + Postal Address + Coordinates Contact - Stream + WebCam + Webcams Sensors + Environment + Space + Organization + Phone + SIP + IRC + Twitter + Mastodon + Facebook + Identica + Foursquare + Email + ML + XMPP + Gopher + Matrix + Mumble + + People Present + Door Status + Locked + Unlocked + Beverage Supply + Power Consumption + Network Connections + Temperature + Humidity + Air Pressure + Wind + Radiation + Member Count + Account Balance + + + App Version: + License: + GNU GENERAL PUBLIC LICENSE Version 3 + Visit Repository + https://github.com/spaceapi-community/my-hackerspace diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 5153cf2..9104e23 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -12,7 +12,7 @@ android:key="api_endpoint" android:title="@string/prefs_api_title" android:summary="@string/prefs_api_summary" - android:defaultValue="https://directory.spaceapi.io/" + android:defaultValue="https://raw.githubusercontent.com/SpaceApi/directory/master/directory.json" android:inputType="textUri" /> + + + diff --git a/app/src/main/res/xml/widget.xml b/app/src/main/res/xml/widget.xml index 94b71ab..b6da0c6 100644 --- a/app/src/main/res/xml/widget.xml +++ b/app/src/main/res/xml/widget.xml @@ -3,10 +3,10 @@ Licensed under GNU's GPL 3, see README --> diff --git a/app/src/test/java/io/spaceapi/community/myhackerspace/UtilsTest.java b/app/src/test/java/io/spaceapi/community/myhackerspace/UtilsTest.java new file mode 100644 index 0000000..9a0e36b --- /dev/null +++ b/app/src/test/java/io/spaceapi/community/myhackerspace/UtilsTest.java @@ -0,0 +1,19 @@ +package io.spaceapi.community.myhackerspace; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class UtilsTest { + @Test + public void testJoinStrings() { + Assert.assertEquals("foo / bar", Utils.joinStrings(" / ", "foo", "bar")); + Assert.assertEquals("foo.bar.baz", Utils.joinStrings(".", "foo", "bar", "baz")); + Assert.assertEquals("foo.baz", Utils.joinStrings(".", "foo", null, "baz")); + Assert.assertEquals("foo.baz", Utils.joinStrings(".", null, "foo", null, "baz")); + Assert.assertNull(Utils.joinStrings(" / ", null, null, null)); + Assert.assertNull(Utils.joinStrings(" / ")); + } +} diff --git a/build.gradle b/build.gradle index be2c04b..207d49f 100644 --- a/build.gradle +++ b/build.gradle @@ -2,11 +2,14 @@ buildscript { repositories { - jcenter() + mavenCentral() google() } + dependencies { - classpath 'com.android.tools.build:gradle:3.5.3' + // Android gradle integration + classpath 'com.android.tools.build:gradle:8.5.1' + // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } @@ -14,7 +17,7 @@ buildscript { allprojects { repositories { - jcenter() + mavenCentral() google() } } diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..f927a0b --- /dev/null +++ b/gradle.properties @@ -0,0 +1,4 @@ +android.defaults.buildfeatures.buildconfig=true +android.nonFinalResIds=false +android.nonTransitiveRClass=false +android.useAndroidX=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 13372ae..62d4c05 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f817df6..48c0a02 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Fri Feb 28 13:50:08 CET 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/gradlew b/gradlew index 9d82f78..fbd7c51 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,20 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## ## @@ -6,20 +22,38 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -30,6 +64,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,28 +75,14 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -85,7 +106,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -105,10 +126,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -134,27 +156,30 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index aec9973..a9f778a 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -8,14 +24,17 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +65,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,17 +78,13 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% diff --git a/graphics/feature-graphic.png b/graphics/feature-graphic.png new file mode 100644 index 0000000..4302100 Binary files /dev/null and b/graphics/feature-graphic.png differ diff --git a/graphics/feature-graphic.svg b/graphics/feature-graphic.svg new file mode 100644 index 0000000..71c0605 --- /dev/null +++ b/graphics/feature-graphic.svg @@ -0,0 +1,16936 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MyHackerspace + + Find out more about yourfavorite Hackerspaceor Makerspace! + + + diff --git a/graphics/myhackerspace.png b/graphics/myhackerspace.png new file mode 100644 index 0000000..d2435ba Binary files /dev/null and b/graphics/myhackerspace.png differ diff --git a/graphics/myhackerspace.svg b/graphics/myhackerspace.svg new file mode 100644 index 0000000..2384691 --- /dev/null +++ b/graphics/myhackerspace.svg @@ -0,0 +1,1519 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/graphics/screenshot-phone.png b/graphics/screenshot-phone.png new file mode 100644 index 0000000..c002485 Binary files /dev/null and b/graphics/screenshot-phone.png differ diff --git a/graphics/screenshot-tablet-10in.png b/graphics/screenshot-tablet-10in.png new file mode 100644 index 0000000..5000322 Binary files /dev/null and b/graphics/screenshot-tablet-10in.png differ diff --git a/graphics/screenshot-tablet-7in.png b/graphics/screenshot-tablet-7in.png new file mode 100644 index 0000000..0d5cb26 Binary files /dev/null and b/graphics/screenshot-tablet-7in.png differ diff --git a/metadata/en-US/changelogs/100.txt b/metadata/en-US/changelogs/100.txt new file mode 100644 index 0000000..eca872b --- /dev/null +++ b/metadata/en-US/changelogs/100.txt @@ -0,0 +1,14 @@ +- [info] App was re-released by the SpaceAPI project under a new package name +- [info] GitHub is now at https://github.com/spaceapi-community/my-hackerspace/ +- [info] The app now requires at least Android 5 (API 21) +- [feature] Support for SpaceAPI v14 +- [feature] New app launcher icon +- [feature] More modern icons in app UI +- [bug] Don't save empty data in application state +- [change] Update all domains to spaceapi.io +- [change] Switch to Java 8 +- [change] Remove MemorizingTrustManager +- [change] Upgrade dependencies +- [change] Switch to CircleCI +- [change] Add support for annotations +- [i18n] Improved translations diff --git a/metadata/en-US/changelogs/101.txt b/metadata/en-US/changelogs/101.txt new file mode 100644 index 0000000..a5da46c --- /dev/null +++ b/metadata/en-US/changelogs/101.txt @@ -0,0 +1 @@ +- [bug] Fix refresh button diff --git a/metadata/en-US/changelogs/102.txt b/metadata/en-US/changelogs/102.txt new file mode 100644 index 0000000..74a5fca --- /dev/null +++ b/metadata/en-US/changelogs/102.txt @@ -0,0 +1,3 @@ +- [change] Update dependencies +- [change] Rename package to `io.spaceapi.community.myhackerspace` +- [info] Add F-Droid metadata diff --git a/metadata/en-US/changelogs/103.txt b/metadata/en-US/changelogs/103.txt new file mode 100644 index 0000000..f31bc53 --- /dev/null +++ b/metadata/en-US/changelogs/103.txt @@ -0,0 +1,6 @@ +- [feature] Add information about the app to settings +- [bug] Fix widget crashing on Android 12 +- [bug] Export widget config activity to fix crashing OpenLauncher +- [change] Reverse latitude and longitude displayed on the screen +- [change] Improvements for widget +- [change] Upgrade dependencies, change TargetSDK to 33 diff --git a/metadata/en-US/changelogs/104.txt b/metadata/en-US/changelogs/104.txt new file mode 100644 index 0000000..0638815 --- /dev/null +++ b/metadata/en-US/changelogs/104.txt @@ -0,0 +1,2 @@ +- [bug] Fix a bug when parsing v14 endpoints that contain a SpaceFED key without a "spacephone" field +- [bug] Temporarily use directory from GitHub directly to speed up loading diff --git a/metadata/en-US/changelogs/105.txt b/metadata/en-US/changelogs/105.txt new file mode 100644 index 0000000..fffd32a --- /dev/null +++ b/metadata/en-US/changelogs/105.txt @@ -0,0 +1,5 @@ +- [feature] Show state "last change" timestamp as localized datetime +- [bug] Use correct mastodon property +- [bug] Fix network error message +- [change] Drop support for Android 5–7, require at least Android 8 +- [change] Remove old autolinking-workaround for HTC devices diff --git a/metadata/en-US/full_description.txt b/metadata/en-US/full_description.txt new file mode 100644 index 0000000..4183d52 --- /dev/null +++ b/metadata/en-US/full_description.txt @@ -0,0 +1,16 @@ +

View information about hackerspaces and makerspaces registered in the +SpaceAPI directory. This includes:

+ +
    +
  • Location and contact information
  • +
  • Opening status
  • +
  • Sensor values
  • +
  • A widget displaying the current open/closed status on the Android home screen
  • +
  • ...and much more!
  • +
+ +History + +

This app was originally developed in 2012 by @rorist from the FIXME Lausanne hackspace. +In 2021, the app was transferred to the SpaceAPI community repositories and is now +mainly being developed by members of Coredump.

diff --git a/metadata/en-US/images/featureGraphic.png b/metadata/en-US/images/featureGraphic.png new file mode 100644 index 0000000..4302100 Binary files /dev/null and b/metadata/en-US/images/featureGraphic.png differ diff --git a/metadata/en-US/images/icon.png b/metadata/en-US/images/icon.png new file mode 100644 index 0000000..d2435ba Binary files /dev/null and b/metadata/en-US/images/icon.png differ diff --git a/metadata/en-US/images/phoneScreenshots/1.png b/metadata/en-US/images/phoneScreenshots/1.png new file mode 100644 index 0000000..c002485 Binary files /dev/null and b/metadata/en-US/images/phoneScreenshots/1.png differ diff --git a/metadata/en-US/images/phoneScreenshots/2.png b/metadata/en-US/images/phoneScreenshots/2.png new file mode 100644 index 0000000..bc99c8e Binary files /dev/null and b/metadata/en-US/images/phoneScreenshots/2.png differ diff --git a/metadata/en-US/images/sevenInchScreenshots/1.png b/metadata/en-US/images/sevenInchScreenshots/1.png new file mode 100644 index 0000000..0d5cb26 Binary files /dev/null and b/metadata/en-US/images/sevenInchScreenshots/1.png differ diff --git a/metadata/en-US/images/tenInchScreenshots/1.png b/metadata/en-US/images/tenInchScreenshots/1.png new file mode 100644 index 0000000..5000322 Binary files /dev/null and b/metadata/en-US/images/tenInchScreenshots/1.png differ diff --git a/metadata/en-US/short_description.txt b/metadata/en-US/short_description.txt new file mode 100644 index 0000000..0916703 --- /dev/null +++ b/metadata/en-US/short_description.txt @@ -0,0 +1 @@ +View information about hacker- and makerspaces diff --git a/metadata/en-US/title.txt b/metadata/en-US/title.txt new file mode 100644 index 0000000..29497e9 --- /dev/null +++ b/metadata/en-US/title.txt @@ -0,0 +1 @@ +MyHackerspace (New) diff --git a/tests/directory.json b/tests/directory.json deleted file mode 100644 index 1031178..0000000 --- a/tests/directory.json +++ /dev/null @@ -1 +0,0 @@ -{"FIXME1":"https://10.0.2.3:8443/status.json","FIXME2":"https://10.0.2.3:8443/status.json","FIXME3":"https://10.0.2.3:8443/status.json"} diff --git a/tests/localhost.pem b/tests/localhost.pem deleted file mode 100644 index 5dff3aa..0000000 --- a/tests/localhost.pem +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICkTCCAfoCCQCa4Wr3K4uJ8zANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMC -Q0gxDTALBgNVBAgMBFZhdWQxETAPBgNVBAcMCExhdXNhbm5lMRowGAYDVQQKDBFG -SVhNRSBIYWNrZXJzcGFjZTEOMAwGA1UECwwFRklYTUUxETAPBgNVBAMMCGZpeG1l -LmNoMRwwGgYJKoZIhvcNAQkBFg1pbmZvQGZpeG1lLmNoMB4XDTE1MDEwMTEzMDIz -N1oXDTE2MDEwMTEzMDIzN1owgYwxCzAJBgNVBAYTAkNIMQ0wCwYDVQQIDARWYXVk -MREwDwYDVQQHDAhMYXVzYW5uZTEaMBgGA1UECgwRRklYTUUgSGFja2Vyc3BhY2Ux -DjAMBgNVBAsMBUZJWE1FMREwDwYDVQQDDAhmaXhtZS5jaDEcMBoGCSqGSIb3DQEJ -ARYNaW5mb0BmaXhtZS5jaDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA312X -QApcV+e0UQR4Aovui22Q4XkJLxbLE+B5VoEMhaVUtMm14E+zIGuQNQAAvsTtVZfJ -AVygyeghdbH/tDMGPslW8yFfwwq0ELbqdWHiuzwtsBu17N6pbtA7aiWR61Ao0Dc/ -dWsT6Uj6Jwr4D3wlAD7RQHqbE1jhE39yUr8d7q0CAwEAATANBgkqhkiG9w0BAQsF -AAOBgQCi/qmcPhzX/di+aoE7rpZDZiHAFFzEIznNt1nyPArCIqrWmLi4mcw3uwUB -7m7g7/i9Z9szh710ctjNa/xFsin1pLmehCKVqFe/V7nOXcXyKDslCts9+iWxmwym -uJU9X9U5/43LGXKU0X/LPIOUAvaL1lOfdWBViYQh8cwEZTaRQA== ------END CERTIFICATE----- ------BEGIN RSA PRIVATE KEY----- -MIICXQIBAAKBgQDfXZdAClxX57RRBHgCi+6LbZDheQkvFssT4HlWgQyFpVS0ybXg -T7Mga5A1AAC+xO1Vl8kBXKDJ6CF1sf+0MwY+yVbzIV/DCrQQtup1YeK7PC2wG7Xs -3qlu0DtqJZHrUCjQNz91axPpSPonCvgPfCUAPtFAepsTWOETf3JSvx3urQIDAQAB -AoGARIlGGItVTE+3P0i8viNLnZKP6u8lh0JaK44sDQPp4LQbKqHd4aby0pbOl8SZ -de+c1y+MqJNQbsOASMnGKPejCVwf5noEwY+aiUF08mjcJjvG5Z8VV/ifbj4+jPs0 -xvVfn8mc6GM3B6QE3VxwIoN1tBujUrMjZFwiUYM+DFnzlu0CQQDzCbY8BKICukDX -ZhLYaa2WCZNEgelYBZnZmmli3DJJIPPbt59siHOQ/3I2XNS4Z4pfW/VUAXq+X42l -Dj4oLeZvAkEA60dI9zBIepAZRyeOEMEH8lUIBr1gXupSsfMp26175KNMsILovrVI -ITRE9nNlZFJC66yQy0t5hXqj3WAgWkoqowJBAKSs8fOc2AGtHf5VRXOpt1qwRj7n -H6rqsKJHBB1eQhxW3aUEuFsb4eJfk43OPLxuO839Sy+OiRKxQBKUof0rjLUCQAQt -w/H//YmPvaMx5KgPhaCcOgREoM2Ow+E9PGUWc7jf3aDU8mVQuvM8Bm2KJybc/ytI -aVnzATmJsBdiebCg2JkCQQCkqtQHrpb47zrWOt+Q7lBrZEcapbdqblAnk+yvztC8 -8NFWk8vww6AodXBSDsMYUvqKHl5RzDximbIYV3I49nHh ------END RSA PRIVATE KEY----- diff --git a/tests/serv.py b/tests/serv.py deleted file mode 100755 index acd2aaa..0000000 --- a/tests/serv.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python2 -import BaseHTTPServer, SimpleHTTPServer -import ssl - -HOST = 'localhost' -PORT = 8443 - -httpd = BaseHTTPServer.HTTPServer((HOST, PORT), SimpleHTTPServer.SimpleHTTPRequestHandler) -httpd.socket = ssl.wrap_socket (httpd.socket, certfile='localhost.pem', server_side=True) - -try: - print 'Serving on https://%s:%s/directory.json' % (HOST, PORT) - httpd.serve_forever() -except KeyboardInterrupt, e: - httpd.server_close() diff --git a/tests/status.json b/tests/status.json deleted file mode 100644 index 10a8377..0000000 --- a/tests/status.json +++ /dev/null @@ -1 +0,0 @@ -{"stream": {"html": "http://webcam.fixme.ch"}, "api": "0.13", "logo": "https://10.0.2.3:8443/test.png", "sensors": {"people_now_present": [{"unit": "device(s)", "value": 0, "description": "Number of devices on the network (excluding some devices)"}], "temperature": [{"unit": "\u00b0C", "value": 20, "location": "Bitcoin farm"}], "total_member_count": [{"unit": "premium members", "value": 27}, {"unit": "standard members", "value": 54}]}, "space": "FIXME", "url": "https://fixme.ch", "issue_report_channels": ["email", "twitter"], "state": {"ext_duration": 0, "lastchange": 1419489693.0, "open": false, "message": "The space is closed.", "icon": {"open": "https://10.0.2.3:8443/test.png", "closed": "https://10.0.2.3:8443/test.png"}}, "contact": {"wiki": "https://wiki.fixme.ch", "phone": "+41216220734", "facebook": "https://www.facebook.com/fixmehackerspace", "ml": "hackerspace-lausanne@lists.saitis.net", "twitter": "@_fixme", "irc": "irc://freenode/#fixme", "email": "info@fixme.ch", "keymaster": ["+41797440880"]}, "location": {"lat": 46.532372000000002, "lon": 6.5912920000000002, "address": "Chemin du Closel 5, 1020 Renens, Switzerland"}, "feeds": {"blog": {"url": "https://fixme.ch/rss.xml", "type": "rss"}, "wiki": {"url": "https://fixme.ch/w/index.php?title=Special:RecentChanges&feed=atom", "type": "rss"}, "calendar": {"url": "https://www.google.com/calendar/ical/sruulkb8vh28dim9bcth8emdm4%40group.calendar.google.com/public/basic.ics", "type": "ical"}}, "events": [{"timestamp": 1419080400, "type": "Meeting", "name": "Pr\u00e9paration au 31c3", "t": 1419080400}, {"timestamp": 1419003000, "type": "Meeting", "name": "Electronics Friday", "t": 1419003000}, {"timestamp": 1417820400, "type": "Meeting", "name": "Monthly cleaning", "t": 1417820400}, {"timestamp": 1417543200, "type": "Meeting", "name": "Conf\u00e9rences DQUOTEHow to start a startup?DQUOTE", "t": 1417543200}, {"timestamp": 1417215600, "type": "CTF", "name": "Security society CTF", "t": 1417215600}, {"timestamp": 1414188000, "type": "Workshop", "name": "Nipconf Hackathon", "t": 1414188000}, {"timestamp": 1412434800, "type": "Meeting", "name": "Swiss Blender User Group", "t": 1412434800}, {"timestamp": 1412272800, "type": "Workshop", "name": "Linux User Group", "t": 1412272800}, {"timestamp": 1411491600, "type": "Workshop", "name": "DBMS Meeting", "t": 1411491600}, {"timestamp": 1411279200, "type": "Workshop", "name": "Minecraft Marathon for Charity", "t": 1411279200}]} diff --git a/tests/test.png b/tests/test.png deleted file mode 100644 index 6f005f3..0000000 Binary files a/tests/test.png and /dev/null differ