[Q] Threads exits with uncaught exception - Java for Android App Development

Hi, Dear XDA-developers,
Recently, I tried to build an blue tooth app. I wanted to display the data in the screen when all data arrived. I used the following code to do that job. However, the looping between Broadcaster receiver and Handler message could not be linked together smoothly. Usually, after one data displayed, I got an error with the following logs
02-03 05:01:30.931: W/dalvikvm(3419): threadid=11: thread exiting with uncaught exception (group=0x40018560)
02-03 05:01:37.827: E/AndroidRuntime(3419): FATAL EXCEPTION: Thread-13
02-03 05:01:37.827: E/AndroidRuntime(3419): java.lang.NullPointerException
02-03 05:01:37.827: E/AndroidRuntime(3419): at com.huasu.healthmonitor3.Device_Activity$1$2.run(Device_Activity.java:325)
02-03 05:01:37.827: E/AndroidRuntime(3419): at java.lang.Thread.run(Thread.java:1019)
the code snippet is as followings, any suggestions are appreciated!
public final Handler mHandler = new Handler() {
@override
public void handleMessage(Message msg) {
switch (msg.what) {
case Common.MESSAGE_CONNECT:
new Thread(new Runnable() {
public void run() {
InputStream tmpIn;
OutputStream tmpOut;
try {
UUID uuid = UUID.fromString(SPP_UUID);
BluetoothDevice btDev = btAdapt
.getRemoteDevice(strAddress);
btSocket = btDev
.createRfcommSocketToServiceRecord(uuid);
btSocket.connect();
tmpIn = btSocket.getInputStream();
tmpOut = btSocket.getOutputStream();
} catch (Exception e) {
Log.d(Common.TAG, "Error connected to: "
+ strAddress);
bConnect = false;
mmInStream = null;
mmOutStream = null;
btSocket = null;
e.printStackTrace();
mHandler.sendEmptyMessage(Common.MESSAGE_CONNECT_LOST);
return;
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
mHandler.sendEmptyMessage(Common.MESSAGE_CONNECT_SUCCEED);
}
}).start();
break;
case Common.MESSAGE_CONNECT_SUCCEED:
bConnect = true;
new Thread(new Runnable() {
public void run() {
// First write command to the bluetooth port
try{
mmOutStream.write(comm1);
}
catch (Exception e) {
Log.d(Common.TAG, "Error in writing command to bluetooth ");
}
int nRecv = 0;
while (bConnect) {
try {
Log.e(Common.TAG, "Start Recv" + String.valueOf(mmInStream.available()));
nRecv = mmInStream.read(bufRecv);
if (nRecv < 1) {
Log.e(Common.TAG, "Recving Short");
Thread.sleep(100);
continue;
}
System.arraycopy(bufRecv, 0, bRecv, nRecved, nRecv);
Log.e(Common.TAG, "Recv:" + String.valueOf(nRecv));
nRecved += nRecv;
if(nRecved < nNeed)
{
Thread.sleep(1000);
continue;
}
//sendBroadcast(intent);
mHandler.obtainMessage(Common.MESSAGE_RECV,nNeed, -1, null).sendToTarget();
} catch (Exception e) {
Log.e(Common.TAG, "Recv thread:" + e.getMessage());
mHandler.sendEmptyMessage(Common.MESSAGE_EXCEPTION_RECV);
break;
}
}
Log.e(Common.TAG, "Exit while");
}
}).start();
break;
case Common.MESSAGE_EXCEPTION_RECV:
case Common.MESSAGE_CONNECT_LOST:
try {
if (mmInStream != null)
mmInStream.close();
if (mmOutStream != null)
mmOutStream.close();
if (btSocket != null)
btSocket.close();
} catch (IOException e) {
Log.e(Common.TAG, "Close Error");
e.printStackTrace();
} finally {
mmInStream = null;
mmOutStream = null;
btSocket = null;
bConnect = false;
}
break;
case Common.MESSAGE_WRITE:
break;
case Common.MESSAGE_READ:
break;
case Common.MESSAGE_RECV:
Boolean bOn = false;
if(extr_validate_data())
{
bytesTofloat(bRec_out,3072);
broadcastIntent();
}
case Common.MESSAGE_TOAST:
Toast.makeText(getApplicationContext(),
msg.getData().getString(Common.TOAST),
Toast.LENGTH_SHORT).show();
break;
}
}
};
private BroadcastReceiver connectDevices = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(Common.TAG, "Receiver:" + action);
update_dis();
mHandler.obtainMessage(Common.MESSAGE_CONNECT_SUCCEED,nNeed, -1, null).sendToTarget();
}
};
public void broadcastIntent()
{
Intent intent = new Intent();
intent.setAction("com.huasu.healthmonitor3.draw");
sendBroadcast(intent);
}

It seems that the problematic part is in the Device_Activity.java at line 325. Could you post that line and lines around it here?

Related

Retrieving cpu frequency

