StationDetailsActivity, DecodeForeignActivity: remove deep links from QR codes and NDEF messages

Android 12 doesn't support this any more for domains you don't own.
This commit is contained in:
Andreas Schildbach 2023-11-14 21:39:19 +01:00
parent 81a89ec939
commit ba3a37f6df
5 changed files with 6 additions and 307 deletions

View file

@ -117,104 +117,6 @@
android:configChanges="keyboard|keyboardHidden" android:configChanges="keyboard|keyboardHidden"
android:label="@string/station_details_activity_title" android:label="@string/station_details_activity_title"
android:taskAffinity="de.schildbach.oeffi.stations"> android:taskAffinity="de.schildbach.oeffi.stations">
<intent-filter android:label="@string/stations_station_details_intentfilter_title">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="oeffi.schildbach.de" />
<data android:pathPrefix="/station/" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
<intent-filter android:label="@string/stations_station_details_intentfilter_title">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:host="qr.bvg.de" />
<data android:pathPrefix="/h" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
<intent-filter android:label="@string/stations_station_details_intentfilter_title">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:host="mobil.s-bahn-berlin.de" />
<data android:path="/" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
<intent-filter android:label="@string/stations_station_details_intentfilter_title">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:host="wap.rmv.de" />
<data android:pathPrefix="/mobil/tag/request.do" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
<intent-filter android:label="@string/stations_station_details_intentfilter_title">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:host="m.vrn.de" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
<intent-filter android:label="@string/stations_station_details_intentfilter_title">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:host="www.mvg-live.de" />
<data android:pathPrefix="/qr" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
<intent-filter android:label="@string/stations_station_details_intentfilter_title">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:host="www.rheinbahn.de" />
<data android:pathPrefix="/QRBarcode/HS" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
<intent-filter android:label="@string/stations_station_details_intentfilter_title">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:host="mobil.vvs.de" />
<data android:pathPrefix="/mob/DMR" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
<intent-filter android:label="@string/stations_station_details_intentfilter_title">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:host="m.qando.at" />
<data android:pathPrefix="/qr" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
</activity>
<activity
android:name=".stations.DecodeForeignActivity"
android:exported="true"
android:launchMode="singleInstance"
android:taskAffinity="de.schildbach.oeffi.stations"
android:theme="@style/My.Theme.Translucent">
<intent-filter android:label="@string/stations_station_details_intentfilter_title">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:host="www.rmv.de" />
<data android:pathPrefix="/t/d" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
</activity> </activity>
<activity <activity

View file

@ -134,7 +134,6 @@
<string name="stations_search_list_empty">Keine Haltestellen gefunden</string> <string name="stations_search_list_empty">Keine Haltestellen gefunden</string>
<!-- station details --> <!-- station details -->
<string name="stations_station_details_intentfilter_title">Abfahrtszeiten an dieser Haltestelle</string>
<string name="stations_station_details_action_favorite_title">Favorit</string> <string name="stations_station_details_action_favorite_title">Favorit</string>
<string name="stations_station_details_progress">Lade Abfahrtszeiten…</string> <string name="stations_station_details_progress">Lade Abfahrtszeiten…</string>
<string name="stations_station_details_list_empty">Keine Abfahrten</string> <string name="stations_station_details_list_empty">Keine Abfahrten</string>

View file

@ -136,7 +136,6 @@
<string name="stations_search_list_empty">No stations found</string> <string name="stations_search_list_empty">No stations found</string>
<!-- station details --> <!-- station details -->
<string name="stations_station_details_intentfilter_title">Departure times of this station</string>
<string name="stations_station_details_action_favorite_title">Favorite</string> <string name="stations_station_details_action_favorite_title">Favorite</string>
<string name="stations_station_details_progress">Loading departures…</string> <string name="stations_station_details_progress">Loading departures…</string>
<string name="stations_station_details_list_empty">No departures</string> <string name="stations_station_details_list_empty">No departures</string>

View file

