I'm using the libaums library (version 0.7.0) for reading SD cards via a USB card reader.
The issue I'm having is that if I don't go into the phone storage options and choose to unmount the SD card before removing it, it always becomes corrupted when I take it out, even if I don't actually write anything to it (Windows complains about it when inserted in a Windows machine). Calling device.init() is enough to trigger the problem.
I wonder if I'm perhaps using the library incorrectly or if nothing else, there is a way to unmount the USB card reader automatically from my app once I'm finished with it? As I understand it calling device.close() SHOULD be enough to flush data and secure the card for removal.
This is the code I'm using for accessing the card:
private class FindDevicesTask extends AsyncTask<Integer, Integer, String> {
private WeakReference<Context> mContext;
FindDevicesTask (Context context){
mContext = new WeakReference<>(context);
}
#Override
protected String doInBackground(Integer... dummy) {
Context ctx = mContext.get();
if (ctx == null)
return null; // Context expired
UsbMassStorageDevice[] devices = UsbMassStorageDevice.getMassStorageDevices(ctx);
for (final UsbMassStorageDevice device : devices) {
// If we don't have permission, request it and leave this function (if we get
// permission the function will be called again)
if (!mUsbManager.hasPermission(device.getUsbDevice())) {
mUsbManager.requestPermission(device.getUsbDevice(), mPermissionIntent);
return null;
}
try {
device.init();
// Do work
} catch (IOException e) {
return e.getMessage();
} finally {
device.close();
}
}
return null;
}
#Override
protected void onPostExecute(String result) {
if (result == null || result.isEmpty())
return;
Context ctx = mContext.get();
if (ctx != null)
displayString(result, "USB connection error", ctx);
}
};
Started by calling new FindDevicesTask(context).execute();
I just wanted to report that this issue was not with the libaums library but rather Android itself. My device automatically configures SD cards it sees to work with Android (it adds some system folders and files). When this happens it does something that makes Windows not like the SD card anymore, although it's by no means corrupt (Windows can read it no problem).
Related
I want to know, does any way exist to communicate with system during instrumentation test execution.
For example:
I have a phone with IR port on onboard & I can work with it through private SDK, also I can tune it with my application. In my Instrumentation test cases I want test app behavior based on external events which I want to configure before test separate test execution.
It's looks like
#Test
public void test() throws Exception {
setupExternalCondition(condition1_ON); // setup external transiver
assertNotNull(IR.read());
assertTrue(assertIR.write());
setupExternalCondition(condition1_OFF);
assertNotNull(IR.read());
assertFalse(IR.write());
}
It's very simple example but there is a lot of "conditions", and sdk updating frequencies to high. I can't do all of this verification manually, and can't ask "transiver&SDK team" make a mock states list for writing just a unit test for coverage. So I want somehow inject external component execution to TestRuner for receiving events(or testName before test case execution) on local machine(or CI machine) to setup external condition.
Simple solution(I think) to run a tcp server on appUnderTest and request external condition change - I am not sure does it possible, and not sure about stable connection(wifi), so may be it's possible to do over adb.
Any suggestions?
P.S: test device has root permissions.
So, find not bad but not ideal solution.
Still wait for better proposition, if not may be this answer will be helpful for someone;
For "building the bridge" between local machine and AndroidJUnitTest I add next class to tests:
class IPCServiceBridge extends BroadcastReceiver {
private static final String FILTER_ID = "IPC_SERVICE";
private static IPCServiceBridge sInstance;
private boolean mIsPermitted;
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("ipc.service.action")) {
mIsPermitted = true;
}
}
public static IPCServiceBridge getInstance() {
if (sInstance == null) {
sInstance = new IPCServiceBridge();
IntentFilter filter = new IntentFilter();
filter.addAction("ipc.service.action");
Context context = InstrumentationRegistry.getContext();
context.registerReceiver(sInstance, filter);
}
return sInstance;
}
public void sendIpcCommand(String commandName) {
try {
int i = 30;
mIsPermitted = false;
while (i > 0) {
pub("request:" + commandName);
Thread.sleep(1000);
if (mIsPermitted) {
break;
}
i--;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (!mIsPermitted) {
throw new RuntimeException("IPC service does not respond");
}
}
private static void pub(String msg) {
Log.e(FILTER_ID, msg);
}
}
Than I start adb logcat -s "filter_name", parse and check which condition should be applied for InsttUnit test. When conditions is ready i send back broadcast receiver with required action.
#Test
public void test2() throws Exception {
IPCServiceBridge.getInstance().sendIpcCommand("CONDITION#123");
}
Work good, but I'm not sure that it will be super stable.
After some research I found that Android likes to cache some parts of an app while installing to improve the performance while runtime.
Is there a way to prevent Android from caching things from my app?
I am sharing my app via my website and users install and update the app manually. As soon as I update my app some Activities and Code-parts seems to be cached on their devices.
You could delete your cache everytime before you update or before you close you application.
Code to clear the cache:
public static void trimCache(Context context) {
try {
File dir = context.getCacheDir();
if (dir != null && dir.isDirectory()) {
deleteDir(dir);
}
} catch (Exception e) {
// TODO: handle exception
}
}
public static boolean deleteDir(File dir) {
if (dir != null && dir.isDirectory()) {
String[] children = dir.list();
for (int i = 0; i < children.length; i++) {
boolean success = deleteDir(new File(dir, children[i]));
if (!success) {
return false;
}
}
}
// The directory is now empty so delete it
return dir.delete();
}
Call Trimcache when you want to clear the cache (before you update perhaps? or override the onStop() method, to clear the cache when the application is going to close.
The way I see you should also have a cache problem when you're NOT updating your app.
If you want to force a server fetch when you upgrade your app you could store a boolean in Shared Preferences, using a key associated with the app version. So when you install version 1 you fetch and make putsBoolean(version, true) where version == "1". When version 2 is installed you'll find a false, which will trigger a fetch, followed by setting putsBoolean(version, true) - but in this case version == "2".
If you are reusing information that have a different meaning when you update, the solution is storing the information in a different part of the cache, so it's not sent to an activity that is not prepared to display it.
Otherwise the solution by #jordy-dieltjens seems a good one.
I have a DropboxHelper Class that is handling downloading and uploading from dropbox.
Downloading works fine but when I try to upload from dropbox the first time the code is called. The following Line is false
if (dropboxFileSystem.isFile(dropboxPath)) {
}
It returns false. Tell the app to try again and this time it sees the file and uploads it to the app. Below is some of the code I am using for the class. Debug seems to incdicate the dropbox api has not completing started / synced the first time
public class DropBoxHelper {
public DropBoxHelper(Context pContext) {
context = pContext;
defineVariables();
}
private void defineVariables() {
dropboxAccountManager = DbxAccountManager.getInstance(context.getApplicationContext(), DROPBOX_APP_KEY, DROPBOX_APP_SECRET);
dropboxPath = new DbxPath(DbxPath.ROOT, DROPBOX_FILE_NAME);
}
public boolean importFromDropbox() {
try {
dropboxFileSystem = DbxFileSystem.forAccount(dropboxAccountManager.getLinkedAccount());
if (dropboxFileSystem.isFile(dropboxPath)) {
DbxFile databaseFileonDropbox = dropboxFileSystem.open(dropboxPath);
try {
// Do Copy
} finally {
Log.i(DEBUG_TAG, "Closing File");
databaseFileonDropbox.close();
}
}
Any ideas on why the copy fails first time.
Thanks
I'm not 100% sure, but I believe you need to use dropboxFileSystem.awaitFirstSync() to make sure at least one sync with the server has happened before you try to find the file.
An alternative might be to just call dropboxFileSystem.open(...) directly and handle the exception that's raised if the file doesn't exist.
Requirement
Scan all folders and files on external storage to find all apk files and for each apk file, get info and display progress.
How I did it so far
My initial thought is that this is a simple thing to achieve. But well... even it it sort of works, it does not perform as expected.
I use an AsyncTask to perform the scanning in background. Here is how I do:
private ArrayList<String> sdcardAppsPath;
protected class ScanTask extends AsyncTask<Context, Scanner, ArrayList<Apk>> {
private ArrayList<Apk> sdcardApps;
#Override
protected ArrayList<App> doInBackground(Context... params) {
visitAllDirsAndFiles(Environment.getExternalStorageDirectory());
for (String path : sdcardAppsPath) {
//get info about apk file and
publishProgress(values) //show in some textviews number of files scanned, total files
sdcardApps.add(currentScannedItemFoundInfo);
}
return sdcardApps
}
#Override
protected void onProgressUpdate(Scanner... values) {
super.onProgressUpdate(values);
// update some textView texts based on values
}
}
And
// Process all files and directories under dir
public void visitAllDirsAndFiles(File dir) {
if (dir != null) {
if (dir.getAbsoluteFile().toString().endsWith(".apk")) {
sdcardAppsPath.add(dir.getAbsoluteFile().toString());
}
if (dir.isDirectory()) {
String[] children = dir.list();
if (children != null) {
for (int i = 0; i < children.length; i++) {
visitAllDirsAndFiles(new File(dir, children[i]));
}
}
}
}
}
As you can see after vistitAllDirsAndFiles I get an ArrayList<String> with all apk files I need, so I can check for each what I want. The downside is that it might take a lot of time to build this sdcardAppsPath if there are many files on the card and the app shows no progress while the paths are being found.
What I want to achieve
I want to be able in the doInBackground to somehow for each found file, get the info I need, update progress and then continue with scanning. Any ideas how to update my code to work like this ?
I would think that you would want to call publishProgress inside your visitAllDirsAndFiles method if you want to update progress while searching for files, instead of iterating through the list afterwards and calling it then...
I'm trying to read data to my widget after device boot.
My boot receiver is this:
public class onBootReceiver extends BroadcastReceiver{
public static final String TAG = "BootReceiver";
private Context c;
#Override
public void onReceive(Context c, Intent i) {
// TODO Auto-generated method stub
boolean dontStop = true;
while(dontStop)
{
try
{
this.c=c;
if(isExternalStorageMounted())
{
dontStop = false;
}
else
for(int j=0;j<10000;j++)
Log.d(TAG, "###################### EXTERNAL STORAGE NOT MOUNTED ##########################");
}
catch (Exception e)
{
for(int j=0;j<10000;j++)
Log.d(TAG, "###################### EXTERNAL STORAGE NOT MOUNTED ##########################");
}
}
Intent externalStorageReady = new Intent(c, TheWidget.class);
externalStorageReady.setAction(GlobalVars.WIDGET_INTENT_ACTION_READ_PREFS_AFTER_BOOT);
c.sendBroadcast(externalStorageReady);
}
private boolean isExternalStorageMounted()
{
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_REMOVED.equals(state))
{
return false;
}
else if (Environment.MEDIA_SHARED.equals(state))
{
return false;
}
else if (Environment.MEDIA_UNMOUNTABLE.equals(state))
{
return false;
}
else if (Environment.MEDIA_UNMOUNTED.equals(state))
{
return false;
}
return true;
}
}
I know I get the BOOT_COMPLETED intent (after using it in the widget itself), but I just can't read my saved data.
I read that using SharedPreferences is the solution, but what I know is when you boot your device, the SharedPreferences is no longer there.
I save the data internally using built-in SQL in the Android SDK.
Please help... :(
External storage may not be ready by the time of a BOOT_COMPLETED broadcast. And your loops are pointless.
but what i know is when you boot your device, the SharedPreferences is no longer there.
Yes, SharedPreferences are there at boot time.
i save the data internally using built-in SQL in the android sdk.
Then it is unclear why you are waiting on external storage, since your data is not on external storage.
Any form of I/O may take too long, though, right at boot time. Have your BroadcastReceiver call startService() on an IntentService that can read your database or SharedPreferences in onHandleIntent() and update your app widget.