Handle all app and search links via the main activity

Closes #208.
This commit is contained in:
Daniel Martí 2015-04-08 21:06:55 +02:00
parent b751594bfe
commit c52262a405
6 changed files with 207 additions and 188 deletions

View File

@ -92,12 +92,35 @@
android:launchMode="singleTop"
android:configChanges="keyboardHidden|orientation|screenSize" >
<!-- App URLs -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="fdroid.app" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="f-droid.org" />
<data android:host="www.f-droid.org" />
<data android:pathPrefix="/app/" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@ -111,6 +134,83 @@
<data android:pathPrefix="/repository/browse" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="market" android:host="details" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="play.google.com" /> <!-- they don't do www. -->
<data android:path="/store/apps/details" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="amzn" android:host="apps" android:path="/android" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="amazon.com" />
<data android:host="www.amazon.com" />
<data android:path="/gp/mas/dl/android" />
</intent-filter>
<!-- Search URLs -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="fdroid.search" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="market" android:host="search" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="play.google.com" /> <!-- they don't do www. -->
<data android:path="/store/search" />
</intent-filter>
<!-- Repo URLs -->
<!--
This intent serves two purposes: Swapping apps between devices and adding a
repo from a website (e.g. https://guardianproject.info/fdroid/repo).
@ -263,71 +363,6 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".FDroid" />
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="fdroid.app" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="f-droid.org" />
<data android:host="www.f-droid.org" />
<data android:pathPrefix="/app/" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="market" android:host="details" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="play.google.com" /> <!-- they don't do www. -->
<data android:path="/store/apps/details" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="amzn" android:host="apps" android:path="/android" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="amazon.com" />
<data android:host="www.amazon.com" />
<data android:path="/gp/mas/dl/android" />
</intent-filter>
</activity>
<activity
android:name=".views.swap.SwapAppListActivity$SwapAppDetails"
@ -377,36 +412,6 @@
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="fdroid.search" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="market" android:host="search" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="play.google.com" /> <!-- they don't do www. -->
<data android:path="/store/search" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" />

View File

@ -344,56 +344,18 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
/**
* Attempt to extract the appId from the intent which launched this activity.
* Various different intents could cause us to show this activity, such as:
* <ul>
* <li>market://details?id=[app_id]</li>
* <li>https://play.google.com/store/apps/details?id=[app_id]</li>
* <li>https://f-droid.org/app/[app_id]</li>
* <li>fdroid.app:[app_id]</li>
* </ul>
* @return May return null, if we couldn't find the appId. In this case, you will
* probably want to do something drastic like finish the activity and show some
* feedback to the user (this method will <em>not</em> do that, it will just return
* null).
* @return May return null, if we couldn't find the appId. This should
* never happen as AppDetails is only to be called by the FDroid activity
* and not externally.
*/
private String getAppIdFromIntent() {
Intent i = getIntent();
Uri data = i.getData();
String appId = null;
if (data != null) {
if (data.isHierarchical()) {
final String host = data.getHost();
if (host == null) {
Log.e(TAG, "Null host found in app link!");
if (!i.hasExtra(EXTRA_APPID)) {
Log.e(TAG, "No application ID found in the intent!");
return null;
}
if (host.equals("details") || host.equals("play.google.com")) {
// market://details?id=app.id
// https://play.google.com/store/apps/details?id=app.id
appId = data.getQueryParameter("id");
} else if (host.equals("apps") || host.equals("amazon.com") ||
host.equals("www.amazon.com")) {
// amzn://apps/android?p=app.id
// http://www.amazon.com/gp/mas/dl/android?p=app.id
appId = data.getQueryParameter("p");
} else {
// https://f-droid.org/app/app.id
appId = data.getLastPathSegment();
if (appId != null && appId.equals("app")) {
appId = null;
}
}
} else {
// fdroid.app:app.id
appId = data.getEncodedSchemeSpecificPart();
}
Log.d(TAG, "AppDetails launched from link, for '" + appId + "'");
} else if (!i.hasExtra(EXTRA_APPID)) {
Log.e(TAG, "No application ID in AppDetails!?");
} else {
appId = i.getStringExtra(EXTRA_APPID);
}
return appId;
return i.getStringExtra(EXTRA_APPID);
}
@Override

View File

@ -22,6 +22,7 @@ package org.fdroid.fdroid;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.NotificationManager;
import android.app.SearchManager;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.DialogInterface;
@ -55,11 +56,10 @@ public class FDroid extends ActionBarActivity {
private static final String TAG = "fdroid.FDroid";
public static final int REQUEST_APPDETAILS = 0;
public static final int REQUEST_MANAGEREPOS = 1;
public static final int REQUEST_PREFS = 2;
public static final int REQUEST_ENABLE_BLUETOOTH = 3;
public static final int REQUEST_SWAP = 4;
public static final int REQUEST_MANAGEREPOS = 0;
public static final int REQUEST_PREFS = 1;
public static final int REQUEST_ENABLE_BLUETOOTH = 2;
public static final int REQUEST_SWAP = 3;
public static final String EXTRA_TAB_UPDATE = "extraTab";
@ -86,25 +86,18 @@ public class FDroid extends ActionBarActivity {
// Start a search by just typing
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
Intent i = getIntent();
Uri data = i.getData();
String appid = null;
if (data != null) {
if (data.isHierarchical()) {
// http(s)://f-droid.org/repository/browse?fdid=app.id
appid = data.getQueryParameter("fdid");
}
} else if (i.hasExtra(EXTRA_TAB_UPDATE)) {
boolean showUpdateTab = i.getBooleanExtra(EXTRA_TAB_UPDATE, false);
Intent intent = getIntent();
// If the intent can be handled via AppDetails or SearchResults, it
// will call finish() and the rest of the code won't execute
handleIntent(intent);
if (intent.hasExtra(EXTRA_TAB_UPDATE)) {
boolean showUpdateTab = intent.getBooleanExtra(EXTRA_TAB_UPDATE, false);
if (showUpdateTab) {
getTabManager().selectTab(2);
}
}
if (appid != null && appid.length() > 0) {
Intent call = new Intent(this, AppDetails.class);
call.putExtra(AppDetails.EXTRA_APPID, appid);
startActivityForResult(call, REQUEST_APPDETAILS);
}
Uri uri = AppProvider.getContentUri();
getContentResolver().registerContentObserver(uri, true, new AppObserver());
@ -118,17 +111,95 @@ public class FDroid extends ActionBarActivity {
checkForAddRepoIntent();
}
private void handleIntent(Intent intent) {
final Uri data = intent.getData();
if (data == null) {
return;
}
final String scheme = data.getScheme();
final String path = data.getPath();
String appId = null;
String query = null;
if (data.isHierarchical()) {
final String host = data.getHost();
if (host == null) {
return;
}
switch (host) {
case "f-droid.org":
// http://f-droid.org/app/app.id
if (path.startsWith("/repository/browse")) {
// http://f-droid.org/repository/browse?fdid=app.id
appId = data.getQueryParameter("fdid");
} else if (path.startsWith("/app")) {
appId = data.getLastPathSegment();
if (appId != null && appId.equals("app")) {
appId = null;
}
}
break;
case "details":
// market://details?id=app.id
appId = data.getQueryParameter("id");
break;
case "search":
// market://search?q=query
query = data.getQueryParameter("q");
break;
case "play.google.com":
if (path.startsWith("/store/apps/details")) {
// http://play.google.com/store/apps/details?id=app.id
appId = data.getQueryParameter("id");
} else if (path.startsWith("/store/search")) {
// http://play.google.com/store/search?q=foo
query = data.getQueryParameter("q");
}
break;
case "apps":
case "amazon.com":
case "www.amazon.com":
// amzn://apps/android?p=app.id
// http://amazon.com/gp/mas/dl/android?p=app.id
appId = data.getQueryParameter("p");
break;
}
} else if (scheme.equals("fdroid.app")) {
// fdroid.app:app.id
appId = data.getSchemeSpecificPart();
} else if (scheme.equals("fdroid.search")) {
// fdroid.search:query
query = data.getSchemeSpecificPart();
}
Intent call = null;
if (appId != null && appId.length() > 0) {
Log.d(TAG, "FDroid launched via app link for '" + appId + "'");
call = new Intent(this, AppDetails.class);
call.putExtra(AppDetails.EXTRA_APPID, appId);
} else if (query != null && query.length() > 0) {
Log.d(TAG, "FDroid launched via search link for '" + query + "'");
call = new Intent(this, SearchResults.class);
call.setAction(Intent.ACTION_SEARCH);
call.putExtra(SearchManager.QUERY, query);
}
if (call != null) {
startActivity(call);
finish();
}
}
private void checkForAddRepoIntent() {
// Don't handle the intent after coming back to this view (e.g. after hitting the back button)
// http://stackoverflow.com/a/14820849
if (!getIntent().hasExtra("handled")) {
NewRepoConfig parser = new NewRepoConfig(this, getIntent());
Intent intent = getIntent();
if (intent.hasExtra("handled")) {
NewRepoConfig parser = new NewRepoConfig(this, intent);
if (parser.isValidRepo()) {
getIntent().putExtra("handled", true);
intent.putExtra("handled", true);
if (parser.isFromSwap()) {
startActivityForResult(new Intent(ACTION_ADD_REPO, getIntent().getData(), this, ConnectSwapActivity.class), REQUEST_SWAP);
startActivityForResult(new Intent(ACTION_ADD_REPO, intent.getData(), this, ConnectSwapActivity.class), REQUEST_SWAP);
} else {
startActivity(new Intent(ACTION_ADD_REPO, getIntent().getData(), this, ManageReposActivity.class));
startActivity(new Intent(ACTION_ADD_REPO, intent.getData(), this, ManageReposActivity.class));
}
} else if (parser.getErrorMessage() != null) {
Toast.makeText(this, parser.getErrorMessage(), Toast.LENGTH_LONG).show();
@ -251,8 +322,6 @@ public class FDroid extends ActionBarActivity {
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_APPDETAILS:
break;
case REQUEST_MANAGEREPOS:
if (data != null && data.hasExtra(ManageReposActivity.REQUEST_UPDATE)) {
AlertDialog.Builder ask_alrt = new AlertDialog.Builder(this);

View File

@ -75,7 +75,6 @@ public class SearchResults extends ActionBarActivity {
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuItem search = menu.add(Menu.NONE, SEARCH, 1, R.string.menu_search).setIcon(
android.R.drawable.ic_menu_search);
@ -85,7 +84,6 @@ public class SearchResults extends ActionBarActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:

View File

@ -17,7 +17,6 @@ import android.widget.AdapterView;
import android.widget.TextView;
import org.fdroid.fdroid.AppDetails;
import org.fdroid.fdroid.FDroid;
import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.UpdateService;
@ -32,6 +31,8 @@ abstract public class AppListFragment extends ThemeableListFragment implements
private static final String TAG = "fdroid.AppListFragment";
private static final int REQUEST_APPDETAILS = 0;
public static final String[] APP_PROJECTION = {
AppProvider.DataColumns._ID, // Required for cursor loader to work.
AppProvider.DataColumns.APP_ID,
@ -149,7 +150,7 @@ abstract public class AppListFragment extends ThemeableListFragment implements
Intent intent = getAppDetailsIntent();
intent.putExtra(AppDetails.EXTRA_APPID, app.id);
intent.putExtra(AppDetails.EXTRA_FROM, getFromTitle());
startActivityForResult(intent, FDroid.REQUEST_APPDETAILS);
startActivityForResult(intent, REQUEST_APPDETAILS);
}
}

View File

@ -36,21 +36,6 @@ public class SearchResultsFragment extends ListFragment implements LoaderManager
String query = null;
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
query = intent.getStringExtra(SearchManager.QUERY);
} else {
final Uri data = intent.getData();
if (data == null) {
return "";
}
if (data.isHierarchical()) {
// market://search?q=foo
// https://play.google.com/store/search?q=foo
query = data.getQueryParameter("q");
if (query != null && query.startsWith("pname:"))
query = query.substring(6);
} else {
// fdroid.search:foo
query = data.getEncodedSchemeSpecificPart();
}
}
if (query == null) {
return "";
@ -121,8 +106,7 @@ public class SearchResultsFragment extends ListFragment implements LoaderManager
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
final App app;
app = new App((Cursor) adapter.getItem(position));
final App app = new App((Cursor) adapter.getItem(position));
Intent intent = new Intent(getActivity(), AppDetails.class);
intent.putExtra(AppDetails.EXTRA_APPID, app.id);