@ -1,125 +0,0 @@
/*
* Copyright the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.schildbach.oeffi.stations;
import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import androidx.activity.ComponentActivity;
import de.schildbach.oeffi.Application;
import de.schildbach.oeffi.R;
import de.schildbach.oeffi.util.DialogBuilder;
import de.schildbach.pte.NetworkId;
import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.LocationType;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.HttpUrl;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DecodeForeignActivity extends ComponentActivity {
private static final Pattern PATTERN_META_REFRESH = Pattern
.compile("<meta\\s+http-equiv=\"refresh\"\\s+content=\"0;\\s+URL=([^\"]*)\"");
private Application application;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.application = (Application) getApplication();
final Intent intent = getIntent();
final Uri uri = intent.getData();
if (uri != null && uri.getScheme().equals("http")) {
final String host = uri.getHost();
final String path = uri.getPath().trim();
if ("www.rmv.de".equals(host)) {
final Matcher m = Pattern.compile("/t/d(\\d+)").matcher(path);
if (m.matches()) {
final ProgressDialog progressDialog = ProgressDialog.show(DecodeForeignActivity.this, null,
getString(R.string.stations_decode_foreign_progress), true, true, dialog -> finish());
progressDialog.setCanceledOnTouchOutside(false);
final Request.Builder request = new Request.Builder();
request.url(HttpUrl.parse(uri.toString()));
final Call call = application.okHttpClient().newCall(request.build());
call.enqueue(new Callback() {
public void onResponse(final Call call, final Response r) throws IOException {
try (final Response response = r) {
if (response.isSuccessful()) {
final Matcher mRefresh = PATTERN_META_REFRESH.matcher(response.body().string());
if (mRefresh.find()) {
final Uri refreshUri = Uri.parse(mRefresh.group(1));
runOnUiThread(() -> {
progressDialog.dismiss();
if ("mobil.rmv.de".equals(refreshUri.getHost())
&& "/mobile".equals(refreshUri.getPath())) {
final String id = refreshUri.getQueryParameter("id");
StationDetailsActivity.start(DecodeForeignActivity.this,
NetworkId.NVV, new Location(LocationType.STATION, id));
finish();
} else {
errorDialog(R.string.stations_decode_foreign_failed);
}
});
} else {
onFail();
}
} else {
onFail();
}
}
}
public void onFailure(final Call call, final IOException x) {
onFail();
}
private void onFail() {
runOnUiThread(() -> {
progressDialog.dismiss();
errorDialog(R.string.stations_decode_foreign_failed);
});
}
});
} else {
throw new IllegalArgumentException("cannot handle path: '" + path + "'");
}
} else {
throw new IllegalArgumentException("cannot handle host: '" + host + "'");
}
}
}
private void errorDialog(final int resId) {
final DialogBuilder builder = DialogBuilder.warn(this, 0);
builder.setMessage(resId);
builder.setPositiveButton("Ok", (dialog, which) -> finish());
builder.setOnCancelListener(dialog -> finish());
builder.show();
}
}

View file

@ -75,8 +75,6 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -195,86 +193,12 @@ public class StationDetailsActivity extends OeffiActivity implements StationsAwa
resultStatusView = findViewById(R.id.stations_station_details_result_status); resultStatusView = findViewById(R.id.stations_station_details_result_status);
final Intent intent = getIntent(); final Intent intent = getIntent();
final Uri uri = intent.getData(); final NetworkId network = (NetworkId) checkNotNull(intent.getSerializableExtra(INTENT_EXTRA_NETWORK));
final Station station = new Station(network, (Location) intent.getSerializableExtra(INTENT_EXTRA_STATION));
if (uri != null && uri.getScheme().equals("http")) { if (intent.hasExtra(INTENT_EXTRA_DEPARTURES))
log.info("Got intent: {}", intent); station.departures = (List<Departure>) intent.getSerializableExtra(INTENT_EXTRA_DEPARTURES);
selectStation(station);
final String host = uri.getHost(); statusMessage(getString(R.string.stations_station_details_progress));
final String path = uri.getPath().trim();
if ("oeffi.schildbach.de".equals(host)) {
final NetworkId network = NetworkId.valueOf(uri.getQueryParameter("network").toUpperCase());
final String stationId = uri.getQueryParameter("id");
selectStation(new Station(network, new Location(LocationType.STATION, stationId, null, null)));
} else if ("qr.bvg.de".equals(host)) {
final Matcher m = Pattern.compile("/h(\\d+)").matcher(path);
if (m.matches()) {
final NetworkId network = NetworkId.BVG;
String stationId = bvgStationIdNfcToQr(m.group(1));
if (stationId.length() <= 6) // mast
stationId = '~' + stationId;
selectStation(new Station(network, new Location(LocationType.STATION, stationId, null, null)));
} else {
throw new IllegalArgumentException("could not parse path: '" + path + "'");
}
} else if ("mobil.s-bahn-berlin.de".equals(host)) {
if ("/".equals(path)) {
final NetworkId network = NetworkId.VBB;
final String qr = uri.getQueryParameter("QR");
final String stationId = qr != null ? qr : uri.getQueryParameter("qr");
selectStation(new Station(network, new Location(LocationType.STATION, stationId, null, null)));
} else {
throw new IllegalArgumentException("could not parse path: '" + path + "'");
}
} else if ("www.mvg-live.de".equals(host)) {
final Matcher m = Pattern.compile("/qr/(\\d+)-\\d*-\\d*").matcher(path);
if (m.matches()) {
final NetworkId network = NetworkId.MVV;
final String stationId = m.group(1);
selectStation(new Station(network, new Location(LocationType.STATION, stationId, null, null)));
} else {
throw new IllegalArgumentException("could not parse path: '" + path + "'");
}
} else if ("wap.rmv.de".equals(host)) {
final NetworkId network = NetworkId.NVV;
final String stationId = uri.getQueryParameter("id");
selectStation(new Station(network, new Location(LocationType.STATION, stationId, null, null)));
} else if ("m.vrn.de".equals(host)) {
final Matcher m = Pattern.compile("/(\\d+)").matcher(path);
if (m.matches()) {
final NetworkId network = NetworkId.VRN;
final String stationId = Integer.toString(Integer.parseInt(m.group(1)) + 6000000);
selectStation(new Station(network, new Location(LocationType.STATION, stationId, null, null)));
} else {
throw new IllegalArgumentException("could not parse path: '" + path + "'");
}
} else if ("www.rheinbahn.de".equals(host)) {
final Matcher m = Pattern.compile("/QRBarcode/HS/(\\d+)_\\d*.html").matcher(path);
if (m.matches()) {
final NetworkId network = NetworkId.VRR;
final String stationId = Integer.toString(Integer.parseInt(m.group(1)) + 20000000);
selectStation(new Station(network, new Location(LocationType.STATION, stationId, null, null)));
} else {
throw new IllegalArgumentException("could not parse path: '" + path + "'");
}
} else if ("mobil.vvs.de".equals(host)) {
final NetworkId network = NetworkId.VVS;
final int stationId = Integer.parseInt(uri.getQueryParameter("name_dm"));
// final String lineId = uri.getQueryParameter("line");
selectStation(new Station(network, new Location(LocationType.STATION,
Integer.toString(stationId < 10000 ? stationId + 5000000 : stationId), null, null)));
} else {
throw new RuntimeException("cannot handle host: '" + host + "'");
}
} else {
final NetworkId network = (NetworkId) checkNotNull(getIntent().getSerializableExtra(INTENT_EXTRA_NETWORK));
final Station station = new Station(network, (Location) intent.getSerializableExtra(INTENT_EXTRA_STATION));
if (intent.hasExtra(INTENT_EXTRA_DEPARTURES))
station.departures = (List<Departure>) intent.getSerializableExtra(INTENT_EXTRA_DEPARTURES);
selectStation(station);
statusMessage(getString(R.string.stations_station_details_progress));
}
favoriteButton favoriteButton
.setChecked(selectedFavState != null && selectedFavState == FavoriteStationsProvider.TYPE_FAVORITE); .setChecked(selectedFavState != null && selectedFavState == FavoriteStationsProvider.TYPE_FAVORITE);