Hi everyone.
I want to retrieve the current cpu frequency in my app but I don't seem to be right.
In my code I want to read the "scaling_cpu_freq" file from internal storage.
This is the code:
Code:
private String ReadCPUMhz() {
String cpuMaxFreq = "";
int cur = 0;
try {
[user=1299008]@supp[/user]ressWarnings("resource")
BufferedReader maxi = new BufferedReader(new FileReader(new File("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq")));
try{
cpuMaxFreq = maxi.readLine();
cur = Integer.parseInt(cpuMaxFreq);
cur = cur/1000;
} catch (Exception ex){
ex.printStackTrace();
}
} catch (FileNotFoundException f) {
f.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return String.valueOf(cur);
}
The problem is that the method only returns 0, which is the initial value of the int "cur".
Can anybody help me?
Thanks in advance.
Here's the code I use:
Declare this class in your Activity
Code:
// Read current frequency from /sys in a separate thread
// This class assumes your TextView is declared and referenced in the OnCreate of the class this one is declared in
// And its variable name is mCurCpuFreq
protected class CurCPUThread extends Thread {
private static final String CURRENT_CPU = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq";
private boolean mInterrupt = false;
public void interrupt() {
mInterrupt = true;
}
[user=439709]@override[/user]
public void run() {
try {
while (!mInterrupt) {
sleep(400);
final String curFreq = readOneLine(CURRENT_CPU);
mCurCPUHandler.sendMessage(mCurCPUHandler.obtainMessage(0,
curFreq));
}
} catch (InterruptedException e) {
return;
}
}
}
// Update real-time current frequency & stats in a separate thread
protected static Handler mCurCPUHandler = new Handler() {
public void handleMessage(Message msg) {
mCurFreq.setText(toMHz((String) msg.obj));
final int p = Integer.parseInt((String) msg.obj);
new Thread(new Runnable() {
public void run() {
// Here I update a real-time graph of the current freq
}
}
}).start();
}
};
Helper methods used :
Code:
// Convert raw collected values to formatted MhZ
private static String toMHz(String mhzString) {
if (Integer.valueOf(mhzString) != null)
return String.valueOf(Integer.valueOf(mhzString) / 1000) + " MHz";
else
return "NaN";
}
// Iterate through the /sys file
public static String readOneLine(String fname) {
BufferedReader br;
String line = null;
try {
br = new BufferedReader(new FileReader(fname), 512);
try {
line = br.readLine();
} finally {
br.close();
}
} catch (Exception e) {
Log.e(TAG, "IO Exception when reading sys file", e);
// attempt to do magic!
return readFileViaShell(fname, true);
}
return line;
}
// Backup method if the above one fails
public static String readFileViaShell(String filePath, boolean useSu) {
CommandResult cr = null;
if (useSu) {
cr = new CMDProcessor().runSuCommand("cat " + filePath);
} else {
cr = new CMDProcessor().runShellCommand("cat " + filePath);
}
if (cr.success())
return cr.getStdout();
return null;
}
CMDProcessor.java and its dependencies attached to this post

[Q] Selecting Text from a TextView for GingerBread

