For file storage, I'm using a custom ContentProvider cobbled together from a few examples I found online. It seems to be working pretty well as long as I use one file name defined in the class itself (mImageName), but I need to be able to define the file name with a parameter outside of the class itself. Can someone help me with this? Here is a snippet of my ContentProvider class:
public class FileStorageContentProvider extends ContentProvider {
static final String AUTHORITY = "content://com.jwburnside.provider";
public static final Uri CONTENT_URI = Uri.parse(AUTHORITY);
private static final HashMap<String, String> MIME_TYPES = new HashMap<String, String>();
private String mImageName = "myImage";
static {
MIME_TYPES.put(".jpg", "image/jpeg");
MIME_TYPES.put(".jpeg", "image/jpeg");
}
public FileStorageContentProvider() {}
#Override
public boolean onCreate() {
try {
File mFile = new File(getContext().getFilesDir(),mImageName);
if(!mFile.exists()) {
mFile.createNewFile();
getContext().getContentResolver().notifyChange(CONTENT_URI, null);
}
return (true);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
And in my activity:
mImageCaptureUri = FileStorageContentProvider.CONTENT_URI;
You'll usually specify things like that as part of the URI that gets passed to your methods. So you'd use something like
mImageCaptureUri = Uri.withAppendedPath(FileStorageContentProvider.CONTENT_URI, "myImage");
Then inside your various methods (like query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)) you can extract the filename from the URI with uri.getPath().
Related
I have two app, from another app I want to get all images,
suppose there is StickerProvider and MainActivity different app
StickerProvider is ContentProvider, MainActivity has ContentResolver
StickerProvider App has Asset Folder
Asset----> Stickers ----------> a.png, b.png
public class StickerProvider extends ContentProvider {
private final static String LOG_TAG = StickerProvider.class.getName();
private static final String[] COLUMNS = {
OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
#Override
public boolean onCreate() {
return true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
/**
* Source: {#link FileProvider#query(Uri, String[], String, String[], String)} .
*/
if (projection == null) {
projection = COLUMNS;
}
String[] images = new String[0];
try {
images = getContext().getAssets().list("stickers");
ArrayList<String> listImages = new ArrayList<String>(Arrays.asList(images));
final MatrixCursor cursor = new MatrixCursor(new String[]{"path"}, 1);
for (int i = 0; i < listImages.size(); i++) {
cursor.addRow(new String[]{listImages.get(i)});
}
return cursor;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
public String getType(Uri uri) {
/**
* Source: {#link FileProvider#getType(Uri)} .
*/
final String file_name = uri.getLastPathSegment();
final int lastDot = file_name.lastIndexOf('.');
if (lastDot >= 0) {
final String extension = file_name.substring(lastDot + 1);
final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
if (mime != null) {
return mime;
}
}
return "image/png";
}
#Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}
It's menifest file is
<provider
android:name="com.example.StickerProvider"
android:authorities="com.example"
android:exported="true"
android:grantUriPermissions="true"
android:label="StickerProvider" />
Another app is MainActivity ------> From this I want to fetch all images of above apps
public class MainActivity extends AppCompatActivity {
int i=0;
private static final String TAG = "MainActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView ivOne = (ImageView)findViewById(R.id.ivOne);
ImageView ivTwo = (ImageView)findViewById(R.id.ivTwo);
try {
ContentResolver resolver = getContentResolver();
Cursor cursor =
resolver.query(Uri.parse("content://com.example/stickers"),
null,
null,
null,
null);
if (cursor.moveToFirst()) {
do {
String word = cursor.getString(0);
if(i==0){
ivOne.setImageURI(Uri.parse("content://com.example/stickers/"+word));
}else if(i==1){
ivTwo.setImageURI(Uri.parse("content://com.example/stickers/"+word));
}
// do something meaningful
Log.d(TAG, "onCreate: "+word);
} while (cursor.moveToNext());
}
}catch(Exception e){
e.printStackTrace();
}
}
}
When ever I start MainActivity App I am getting below exception
Unable to open content: content://com.example/a.png
java.io.FileNotFoundException: No files supported by provider at content://com.example/a.png
at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:144)
at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:692)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1149)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:986)
at android.content.ContentResolver.openInputStream(ContentResolver.java:706)
at android.widget.ImageView.getDrawableFromUri(ImageView.java:900)
at android.widget.ImageView.resolveUri(ImageView.java:871)
at android.widget.ImageView.setImageURI(ImageView.java:490)
at android.support.v7.widget.AppCompatImageView.setImageURI(AppCompatImageView.java:124)
at com.pixr.photo.collage.MainActivity.onCreate(MainActivity.java:36)
at android.app.Activity.performCreate(Activity.java:6684)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2652)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2766)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1507)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6236)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:891)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:781)
ivOne.setImageURI(Uri.parse("content://com.example/stickers/"+word));
Your ContentProvider does not support this. It does not implement openFile() or any of its variants.
Either:
Use an existing ContentProvider that offers support for serving assets, such as my StreamProvider, or
Augment your provider to support openFile(), such as I do in this sample app
ic_launcher is related with app icon. There is no app icon. Check your icon in your drawable directory.
codes:
First my Uris
public static final String PACKAGE = "my.url.contentprovider";
public static final String TABLE_NAME = "NetworkTransaction";
public static final String AUTHORITY = PACKAGE + ".NetTransContentProvider";
public static final Uri BASE_URI = Uri.parse("content://"+AUTHORITY);
public static final Uri CONTENT_URI_ANY_OBSERVER = Uri.withAppendedPath(BASE_URI,TABLE_NAME+"/*");
public static final Uri CONTENT_URI_FIND_BY_ID = Uri.withAppendedPath(BASE_URI,TABLE_NAME+"/FIND/ID");
public static final Uri CONTENT_URI_INSERT_OR_REPLACE_BY_ID = Uri.withAppendedPath(BASE_URI,TABLE_NAME+"/INSERT/REPLACE/ID");
public static final Uri CONTENT_URI_INSERT_BY_ID = Uri.withAppendedPath(BASE_URI,TABLE_NAME+"/INSERT/ID");
and my loader from activity code:
#Override
protected void onResume() {
super.onResume();
getSupportLoaderManager().restartLoader(NET_TRANS_LOADER_ID,mBundle,this).forceLoad();
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
Uri uri = null;
CursorLoader cl=null;
switch (id) {
case NET_TRANS_LOADER_ID:
uri = NetTransContentProvider.CONTENT_URI_FIND_BY_ID;
cl = new CursorLoader(ChoosingUserNameActivity.this, uri, NetTransDbUtils.allColumns,
NetTransDbUtils.COLUMN_ID + " = ? ",
new String[]{String.valueOf(bundle.getLong(EXTRA_TRANSACTION_ID,-1))}, null);
break;
default:
break;
}
return cl;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
final int loaderId = loader.getId();
switch (loaderId) {
case NET_TRANS_LOADER_ID:
if(mTransactionId != null){
NetTrans netTrans = NetTransDbUtils.cursorToNetTrans(cursor);
if(netTrans != null && netTrans.getStatus() != null
&& !netTrans.getStatus().equals(NetTrans.STATUS_PENDING)){
EventBus.getDefault().post(new NetTransMsg(true, mTransactionId, netTrans.getMessage()));
}
}
break;
default:
break;
}
}
and at a runnable that runs on ExecutorService in a started service I call
mContext.getContentResolver().insert(NetTransContentProvider.CONTENT_URI_INSERT_OR_REPLACE_BY_ID, cv );
the value inserted but the loader dose not call:
#Override
public Uri insert(Uri uri, ContentValues values) {
int uriType = sUriMatcher.match(uri);
switch (uriType) {
case INSERT_OR_REPLACE_BY_ID:
mDatabase.insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE);
break;
case INSERT_BY_ID:
mDatabase.insert(TABLE_NAME, null, values);
break;
default:
break;
}
getContext().getContentResolver().notifyChange(CONTENT_URI_ANY_OBSERVER, null);
return null;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder) {
Cursor cursor = mDatabase.query(TABLE_NAME,projection,selection, selectionArgs, null, null, null);
cursor.setNotificationUri(getContext().getContentResolver(), CONTENT_URI_ANY_OBSERVER);
return cursor;
}
my problem is getContext().getContentResolver().notifyChange(CONTENT_URI_ANY_OBSERVER, null); in the insert method can not make my loader restart.
UPDATE
I have created a sample project, you press the button, a new NetTrans object is created and written into database, then the thread sleeps 5000 ms and overwrites that value (to simulate network operation). but after that loader dose not restart. Where is my bug ?
If you want observers registered on CONTENT_URI_ANY_OBSERVER to be notified when a change happens on CONTENT_URI_FIND_BY_ID, you need to make sure of two things.
First, the CONTENT_URI_ANY_OBSERVER needs to be a parent of CONTENT_URI_FIND_BY_ID. If you think of it like folders on a file system, 'CONTENT_URI_ANY_OBSERVER' should contain `CONTENT_URI_FIND_BY_ID' in one of its sub folders.
Second, you must pass true for the notifyDescendants argument when registering your content observer.
There is no wild card considerations when Android attempts to find matching content observers (the link provided in the comments only applies to the UriMatcher). So, to fix your problem you should remove the /* from your CONTENT_URI_ANY_OBSERVER and it should start matching. You can see how my.url.contentprovider/NetworkTransaction is now a parent "folder" of my.url.contentprovider/NetworkTransaction/INSERT/REPLACE/ID where as before you had my.url.contentprovider/NetworkTransaction/*.
EDIT 1
After reviewing your sample project, I have found the other area where your problem lies. When you use a cursor loader, the cursor is owned by the loader. This means that you shouldn't alter it in anyway aside from just iterating through its data. In your NetTransDbUtils.cursorToNetTrans(cursor) method, you are closing your cursor which will prevent the CursorLoader from being able to effectively monitor changes to your cursor's data.
Simple answer: Don't call close cursor.close() in NetTransDbUtils.cursorToNetTrans(cursor); for this use case.
Replace your Uris with a simple parse:
public static final Uri CONTENT_URI_ANY_OBSERVER = Uri
.parse(BASE_URI + "/" + TABLE_NAME);
public static final Uri CONTENT_URI_FIND_BY_ID = Uri
.parse(BASE_URI + "/" + TABLE_NAME + "/FIND/ID");
public static final Uri CONTENT_URI_INSERT_OR_REPLACE_BY_ID = Uri
.parse(BASE_URI + "/" + TABLE_NAME + "/INSERT/REPLACE/ID");
public static final Uri CONTENT_URI_INSERT_BY_ID = Uri
.parse(BASE_URI + "/" + TABLE_NAME + "/INSERT/ID");
In your RestService class, use a handler for callbacks:
private Handler mHandler;
#Override
public void onCreate() {
...
mHandler = new Handler();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
...
mHandler.post(task);
...
}
Following MediaContentProvider (a simple image file provider) is working correctly with all types of apps, but not with my SMS (or better MMS) app.
I know, the SMS app is expecting a Cursor instead of a ParcelFileDescriptor, does this mean I have to save my image file to a database and retrieve it from there? Or is there a better solution for that?
public class MediaContentProvider extends ContentProvider
{
public static final String AUTHORITY = "MEDIA";
#Override
public int delete(Uri uri, String selection, String[] selectionArgs)
{
return 0;
}
#Override
public String getType(Uri uri)
{
return null;
}
#Override
public Uri insert(Uri uri, ContentValues values)
{
return null;
}
#Override
public boolean onCreate()
{
return true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
{
return null;
}
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
{
return 0;
}
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException
{
String fileName = uri.getLastPathSegment();
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(getContext().getFilesDir().getAbsolutePath() + "/" + fileName), ParcelFileDescriptor.MODE_READ_ONLY);
return pfd;
}
}
EDIT
as suggested, here my FileProvider... Actually, it results in the same behaviour... As I said, I think I have to somehow provide a cursor for the SMS app...
public class ImageFileProvider extends FileProvider
{
public static final String AUTHORITY = "ImageFileProvider";
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException
{
String fileName = uri.getLastPathSegment();
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(getContext().getFilesDir().getAbsolutePath() + "/" + fileName), ParcelFileDescriptor.MODE_READ_ONLY);
return pfd;
}
}
It may be expecting you to support OpenableColumns in your query() method.
It may be needing you to grant it permission to access the data, as your ContentProvider should be appropriately secured.
It certainly is expecting you to return a real MIME type from getType(), rather than null.
I still recommend FileProvider, but if you want to roll something yourself, this sample app has the basics.
I am trying to create a button in my android app that allows the user to share an image using their choice of social media network. The image file is stored in the assets folder of the app.
My plan is to implement a custom ContentProvider to give external access to the image, then send a TYPE_SEND intent specifying the uri of the image within my content provider.
I have done this and it works for Google+ and GMail, but for other services it fails. The hardest part has been finding information on what I'm supposed to return from the query() method of my ContentProvider. Some apps specify a projection (e.g. Google+ asks for _id and _data), while some apps pass null as the projection. Even where the projection is specified, I've no idea what actual data (types) are expected in the columns. I can find no documentation on this.
I have also implemented the openAssetFile method of the ContentProvider and this gets called (twice by Google+!) but then inevitably the query method get called as well. Only the result of the query method seems to count.
Any ideas where I'm going wrong? What should I be returning from my query method?
Code below:
// my intent
Intent i = new Intent(android.content.Intent.ACTION_SEND);
i.setType("image/jpeg");
Uri uri = Uri.parse("content://com.me.provider/ic_launcher.jpg");
i.putExtra(Intent.EXTRA_STREAM, uri);
i.putExtra(android.content.Intent.EXTRA_TEXT, text);
startActivity(Intent.createChooser(i, "Share via"));
// my custom content provider
public class ImageProvider extends ContentProvider
{
private AssetManager _assetManager;
public static final Uri CONTENT_URI = Uri.parse("content://com.me.provider");
// not called
#Override
public int delete(Uri arg0, String arg1, String[] arg2)
{
return 0;
}
// not called
#Override
public String getType(Uri uri)
{
return "image/jpeg";
}
// not called
#Override
public Uri insert(Uri uri, ContentValues values)
{
return null;
}
#Override
public boolean onCreate()
{
_assetManager = getContext().getAssets();
return true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
{
MatrixCursor c = new MatrixCursor(new String[] { "_id", "_data" });
try
{
// just a guess!! works for g+ :/
c.addRow(new Object[] { "ic_launcher.jpg", _assetManager.openFd("ic_launcher.jpg") });
} catch (IOException e)
{
e.printStackTrace();
return null;
}
return c;
}
// not called
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
{
return 0;
}
// not called
#Override
public String[] getStreamTypes(Uri uri, String mimeTypeFilter)
{
return new String[] { "image/jpeg" };
}
// called by most apps
#Override
public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException
{
try
{
AssetFileDescriptor afd = _assetManager.openFd("ic_launcher.jpg");
return afd;
} catch (IOException e)
{
throw new FileNotFoundException("No asset found: " + uri);
}
}
// not called
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException
{
return super.openFile(uri, mode);
}
}
Thanks, your question solved mine ;)
I was having the exactly inverse problem of yours: every service would work except g+.
I was returning null in the query method, that made g+ crash.
The only thing to actually expose my images was to implement openFile().
I have my images stored on the filesystem, not in the assets, but I suppose you
could get a ParcelFileDescriptor from your AssetFileDescriptor and return it.
My openFile() method looks like this:
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
String path = uri.getLastPathSegment();
if (path == null) {
throw new IllegalArgumentException("Not a Path");
}
File f = new File(getContext().getFilesDir() + File.separator + "solved" + path + ".jpg");
int iMode;
if ("r".equals(mode)) {
iMode = ParcelFileDescriptor.MODE_READ_ONLY;
} else if ("rw".equals(mode)) {
iMode = ParcelFileDescriptor.MODE_READ_WRITE;
} else if ("rwt".equals(mode)) {
iMode = ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_TRUNCATE;
} else {
throw new IllegalArgumentException("Invalid mode");
}
return ParcelFileDescriptor.open(f, iMode);
}
This works for every service I have installed with the ACTION_SEND intents except g+.
Using your query method makes it work for google plus, too.
I have successfully integrated my app's country search into the global search facility and now I am trying to display each country's flag next to the search suggestions. Search inside my app works this way but of course I have control of the list and its view binding myself. So I know the flags are all there and I can use them in the rest of my app.
The trouble comes when I try to supply a Uri to a .gif file in my Assets. According to the search documentation the value of the column with the key SearchManager.SUGGEST_COLUMN_ICON_1 should be a Uri to the image.
Below is what the code looks like. In response to the ContentProvider method public Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) I am creating a MatrixCursor that maps columns from my country database to those required by the search facility. The country names show up fine and I can select them and correctly respond in my application.
I have tried forming the Uri three different ways:
// String flagUri = "file:///android_asset/" + flagPath;
// String flagUri = "file:///assets/" + flagPath;
String flagUri = "android.resource://com.lesliesoftware.worldinfo.WorldInfoActivity/assets/" + flagPath;
columnValues.add (flagUri);
They all lead to the same thing - my application icon next to each suggestion which I can get by using a value of empty string.
Is there a Uri that will work? How can I get the country flag icon next to the search suggestions?
Thanks Ian
The full source:
private Cursor search (String query, int limit) {
query = query.toLowerCase ();
String[] requestedColumns = new String[] {
BaseColumns._ID,
SearchManager.SUGGEST_COLUMN_TEXT_1,
SearchManager.SUGGEST_COLUMN_ICON_1,
};
String[] queryColumns = new String[] {
WorldInfoDatabaseAdapter.KEY_ROWID,
WorldInfoDatabaseAdapter.KEY_COUNTRYNAME,
WorldInfoDatabaseAdapter.KEY_COUNTRYCODE
};
return packageResults (query, requestedColumns, queryColumns, limit);
}
private Cursor packageResults (String query, String[] requestedColumns, String[] queryMappedColumns, int limit) {
if (requestedColumns.length != queryMappedColumns.length)
throw new IllegalArgumentException ("Internal error: requested columns do not map to query columns");
MatrixCursor results = new MatrixCursor (requestedColumns);
// Query the country list returns columns: KEY_ROWID, KEY_COUNTRYNAME, KEY_COUNTRYCODE
Cursor dbResults = myDbHelper.getCountryList (query);
// Verify that the query columns are available
for (int index = 0; index < queryMappedColumns.length; index++) {
int col = dbResults.getColumnIndex (queryMappedColumns[index]);
if (col == -1)
throw new IllegalArgumentException ("Internal error: requested column '" +
queryMappedColumns[index] + "' was not returned from the database.");
}
// Loop over the database results building up the requested results
int rowCount = 0;
while (dbResults.moveToNext () && rowCount < limit) {
Vector<String> columnValues = new Vector<String> ();
for (int index = 0; index < requestedColumns.length; index++) {
if (requestedColumns[index].compareTo (SearchManager.SUGGEST_COLUMN_ICON_1) == 0) {
String flagPath = "flags/small/" + dbResults.getString (
dbResults.getColumnIndexOrThrow (queryMappedColumns[index]))
+ "-flag.gif";
// String flagUri = "file:///android_asset/" + flagPath;
// String flagUri = "file:///assets/" + flagPath;
String flagUri = "android.resource://com.lesliesoftware.worldinfo.WorldInfoActivity/assets/" + flagPath;
columnValues.add (flagUri);
} else {
// Add the mapped query column values from the database
String colValue = dbResults.getString (dbResults.getColumnIndexOrThrow (queryMappedColumns[index]));
columnValues.add (colValue);
}
}
results.addRow (columnValues);
rowCount++;
}
return results;
}
EDIT:
I have tried other variations including moving the images from the assets to the raw folder. Nothing worked. Here are the uri's I tried:
flagUriStr = "android.resource://com.lesliesoftware.worldinfo/raw/flags/small/" +
countryCode + "-flag.gif";
flagUriStr = "android.resource://com.lesliesoftware.worldinfo/assets/flags/small/" +
countryCode + "-flag.gif";
flagUriStr = "android.resource://com.lesliesoftware.worldinfo/assets/flags/small/" +
countryCode + "-flag";
flagUriStr = "android.resource://com.lesliesoftware.worldinfo/raw/" +
countryCode + "-flag.gif";
flagUriStr = "android.resource://com.lesliesoftware.worldinfo/raw/" +
countryCode + "-flag";
The only uri that did work was if I moved a test flag into my drawable folder:
flagUriStr = "android.resource://com.lesliesoftware.worldinfo/" +
R.drawable.small_ca_flag;
Sadly after much searching around I have reached the conclusion that you cannot return a uri to an image asset to the search facility. What I did instead was move my flag images to the resources (so they do not clutter up my app's resources I created a library for the flag images) and use resource uri's. So, in my provider I have code that looks like this in the loop that maps database results to search results:
if (requestedColumns[index].compareTo (SearchManager.SUGGEST_COLUMN_ICON_1) == 0) {
// Translate the country code into a flag icon uri
String countryCode = dbResults.getString (
dbResults.getColumnIndexOrThrow (queryMappedColumns[index]));
int flagImageID = FlagLookup.smallFlagResourceID (countryCode);
String flagUriStr = "android.resource://com.lesliesoftware.worldinfo/" + flagImageID;
columnValues.add (flagUriStr);
}
and look up code that looks like this:
static public int smallFlagResourceID (String countryCode) {
if (countryCode == null || countryCode.length () == 0)
return R.drawable.flag_small_none;
if (countryCode.equalsIgnoreCase ("aa"))
return R.drawable.flag_small_aa;
else if (countryCode.equalsIgnoreCase ("ac"))
return R.drawable.flag_small_ac;
else if (countryCode.equalsIgnoreCase ("ae"))
return R.drawable.flag_small_ae;
...
I will just have to make sure I create a test that verifies that all countries that have flags returns the expected results.
You first create an AssetProvider. We will later create uris that will be handled by this class.
public class AssetsProvider extends ContentProvider {
private AssetManager assetManager;
public static final Uri CONTENT_URI =
Uri.parse("content://com.example.assets");
#Override
public int delete(Uri arg0, String arg1, String[] arg2) { return 0; }
#Override
public String getType(Uri uri) { return null; }
#Override
public Uri insert(Uri uri, ContentValues values) { return null; }
#Override
public boolean onCreate() {
assetManager = getContext().getAssets();
return true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return null; }
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; }
#Override
public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
String path = uri.getPath().substring(1);
try {
AssetFileDescriptor afd = assetManager.openFd(path);
return afd;
} catch (IOException e) {
throw new FileNotFoundException("No asset found: " + uri, e);
}
}
}
Lot's of boilerplate in there. The essential method is of course the openAssetFile that justs translates the path of the uri passed to it into a AssetFileDescriptor. In order for Android to be able to pass URIs to this provider, remember to include this in your AndroidManifest.xml file inside the application-tag:
<provider
android:name=".AssetsProvider"
android:authorities="com.example.assets" />
Now, in your search provider you can create an URI like this:
content://com.example.assets/my_folder_inside_assets/myfile.png
I was able to refer images from drawable in SearchRecentSuggestionsProvider using following uri,
"android.resource://your.package.here/drawable/image_name
Just wanted to add to vidstige's answer: I wanted to serve files that I had downloaded to my temporary cache directory earlier, so I can serve external image thumbnails to the SearchView suggestions from URLs in my search ContentProvider. I used this function instead:
#Override
public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
String filename = uri.getLastPathSegment();
try {
File file = new File(getContext().getCacheDir(), filename);
// image downloading has to be done in the search results provider, since it's not on the UI thread like this guy.
//downloadAndSaveFile("http://urdomain/urfile.png", filename);
AssetFileDescriptor afd = new AssetFileDescriptor(
ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY | ParcelFileDescriptor.MODE_WORLD_READABLE),
0, AssetFileDescriptor.UNKNOWN_LENGTH);
return afd;
} catch (IOException e) {
throw new FileNotFoundException("No asset found: " + uri);
}
}