Merge branch 'fix-555--content-provider-invalid-uri' into 'master'
Fix 555 content provider invalid uri Was not correctly encoding "/" characters when searching. This caused the Uri used by the Content Providers to include a slash, which makes it look like a separate segment of the path which was wrong. Now correctly encodes "/" characters. Also noticed one other place incorrectly encoding characters, where they would've been double encoded when added as query parameters to a Uri. See merge request !203
This commit is contained in:
commit
9997b0f448
@ -304,9 +304,9 @@ public final class Utils {
|
|||||||
b.scheme(localRepoUri.getScheme().replaceFirst("http", "fdroidrepo"));
|
b.scheme(localRepoUri.getScheme().replaceFirst("http", "fdroidrepo"));
|
||||||
b.appendQueryParameter("swap", "1");
|
b.appendQueryParameter("swap", "1");
|
||||||
if (!TextUtils.isEmpty(FDroidApp.bssid)) {
|
if (!TextUtils.isEmpty(FDroidApp.bssid)) {
|
||||||
b.appendQueryParameter("bssid", Uri.encode(FDroidApp.bssid));
|
b.appendQueryParameter("bssid", FDroidApp.bssid);
|
||||||
if (!TextUtils.isEmpty(FDroidApp.ssid))
|
if (!TextUtils.isEmpty(FDroidApp.ssid))
|
||||||
b.appendQueryParameter("ssid", Uri.encode(FDroidApp.ssid));
|
b.appendQueryParameter("ssid", FDroidApp.ssid);
|
||||||
}
|
}
|
||||||
return b.build();
|
return b.build();
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import android.content.UriMatcher;
|
|||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.fdroid.fdroid.Preferences;
|
import org.fdroid.fdroid.Preferences;
|
||||||
@ -542,17 +543,22 @@ public class AppProvider extends FDroidProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Uri getSearchUri(String query) {
|
public static Uri getSearchUri(String query) {
|
||||||
return getContentUri().buildUpon()
|
if (TextUtils.isEmpty(query)) {
|
||||||
.appendPath(PATH_SEARCH)
|
// Return all the things for an empty search.
|
||||||
.appendEncodedPath(query)
|
return getContentUri();
|
||||||
.build();
|
} else {
|
||||||
|
return getContentUri().buildUpon()
|
||||||
|
.appendPath(PATH_SEARCH)
|
||||||
|
.appendPath(query)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Uri getSearchInstalledUri(String query) {
|
public static Uri getSearchInstalledUri(String query) {
|
||||||
return getContentUri()
|
return getContentUri()
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
.appendPath(PATH_SEARCH_INSTALLED)
|
.appendPath(PATH_SEARCH_INSTALLED)
|
||||||
.appendEncodedPath(query)
|
.appendPath(query)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -560,7 +566,7 @@ public class AppProvider extends FDroidProvider {
|
|||||||
return getContentUri()
|
return getContentUri()
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
.appendPath(PATH_SEARCH_CAN_UPDATE)
|
.appendPath(PATH_SEARCH_CAN_UPDATE)
|
||||||
.appendEncodedPath(query)
|
.appendPath(query)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,7 +574,7 @@ public class AppProvider extends FDroidProvider {
|
|||||||
return getContentUri().buildUpon()
|
return getContentUri().buildUpon()
|
||||||
.appendPath(PATH_SEARCH_REPO)
|
.appendPath(PATH_SEARCH_REPO)
|
||||||
.appendPath(repo.id + "")
|
.appendPath(repo.id + "")
|
||||||
.appendEncodedPath(query)
|
.appendPath(query)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,129 +0,0 @@
|
|||||||
package org.fdroid.fdroid.views.fragments;
|
|
||||||
|
|
||||||
import android.app.SearchManager;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v4.app.ListFragment;
|
|
||||||
import android.support.v4.app.LoaderManager;
|
|
||||||
import android.support.v4.content.CursorLoader;
|
|
||||||
import android.support.v4.content.Loader;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.fdroid.fdroid.AppDetails;
|
|
||||||
import org.fdroid.fdroid.R;
|
|
||||||
import org.fdroid.fdroid.Utils;
|
|
||||||
import org.fdroid.fdroid.data.App;
|
|
||||||
import org.fdroid.fdroid.data.AppProvider;
|
|
||||||
import org.fdroid.fdroid.views.AppListAdapter;
|
|
||||||
import org.fdroid.fdroid.views.AvailableAppListAdapter;
|
|
||||||
|
|
||||||
public class SearchResultsFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
|
|
||||||
|
|
||||||
private static final String TAG = "SearchResultsFragment";
|
|
||||||
|
|
||||||
private static final int REQUEST_APPDETAILS = 0;
|
|
||||||
|
|
||||||
private AppListAdapter adapter;
|
|
||||||
|
|
||||||
protected String getQuery() {
|
|
||||||
Intent intent = getActivity().getIntent();
|
|
||||||
String query = null;
|
|
||||||
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
|
|
||||||
query = intent.getStringExtra(SearchManager.QUERY);
|
|
||||||
}
|
|
||||||
if (query == null) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
|
|
||||||
//Starts a new or restarts an existing Loader in this manager
|
|
||||||
getLoaderManager().restartLoader(0, null, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup root, Bundle data) {
|
|
||||||
|
|
||||||
adapter = new AvailableAppListAdapter(getActivity(), null);
|
|
||||||
setListAdapter(adapter);
|
|
||||||
|
|
||||||
View view = inflater.inflate(R.layout.searchresults, null);
|
|
||||||
updateSummary(view);
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
|
||||||
Uri uri = AppProvider.getSearchUri(getQuery());
|
|
||||||
return new CursorLoader(
|
|
||||||
getActivity(),
|
|
||||||
uri,
|
|
||||||
AppListFragment.APP_PROJECTION,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
AppListFragment.APP_SORT
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateSummary() {
|
|
||||||
updateSummary(getView());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateSummary(View view) {
|
|
||||||
|
|
||||||
String query = getQuery();
|
|
||||||
|
|
||||||
if (query != null)
|
|
||||||
query = query.trim();
|
|
||||||
|
|
||||||
if (TextUtils.isEmpty(query))
|
|
||||||
getActivity().finish();
|
|
||||||
|
|
||||||
TextView tv = (TextView) view.findViewById(R.id.description);
|
|
||||||
String headerText;
|
|
||||||
int count = adapter.getCount();
|
|
||||||
if (count == 0) {
|
|
||||||
headerText = getString(R.string.searchres_noapps, query);
|
|
||||||
} else if (count == 1) {
|
|
||||||
headerText = getString(R.string.searchres_oneapp, query);
|
|
||||||
} else {
|
|
||||||
headerText = getString(R.string.searchres_napps, count, query);
|
|
||||||
}
|
|
||||||
tv.setText(headerText);
|
|
||||||
Utils.debugLog(TAG, "Search for '" + query + "' returned " + count + " results");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
|
||||||
final App app = new App((Cursor) adapter.getItem(position));
|
|
||||||
|
|
||||||
Intent intent = new Intent(getActivity(), AppDetails.class);
|
|
||||||
intent.putExtra(AppDetails.EXTRA_APPID, app.packageName);
|
|
||||||
intent.putExtra(AppDetails.EXTRA_HINT_SEARCHING, true);
|
|
||||||
startActivityForResult(intent, REQUEST_APPDETAILS);
|
|
||||||
super.onListItemClick(l, v, position, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
|
||||||
adapter.swapCursor(data);
|
|
||||||
updateSummary();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoaderReset(Loader<Cursor> loader) {
|
|
||||||
adapter.swapCursor(null);
|
|
||||||
}
|
|
||||||
}
|
|
@ -86,8 +86,11 @@ public class AppProviderTest extends FDroidProviderTest<AppProvider> {
|
|||||||
assertInvalidUri(AppProvider.getAuthority());
|
assertInvalidUri(AppProvider.getAuthority());
|
||||||
assertInvalidUri(ApkProvider.getContentUri());
|
assertInvalidUri(ApkProvider.getContentUri());
|
||||||
|
|
||||||
assertValidUri(AppProvider.getContentUri());
|
assertValidUri(AppProvider.getContentUri(), "content://org.fdroid.fdroid.data.AppProvider");
|
||||||
assertValidUri(AppProvider.getSearchUri("'searching!'"));
|
assertValidUri(AppProvider.getSearchUri("'searching!'"), "content://org.fdroid.fdroid.data.AppProvider/search/'searching!'");
|
||||||
|
assertValidUri(AppProvider.getSearchUri("/"), "content://org.fdroid.fdroid.data.AppProvider/search/%2F");
|
||||||
|
assertValidUri(AppProvider.getSearchUri(""), "content://org.fdroid.fdroid.data.AppProvider");
|
||||||
|
assertValidUri(AppProvider.getSearchUri(null), "content://org.fdroid.fdroid.data.AppProvider");
|
||||||
assertValidUri(AppProvider.getNoApksUri());
|
assertValidUri(AppProvider.getNoApksUri());
|
||||||
assertValidUri(AppProvider.getInstalledUri());
|
assertValidUri(AppProvider.getInstalledUri());
|
||||||
assertValidUri(AppProvider.getCanUpdateUri());
|
assertValidUri(AppProvider.getCanUpdateUri());
|
||||||
|
@ -131,6 +131,11 @@ public abstract class FDroidProviderTest<T extends FDroidProvider> extends Provi
|
|||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void assertValidUri(Uri actualUri, String expectedUri) {
|
||||||
|
assertValidUri(actualUri);
|
||||||
|
assertEquals(expectedUri, actualUri.toString());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Many queries need at least some sort of projection in order to produce
|
* Many queries need at least some sort of projection in order to produce
|
||||||
* valid SQL. As such, we also need to know about that, so we can provide
|
* valid SQL. As such, we also need to know about that, so we can provide
|
||||||
|
Loading…
x
Reference in New Issue
Block a user