I would like to select text from a textview,partially that is for Gingerbread as well as higher platforms,I find that android:textIsSelectable is not available and the WebView workaround only does it for complete text,I have tried to use pre-baked code to do this by extending a TextView:
public class SelectableTextView extends TextView {
final String TAG="SelectableTextView";
public static int _SelectedBackgroundColor = 0xffA6D4E1;
public static int _SelectedTextColor = 0xff000000;
private OnTouchListener lastOnTouch;
protected int textOffsetStart;
protected int textOffsetEnd;
private OnLongClickListener lastOnLongClick;
protected boolean longCliked;
protected boolean isDowned;
protected int textSelectedEnd;
protected int textSelectedStart;
private static SelectableTextView lastInstance;
public SelectableTextView(Context context) {
super(context);
}
public SelectableTextView(Context context,AttributeSet attrs)
{
super(context,attrs);
}
public SelectableTextView(Context context,AttributeSet attrs,int defStyle)
{
super(context,attrs,defStyle);
}
@SuppressLint("NewApi")
public void setTextIsSelectable(boolean selectable) {
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB)
super.setTextIsSelectable(true);
else
{
super.setLongClickable(true);
super.setOnLongClickListener(getSelectableLongClick());
super.setOnTouchListener(getSelectableOnTouch());
}
}
private OnLongClickListener getSelectableLongClick() {
return new OnLongClickListener() {
@override
public boolean onLongClick(View v) {
longCliked = true;
if (lastOnLongClick != null) {
lastOnLongClick.onLongClick(v);
}
return true;
}
};
}
@override
public void setOnTouchListener(OnTouchListener listener) {
super.setOnTouchListener(listener);
this.lastOnTouch = listener;
}
@override
public void setOnLongClickListener(OnLongClickListener listener) {
super.setOnLongClickListener(listener);
Log.d(TAG, "Setting touch listener in long click");
this.lastOnLongClick = listener;
// this.setOnTouchListener(getSelectableOnTouch());
}
private OnTouchListener getSelectableOnTouch() {
return new OnTouchListener() {
@override
public boolean onTouch(View v, MotionEvent event) {
Log.d(TAG, "In SelectableTextView OnTouchListener");
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
Log.d(TAG, "Finger down on the TextView");
if (lastInstance == null)
lastInstance = SelectableTextView.this;
if (lastInstance != null && lastInstance != SelectableTextView.this) {
lastInstance.clean();
lastInstance = SelectableTextView.this;
}
int offset = getOffset(event);
Log.d(TAG, "Selection starts at: "+offset);
if ((offset < textOffsetEnd && offset > textOffsetStart)
|| (offset > textOffsetEnd && offset < textOffsetStart)) {
if (textOffsetEnd - offset > offset - textOffsetStart)
textOffsetStart = textOffsetEnd;
} else {
clean();
textOffsetStart = offset;
}
isDowned = true;
} else if (isDowned && longCliked && action == MotionEvent.ACTION_MOVE) {
Log.d(TAG, "Selecting text");
selectTextOnMove(event);
} else if (action == MotionEvent.ACTION_UP) {
isDowned = false;
longCliked = false;
}
if (lastOnTouch != null)
lastOnTouch.onTouch(v, event);
return false;
}
};
}
private void selectTextOnMove(MotionEvent event) {
Log.d(TAG, "Selection text on ACTION_MOVE");
int offset = getOffset(event);
if (offset != Integer.MIN_VALUE) {
String text = getText().toString();
SpannableStringBuilder sb = new SpannableStringBuilder(text);
BackgroundColorSpan bgc = new BackgroundColorSpan(_SelectedBackgroundColor);
ForegroundColorSpan textColor = new ForegroundColorSpan(_SelectedTextColor);
int start = textOffsetStart;
textOffsetEnd = offset;
int end = offset;
Log.d(TAG,"Start: "+start+"and End: "+end);
if (start > end) {
int temp = start;
start = end;
end = temp;
Log.d(TAG, "Interchanging Start: "+start+" and End: "+end);
}
int[] curectStartEnd = curectStartEnd(text, start, end);
start = curectStartEnd[0];
end = curectStartEnd[1];
Log.d(TAG, "Corrected start and end,Start: "+start+" and End: "+end+".These values are final");
SelectableTextView.this.textSelectedStart = start;
SelectableTextView.this.textSelectedEnd = end;
sb.setSpan(bgc, start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
sb.setSpan(textColor, start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
this.setText(sb);
}
}
private int[] curectStartEnd(String text, int start, int end) {
int length = text.length();
while (start < length && start > 0 && text.charAt(start) != ' ') {
start--;
}
while (end < length && text.charAt(end) != ' ') {
end++;
}
return new int[] { start, end };
}
private int getOffset(MotionEvent event) {
Log.d(TAG,"Get offset method");
Layout layout = getLayout();
if (layout == null)
return Integer.MIN_VALUE;
float x = event.getX() + getScrollX();
float y = event.getY() + getScrollY();
int line = layout.getLineForVertical((int) y);
Log.d(TAG, "Obtaining line "+line);
int offset = layout.getOffsetForHorizontal(line, x);
return offset;
}
protected void clean() {
String gotText=this.getText().toString();
if (gotText != null) {
this.setText(gotText);
textSelectedStart = 0;
textSelectedEnd = 0;
}
}
@override
public int getSelectionStart() {
return textSelectedStart;
}
@override
public int getSelectionEnd() {
return textSelectedEnd;
}
}
This is how I am using it:
txt_copyFrom.setClickable(false);
txt_copyFrom.setCursorVisible(true);
txt_copyFrom.setEnabled(true);
txt_copyFrom.setTextIsSelectable(true);
txt_copyFrom.setOnLongClickListener(new OnLongClickListener(){
@override
public boolean onLongClick(View v) {
Log.d(TAG, "On LongClick Listener");
int start=txt_copyFrom.getSelectionStart();
int end=txt_copyFrom.getSelectionEnd();
if(start>end)
{
//I guess the SelectableTextView fixes this:
start=start+end;
end=start=end;
start=start-end;
}
mSelectedText=txt_copyFrom.getText().toString().substring(start, end);
Log.d(TAG, "Selected text: "+mSelectedText);
return true;
}});
I get a StackOverflowError:
It repeatedly prints the message:
SelectableTextView(12712): In SelectableTextView OnTouchListener
SelectableTextView(12712): Finger down on the TextView
SelectableTextView(12712): Get offset method
SelectableTextView(12712): Obtaining line 4
SelectableTextView(12712): Selection starts at: 205
Followed by:
AndroidRuntime(12712): FATAL EXCEPTION: main
AndroidRuntime(12712): java.lang.StackOverflowError
AndroidRuntime(12712): at android.text.MeasuredText.addStyleRun(MeasuredText.java:164)
AndroidRuntime(12712): at android.text.MeasuredText.addStyleRun(MeasuredText.java:204)
at android.text.StaticLayout.generate(StaticLayout.java:281)
AndroidRuntime(12712): at android.text.DynamicLayout.reflow(DynamicLayout.java:284)
AndroidRuntime(12712): at android.text.DynamicLayout.<init>(DynamicLayout.java:170)
AndroidRuntime(12712): at android.widget.TextView.makeSingleLayout(TextView.java:6044)
AndroidRuntime(12712): at android.widget.TextView.makeNewLayout(TextView.java:5942)
AndroidRuntime(12712): at android.widget.TextView.checkForRelayout(TextView.java:6481)
AndroidRuntime(12712): at android.widget.TextView.setText(TextView.java:3764)
AndroidRuntime(12712): at android.widget.TextView.setText(TextView.java:3622)
AndroidRuntime(12712): at android.widget.TextView.setText(TextView.java:3597)
AndroidRuntime(12712): at com.example.clipboardtest.SelectableTextView.clean(SelectableTextView.java:185)
This happens to be:this.setText(this.getText().toString());
This is followed by:
AndroidRuntime(12712): at com.example.clipboardtest.SelectableTextView$2.onTouch(SelectableTextView.java:108)
This is,as expected: clean();
Then this occurs repeatedly:
AndroidRuntime(12712): at com.example.clipboardtest.SelectableTextView$2.onTouch(SelectableTextView.java:120)
which is `lastOnTouch.onTouch(v, event);`
I guess this is because we retain the OnLongClick and OnTouchListener.
Also the code calls for selection of stuff using a background colour and foreground color:
SpannableStringBuilder sb = new SpannableStringBuilder(text);
BackgroundColorSpan bgc = new BackgroundColorSpan(_SelectedBackgroundColor);
ForegroundColorSpan textColor = new ForegroundColorSpan(_SelectedTextColor);
sb.setSpan(bgc, start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
sb.setSpan(textColor, start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
setText(this);
I do not understand why this implementation does not chain to the superclass implementation like I specifically asked it to.Is there anything else I can do,any approach I can follow to make it work,is there a pre-implemented version I can use.If there is nothing can you guide me through implementing a custom TextView for Android

[Q] Can I register a listener on process state?

I'm an experienced developer but new to Android development. I have an app that runs some native binaries, and I provide a status indicator to show when the native process is running and when it's not. Currently I poll the device to figure this out, using the ActivityManager API to determine if specific processes are running or not.
I'm hoping there is some way to register a listener on process state changes, so I can get notified when my process starts or stops. I looked through the API, and there doesn't seem to be such a thing. Does anyone know how I can keep track of process start and stop other than polling via ActivityManager?
MidnightJava said:
I'm an experienced developer but new to Android development. I have an app that runs some native binaries, and I provide a status indicator to show when the native process is running and when it's not. Currently I poll the device to figure this out, using the ActivityManager API to determine if specific processes are running or not.
I'm hoping there is some way to register a listener on process state changes, so I can get notified when my process starts or stops. I looked through the API, and there doesn't seem to be such a thing. Does anyone know how I can keep track of process start and stop other than polling via ActivityManager?
Click to expand...
Click to collapse
Afaik there's no way to accomplish that other than your way or being system/root app. See this similar question here for reference.
Can you show how you start the process?
EmptinessFiller said:
Can you show how you start the process?
Click to expand...
Click to collapse
Sure. Here's the class that manages starting, stopping, and statusing (running or not) the binary executable. In this case, it's the omniNames service of the omni ORB (CORBA broker).
Code:
public class RHManager {
private TimerTask task = new TimerTask() {
@Override
public void run() {
if (RHManager.this.listener != null) {
listener.running(isOmniNamesRunning());
}
}
};
private IStatusListener listener;
public RHManager() {
}
public void startOmniNames() {
final Exec exec = new Exec();
final String[] args = new String[]
{RhMgrConstants.INSTALL_LOCATION_OMNI_NAMES_SCRIPTS + "/" + RhMgrConstants.OMNI_NAMES_SCRIPT_FILE,
"start"};
final String[] env = new String[] {"LD_LIBRARY_PATH=/sdcard/data/com.axiosengineering.rhmanager/omniORB/lib"};
Thread t = new Thread() {
public void run() {
try {
int res = exec.doExec(args, env);
logMsg("omniNames start return code " + res);
} catch (IOException e) {
logMsg("Failed to start omniNames");
e.printStackTrace();
}
String std = exec.getOutResult();
logMsg("omniNames start: std out==> " + std );
String err = exec.getErrResult();
logMsg("omniNames start: err out==> " + err );
};
};
t.start();
logMsg("omniNames started");
}
private boolean isOmniNamesRunning() {
String pid_s = getOmniNamesPid();
Integer pid = null;
if (pid_s != null) {
try {
pid = Integer.parseInt(pid_s);
} catch (NumberFormatException e) {
return false;
}
}
if (pid != null) {
RunningAppProcessInfo activityMgr = new ActivityManager.RunningAppProcessInfo("omniNames", pid, null);
return activityMgr.processName != null ;
}
return false;
}
public void stopOmniNames() {
String pid = getOmniNamesPid();
android.os.Process.killProcess(Integer.parseInt(pid));
android.os.Process.sendSignal(Integer.parseInt(pid), android.os.Process.SIGNAL_KILL);
}
private String getOmniNamesPid() {
Exec exec = new Exec();
final String[] args = new String[]
{RhMgrConstants.INSTALL_LOCATION_OMNI_NAMES_SCRIPTS + "/" + RhMgrConstants.OMNI_NAMES_SCRIPT_FILE,
"pid"};
String pid = "";
try {
int res = exec.doExec(args, null);
logMsg("oniNames pid return code: " + res);
} catch (IOException e) {
logMsg("Failed to start omniNames");
e.printStackTrace();
return pid;
}
String std = exec.getOutResult();
logMsg("omniNames pid: std out ==> " + std);
String err = exec.getErrResult();
logMsg("omniNames pid: err out ==> " + err);
String[] parts = std.split("\\s+");
if (parts.length >= 2) {
pid = parts[1];
}
return pid;
}
//monitor omniNames status and report status periodically to an IStatusListener
public void startMonitorProcess(IStatusListener listener, String string) {
this.listener = listener;
Timer t = new Timer();
t.schedule(task, 0, 1000);
}
private void logMsg(String msg) {
if (RhMgrConstants.DEBUG) {
System.err.println(msg);
}
}
}
Here's the Exec class that handles invocation of Runtime#exec(), consumes std and err out, and reports those and process return status to the caller.
Code:
public class Exec {
private String outResult;
private String errResult;
private Process process;
private boolean failed = false;
StreamReader outReader;
StreamReader errReader;
public int doExec(String[] cmd, String[] envp) throws IOException{
Timer t = null;
try {
process = Runtime.getRuntime().exec(cmd, envp);
outReader = new StreamReader(process.getInputStream());
outReader.setPriority(10);
errReader = new StreamReader(process.getErrorStream());
outReader.start();
errReader.start();
t = new Timer();
t.schedule(task, 10000);
int status = process.waitFor();
outReader.join();
errReader.join();
StringWriter outWriter = outReader.getResult();
outResult = outWriter.toString();
outWriter.close();
StringWriter errWriter = errReader.getResult();
errResult = errWriter.toString();
errWriter.close();
return (failed ? -1: status);
} catch (InterruptedException e) {
return -1;
} finally {
if (t != null) {
t.cancel();
}
}
}
public int doExec(String[] cmd) throws IOException{
return doExec(cmd, null);
}
public String getOutResult(){
return outResult;
}
public String getErrResult(){
return errResult;
}
private static class StreamReader extends Thread {
private InputStream is;
private StringWriter sw;
StreamReader(InputStream is) {
this.is = is;
sw = new StringWriter(30000);
}
public void run() {
try {
int c;
while ((c = is.read()) != -1){
sw.write(c);
}
}
catch (IOException e) { ; }
}
StringWriter getResult() {
try {
is.close();
} catch (IOException e) {
System.err.println("Unable to close input stream in StreamReader");
}
return sw;
}
}
private TimerTask task = new TimerTask() {
@Override
public void run() {
failed = true;
process.destroy();
}
};
}
Here's the script that startOminNames() invokes. It's the shell script installed with omniORB with functions other than start and get_pid removed, since those are handled by Android classes. You can invoke any executable in place of the script, or wrap your executable in a script.
Code:
#
# omniNames init file for starting up the OMNI Naming service
#
# chkconfig: - 20 80
# description: Starts and stops the OMNI Naming service
#
exec="/sdcard/data/com.axiosengineering.rhmanager/omniORB/bin/omniNames"
prog="omniNames"
logdir="/sdcard/data/com.axiosengineering.rhmanager/omniORB/logs"
logfile="/sdcard/data/com.axiosengineering.rhmanager/omniORB/logs/omninames-localhost.err.log"
options=" -start -always -logdir $logdir -errlog $logfile"
start() {
#[ -x $exec ] || exit 5
echo -n $"Starting $prog: "
$exec $options
}
get_pid() {
ps | grep omniNames
}
case "$1" in
start)
start && exit 0
$1
;;
pid)
get_pid
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
exit 2
esac
exit $?
And here's the IStatusListener interface
Code:
public interface IStatusListener {
public void running(boolean running);
}
Runtime.exec() has some pitfalls. See this helpful Runtime.exec tutorial for a nice explanation.
And you may also want to check out this post on loading native binaries in Android.

[Q][SOLVED]Get path and size of SDcard in Android 4.4 KitKat

Hello everyone, I have an app on Google Play that shows the end user information about their device. Within this information, a Memory/Storage category is shown. Everything was good and fine until mean ol` KitKat wanted to deny access to the SDcard... Although I can understand Google's move on that subject, it can not go un-noticed that it may break many app's functionality (like my own). Anyhow, below is my class that scans for mount points, and stores them in an ArrayList. I used some code from StackOverflow somewhere, but I do not have the link.
StorageUtils :
Code:
public class StorageUtils {
private static final String TAG = "StorageUtils";
public static class StorageInfo {
public final String path;
public final boolean internal;
public final boolean readonly;
public final int display_number;
StorageInfo(String path, boolean internal, boolean readonly,
int display_number) {
this.path = path;
this.internal = internal;
this.readonly = readonly;
this.display_number = display_number;
}
}
public static ArrayList<StorageInfo> getStorageList() {
ArrayList<StorageInfo> list = new ArrayList<StorageInfo>();
String def_path = Environment.getExternalStorageDirectory().getPath();
boolean def_path_internal = !Environment.isExternalStorageRemovable();
String def_path_state = Environment.getExternalStorageState();
boolean def_path_available = def_path_state
.equals(Environment.MEDIA_MOUNTED)
|| def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
boolean def_path_readonly = Environment.getExternalStorageState()
.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
BufferedReader buf_reader = null;
try {
HashSet<String> paths = new HashSet<String>();
buf_reader = new BufferedReader(new FileReader("/proc/mounts"));
String line;
int cur_display_number = 1;
Log.d(TAG, "/proc/mounts");
while ((line = buf_reader.readLine()) != null) {
Log.d(TAG, line);
if (line.contains("vfat") || line.contains("/mnt")) {
StringTokenizer tokens = new StringTokenizer(line, " ");
String unused = tokens.nextToken(); // device
String mount_point = tokens.nextToken(); // mount point
if (paths.contains(mount_point)) {
continue;
}
unused = tokens.nextToken(); // file system
List<String> flags = Arrays.asList(tokens.nextToken()
.split(",")); // flags
boolean readonly = flags.contains("ro");
if (mount_point.equals(def_path)) {
paths.add(def_path);
list.add(new StorageInfo(def_path, def_path_internal,
readonly, -1));
} else if (line.contains("/dev/block/vold")) {
if (!line.contains("/mnt/secure")
&& !line.contains("/mnt/asec")
&& !line.contains("/mnt/obb")
&& !line.contains("/dev/mapper")
&& !line.contains("tmpfs")) {
paths.add(mount_point);
list.add(new StorageInfo(mount_point, false,
readonly, cur_display_number++));
}
}
}
}
if (!paths.contains(def_path) && def_path_available) {
list.add(new StorageInfo(def_path, def_path_internal,
def_path_readonly, -1));
}
} catch (FileNotFoundException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (buf_reader != null) {
try {
buf_reader.close();
} catch (IOException ex) {
}
}
}
return list;
}
public static String getReadableFileSize(long bytes, boolean si) {
int unit = si ? 1000 : 1024;
if (bytes < unit)
return bytes + " B";
int exp = (int) (Math.log(bytes) / Math.log(unit));
String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1)
+ (si ? "" : "i");
return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
}
@SuppressLint("NewApi")
public static long getFreeSpace(String path) {
StatFs statFs = new StatFs(path);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
long sdAvailSize = statFs.getFreeBlocksLong()
* statFs.getBlockSizeLong();
return sdAvailSize;
} else {
@SuppressWarnings("deprecation")
double sdAvailSize = (double) statFs.getFreeBlocks()
* (double) statFs.getBlockSize();
return (long) sdAvailSize;
}
}
public static long getUsedSpace(String path) {
return getTotalSpace(path) - getFreeSpace(path);
}
@SuppressLint("NewApi")
public static long getTotalSpace(String path) {
StatFs statFs = new StatFs(path);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
long sdTotalSize = statFs.getBlockCountLong()
* statFs.getBlockSizeLong();
return sdTotalSize;
} else {
@SuppressWarnings("deprecation")
double sdTotalSize = (double) statFs.getBlockCount()
* statFs.getBlockSize();
return (long) sdTotalSize;
}
}
/**
* getSize()[0] is /mnt/sdcard. getSize()[1] is size of sd (example 12.0G),
* getSize()[2] is used, [3] is free, [4] is blksize
*
* @return
* @throws IOException
*/
public static String[] getSize() throws IOException {
String memory = "";
Process p = Runtime.getRuntime().exec("df /mnt/sdcard");
InputStream is = p.getInputStream();
int by = -1;
while ((by = is.read()) != -1) {
memory += new String(new byte[] { (byte) by });
}
for (String df : memory.split("/n")) {
if (df.startsWith("/mnt/sdcard")) {
String[] par = df.split(" ");
List<String> pp = new ArrayList<String>();
for (String pa : par) {
if (!pa.isEmpty()) {
pp.add(pa);
}
}
return pp.toArray(new String[pp.size()]);
}
}
return null;
}
}
Next, I retrieve the used, free, and total space of each mount point. This is where KitKat breaks my app.
CpuMemFragment :
Code:
public class CpuMemFragment extends Fragment {
// CPU
String devCpuInfo;
TextView tvCpuInfo;
// RAM
String devRamInfo;
TextView tvRamInfo;
// Storage
String devStorageA, devStorageB;
TextView tvStorageAName, tvStorageA, tvStorageB, tvStorageBName;
AdView adView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.cpu_mem, container, false);
return rootView;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
// *** CPU ***
//
devCpuInfo = readCpuInfo();
//
// #################################
// *** RAM ***
//
devRamInfo = readTotalRam();
//
// #################################
// *** STORAGE ***
//
ArrayList<StorageInfo> storageInfoList = StorageUtils.getStorageList();
tvStorageAName = (TextView) getView().findViewById(R.id.tvStorageAName);
tvStorageBName = (TextView) getView().findViewById(R.id.tvStorageBName);
if (storageInfoList.size() > 0) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
&& !storageInfoList.get(0).internal) {
kitKatWorkaround(0);
}
tvStorageAName.setText(storageInfoList.get(0).path);
devStorageA = StorageUtils.getReadableFileSize(
(StorageUtils.getUsedSpace(storageInfoList.get(0).path)),
true)
+ "/"
+ StorageUtils.getReadableFileSize((StorageUtils
.getTotalSpace(storageInfoList.get(0).path)), true);
if (storageInfoList.size() > 1) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
&& !storageInfoList.get(0).internal) {
kitKatWorkaround(1);
}
tvStorageBName.setText(storageInfoList.get(1).path);
devStorageB = StorageUtils.getReadableFileSize(
StorageUtils.getUsedSpace(storageInfoList.get(1).path)
+ (StorageUtils.getUsedSpace("system/")), true)
+ "/"
+ StorageUtils.getReadableFileSize((StorageUtils
.getTotalSpace(storageInfoList.get(1).path)),
true);
} else {
devStorageB = "N/A";
}
} else {
devStorageA = "N/A";
devStorageB = "N/A";
}
//
// #################################
// CPU
tvCpuInfo = (TextView) getView().findViewById(R.id.tvCpuInfo);
tvCpuInfo.setText(devCpuInfo);
//
// #################################
// RAM
tvRamInfo = (TextView) getView().findViewById(R.id.tvRamInfo);
tvRamInfo.setText(devRamInfo);
//
// #################################
// STORAGE
tvStorageA = (TextView) getView().findViewById(R.id.tvStorageA);
tvStorageA.setText(devStorageA);
tvStorageB = (TextView) getView().findViewById(R.id.tvStorageB);
tvStorageB.setText(devStorageB);
//
// #################################
// Look up the AdView as a resource and load a request.
adView = (AdView) getActivity().findViewById(R.id.adCpuMemBanner);
AdRequest adRequest = new AdRequest.Builder().build();
adView.loadAd(adRequest);
}
@Override
public void onPause() {
if (adView != null) {
adView.pause();
}
super.onPause();
}
@Override
public void onResume() {
super.onResume();
if (adView != null) {
adView.resume();
}
}
@Override
public void onDestroy() {
if (adView != null) {
adView.destroy();
}
super.onDestroy();
}
private static synchronized String readCpuInfo() {
ProcessBuilder cmd;
String result = "";
try {
String[] args = { "/system/bin/cat", "/proc/cpuinfo" };
cmd = new ProcessBuilder(args);
Process process = cmd.start();
InputStream in = process.getInputStream();
byte[] re = new byte[1024];
while (in.read(re) != -1) {
System.out.println(new String(re));
result = result + new String(re);
}
in.close();
} catch (IOException ex) {
ex.printStackTrace();
}
return result;
}
public static synchronized String readTotalRam() {
String load = "";
try {
RandomAccessFile reader = new RandomAccessFile("/proc/meminfo", "r");
load = reader.readLine();
reader.close();
} catch (IOException ex) {
ex.printStackTrace();
}
return load;
}
// An attempt to workaround KitKat changes according to android documentation
public void kitKatWorkaround(int index) {
String path1 = Environment.getExternalStorageDirectory().getPath();
if (index == 0) {
tvStorageAName.setText(path1);
devStorageA = StorageUtils.getReadableFileSize(
(StorageUtils.getUsedSpace(path1)), true)
+ "/"
+ StorageUtils.getReadableFileSize(
(StorageUtils.getTotalSpace(path1)), true);
}
if (index == 1) {
tvStorageBName.setText(path1);
devStorageB = StorageUtils.getReadableFileSize(
StorageUtils.getUsedSpace(path1)
+ (StorageUtils.getUsedSpace("system/")), true)
+ "/"
+ StorageUtils.getReadableFileSize(
(StorageUtils.getTotalSpace(path1)), true);
}
}
}
The suspected error is right here in the logcat:
Caused by: libcore.io.ErrnoException: statvfs failed: EACCES (Permission denied)
Click to expand...
Click to collapse
is there any alternative to retrieving sdCards sizes, or should I simply display a dialog stating that I have no control over Google's decision in Android 4.4, and apologize for the inconvenince?
Also, while I'm at it: On some models of Android devices, the sizes of the SDcard are all out of whack. Some showing more used space than total, and innacurate readings. This seems to only happen with internal/emulated storage. What is the most accurate way of getting sizes of all SDcard locations?
Thank you kindly for your time, Happy Coding!
Fully Functional
Got it working after further looking into android documentation, and implementing new methods:
Within analyzing storage method:
Code:
...
ArrayList<StorageInfo> storageInfoList = StorageUtils.getStorageList();
tvStorageAName = (TextView) getView().findViewById(R.id.tvStorageAName);
tvStorageBName = (TextView) getView().findViewById(R.id.tvStorageBName);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
kitKatWorkaround();
} else if (storageInfoList.size() > 0) {
tvStorageAName.setText(storageInfoList.get(0).path);
devStorageA = StorageUtils.getReadableFileSize(
(StorageUtils.getUsedSpace(storageInfoList.get(0).path)),
true)
+ "/"
+ StorageUtils.getReadableFileSize((StorageUtils
.getTotalSpace(storageInfoList.get(0).path)), true);
if (storageInfoList.size() > 1) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
&& !storageInfoList.get(0).internal) {
kitKatWorkaround();
}
tvStorageBName.setText(storageInfoList.get(1).path);
devStorageB = StorageUtils.getReadableFileSize(
StorageUtils.getUsedSpace(storageInfoList.get(1).path)
+ (StorageUtils.getUsedSpace("system/")), true)
+ "/"
+ StorageUtils.getReadableFileSize((StorageUtils
.getTotalSpace(storageInfoList.get(1).path)),
true);
} else {
devStorageB = "N/A";
}
} else {
devStorageA = "N/A";
devStorageB = "N/A";
}
...
kitKatWorkaround();
Code:
@SuppressLint("NewApi")
public void kitKatWorkaround() {
tvStorageA = (TextView) getView().findViewById(R.id.tvStorageA);
tvStorageB = (TextView) getView().findViewById(R.id.tvStorageB);
File[] sdCards = getActivity().getApplicationContext()
.getExternalCacheDirs();
if (sdCards.length > 0) {
File sdCard1 = sdCards[0];
tvStorageAName.setText(sdCard1.getAbsolutePath()
.replace(
"Android/data/" + getActivity().getPackageName()
+ "/cache", ""));
devStorageA = StorageUtils.getReadableFileSize(
(StorageUtils.getUsedSpace(sdCard1.getAbsolutePath())),
true)
+ "/"
+ StorageUtils.getReadableFileSize((StorageUtils
.getTotalSpace(sdCard1.getAbsolutePath())), true);
if (sdCards.length > 1) {
File sdCard2 = sdCards[1];
tvStorageBName.setText(sdCard2.getAbsolutePath().replace(
"Android/data/" + getActivity().getPackageName()
+ "/cache", ""));
devStorageB = StorageUtils.getReadableFileSize(
(StorageUtils.getUsedSpace(sdCard2.getAbsolutePath())),
true)
+ "/"
+ StorageUtils.getReadableFileSize((StorageUtils
.getTotalSpace(sdCard2.getAbsolutePath())),
true);
} else {
devStorageB = "N/A";
}
} else {
devStorageA = "N/A";
devStorageB = "N/A";
}
tvStorageA.setText(devStorageA);
tvStorageB.setText(devStorageB);
}
Just going to leave this out there, it works on at least Android 3.0+ that I have tested. Using StorageUtils retrieve all available mount points, and use code above to setText() path and size
Feel free to use this if it helps (don't forget the thanks button) :good:

Camera2 focus state during preview

Hi,
I have built a small notepad+barcodescanner app. Not it works, but the performance is low, as it processes all frames. It would be good if it would process only frams which are captured when the focus settled.
The app uses Camera2 PreviewBulder and CaptureRequestBuilder / RepeatingRequest, but i only found ways to get focus state during a Capturesession. (Capture is not used in this app, only getting frames from preview).
Does anyone how to process the focus state if one uses a Preview...?
Thanks for any help
Corresponding code part:
private final CameraDevice.StateCallback stateCallback = new
CameraDevice.StateCallback() {
@override
public void onOpened(CameraDevice camera) {
//This is called when the camera is open
// Log.e(TAG, "onOpened");
cameraDevice = camera;
createCameraPreview();
}
@override
public void onDisconnected(CameraDevice camera) {
cameraDevice.close();
}
@override
public void onError(CameraDevice camera, int error) {
cameraDevice.close();
cameraDevice = null;
}
};
final CameraCaptureSession.CaptureCallback captureCallbackListener = new CameraCaptureSession.CaptureCallback() {
@override
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
// makeText(MainActivity.this, "Saved:" + file, LENGTH_SHORT).show();
createCameraPreview();
}
};
protected void startBackgroundThread() {
mBackgroundThread = new HandlerThread("Camera Background");
mBackgroundThread.start();
mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}
protected void stopBackgroundThread() {
mBackgroundThread.quitSafely();
try {
mBackgroundThread.join();
mBackgroundThread = null;
mBackgroundHandler = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
protected void createCameraPreview() {
try {
SurfaceTexture texture = textureView.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(imageDimension.getWidth(),
imageDimension.getHeight());
Surface surface = new Surface(texture);
CameraManager manager;
manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
String camerId = manager.getCameraIdList()[0];
CameraCharacteristics characteristics = manager.getCameraCharacteristics(camerId);
boolean aelockavailable = characteristics.get(CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE);
}catch (Exception e)
{
}
captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, captureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
//captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, captureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
captureRequestBuilder.addTarget(surface);
cameraDevice.createCaptureSession(Arrays.asList(surface),
new CameraCaptureSession.StateCallback() {
@override
public void onConfigured(@NonNull CameraCaptureSession
cameraCaptureSession) {
//The camera is already closed
if (null == cameraDevice) {
return;
}
// When the session is ready, we start displaying the preview.
cameraCaptureSessions = cameraCaptureSession;
updatePreview();
}
@override
public void onConfigureFailed(@NonNull
CameraCaptureSession cameraCaptureSession) {
//makeText(MainActivity.this, "Configuration change", LENGTH_SHORT).show();
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
void openCamera() {
CameraManager manager = (CameraManager)
getSystemService(Context.CAMERA_SERVICE);
//Log.e(TAG, "is camera open");
try {
cameraId = manager.getCameraIdList()[0];
CameraCharacteristics characteristics =
manager.getCameraCharacteristics(cameraId);
StreamConfigurationMap map =
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
assert map != null;
imageDimension = map.getOutputSizes(SurfaceTexture.class)[0];
// Add permission for camera and let user grant the permission
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CAMERA_PERMISSION);
return;
}
manager.openCamera(cameraId, stateCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
//Log.e(TAG, "openCamera X");
}
void updatePreview() {
if (null == cameraDevice) {
//Log.e(TAG, "updatePreview error, return");
}
captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, captureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
//captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
if (flashchanged) {flashchanged=false; if (lamp && autoflash) {captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH);} else {captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);} }
try {
cameraCaptureSessions.setRepeatingRequest(captureRequestBuilder.build(),
null, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void closeCamera() {
if (null != cameraDevice) {
cameraDevice.close();
cameraDevice = null;
}
if (null != imageReader) {
imageReader.close();
imageReader = null;
}
}

Categories

Resources